1#define Copyright "Copyright 1999 Ed Casas" 2 3#define Version "efix v 0.3" 4 5/* 6 Copyright (C) 1999 Ed Casas 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22 Please contact the author if you wish to use efax or efix in 23 ways not covered by the GNU GPL. 24 25 You may contact the author by e-mail at: edc@cce.com. 26 27*/ 28 29const char *Usage = 30 "Usage:\n" 31 " %s [ option ]... file... \n" 32"Options (defaults):\n" 33 " -i f input format (auto):\n" 34 " fax fax (\"Group3\") 1-D coded image\n" 35 " text text\n" 36 " pbm raw PBM (portable bit map)\n" 37 " tiffg3 TIFF, Group 3 fax compression\n" 38 " tiffraw TIFF, no compression\n" 39 " pcx mono PCX\n" 40 " dcx mono DCX\n" 41 " -o f output format (tiffg3):\n" 42 " fax fax (\"Group3\") 1-D coded image\n" 43 " pbm Portable Bit Map\n" 44 " pgm Portable Gray Map (decimated by 4)\n" 45 " pcl HP-PCL (e.g. HP LaserJet)\n" 46 " ps Postscript (e.g. Apple Laserwriter)\n" 47 " tiffg3 TIFF, Group 3 fax compression\n" 48 " tiffraw TIFF, no compression\n" 49 " pcx mono PCX\n" 50 " dcx mono DCX\n" 51 " -n pat printf() pattern for output file name (ofile)\n" 52 " -f fnt use PBM font file fnt for text (built-in)\n" 53 " -l n lines per text page (66)\n" 54 " -v lvl print messages of type in string lvl (ewi)\n" 55 " -s XxY scale input by X and Y (Y optional) (1x1)\n" 56 " -r XxY resolution of output is X by Y (dpi, Y optional) (204x196)\n" 57 " -R XxY resolution of input is X by Y (dpi, Y optional) (204x196)\n" 58 " -p WxH pad/truncate output to width W by height H (215x297mm)\n" 59 " -d R,D displace output right R, down D (opposite if -ve) (0,0)\n" 60 " -O f overlay file f (none)\n" 61 " -M ignore other options and base64 (MIME) encode stdin to stdout\n" 62 "\n" 63 "Add 'in', 'cm', 'mm', or 'pt' to -p and -d arguments (default in[ches]).\n" 64 "Default output size and resolution is same as input (if known).\n" 65 ; 66 67#include <ctype.h> /* ANSI C */ 68#include <limits.h> 69#include <locale.h> 70#include <stdio.h> 71#include <string.h> 72#include <stdlib.h> 73 74#include "efaxlib.h" 75#include "efaxmsg.h" 76 77#ifndef INT_MAX 78#define INT_MAX 32767 79#endif 80 81/* Allowed input and output formats. *** MUST match enum *** */ 82 83char *iformatstr[] = { " 3text", " 1pbm", " 2fax", " 4tiffg3", " 4tiffraw", 84 " 6pcx", " 6pcxraw", " 8dcx", 0 } ; 85 86char *oformatstr[] = { " 1pbm" , " 2fax", " 3pcl", " 4ps", " 5pgm", 87 " 7tiffg3", " 8tiffraw", 88 "11pcx", "12pcxraw", "13dcx", 0 } ; 89 90/* Look up a string in a NULL-delimited table where the first 91 character of each string is the digit to return if the rest of 92 the string matches. Returns the value of the digit for the 93 matching string or -1 if no matches. */ 94 95int lookup ( char **tab, char *s ) 96{ 97 char **p ; 98 for ( p=tab ; p && *p && strcmp ( *p+2, s ) ; p++ ) ; 99 return p && *p ? atoi ( *p ) : -1 ; 100} 101 102 103/* Extract pair of values from string. If it's a `dim'ension, 104 two values are required and they are converted to inches, else 105 the y value is optional. Returns 0 or 2 on error. */ 106 107int getxy ( char *arg, float *x, float *y, int dim ) 108{ 109 int i, n, nc=0, err=0 ; 110 char c ; 111 static char *unitstr[] = { " 0in", " 1cm", " 2mm", " 3pt", 0 } ; 112 static float unitval[] = { 1.0, 2.54, 25.4, 72.0, 1.0 } ; 113 114 if ( ! arg ) 115 err = msg ( "E2 missing argument" ) ; 116 117 if ( !x || !y ) 118 err = msg ( "E2 can't happen (getxy)" ) ; 119 120 if ( ! err ) { 121 n = sscanf ( arg , "%f%c%f%n", x, &c, y, &nc ) ; 122 switch ( n ) { 123 case 0 : err = msg ( "E2bad X value in (%s)", arg ) ; break ; 124 case 2 : err = msg ( "E2bad Y value in (%s)", arg ) ; break ; 125 } 126 } 127 128 if ( ! err ) { 129 if ( dim ) { 130 if ( n != 3 ) { 131 err = msg ( "Emissing Y dimension in (%s)", arg ) ; 132 } else { 133 while ( arg [ nc ] && isspace ( arg [ nc ] ) ) nc++ ; 134 if ( arg [ nc ] ) { 135 if ( ( i = lookup ( unitstr, arg+nc ) ) >= 0 ) { 136 *x /= unitval [ i ] ; 137 *y /= unitval [ i ] ; 138 } else { 139 err = msg ( "E2bad units: `%s'", arg+nc ) ; 140 } 141 } 142 } 143 } else { 144 if ( n == 1 ) *y = *x ; 145 } 146 } 147 148 if ( ! err ) 149 msg ( "Aconverted (%s) into %f x %f", arg, *x, *y ) ; 150 151 return err ; 152} 153 154 155/* Copy stdin to stdout while applying base64 (RFC 1521) 156 encoding. This encoding must be applied after the file is 157 complete since some output formats (e.g. TIFF) require seeking 158 backwards within the (binary) file. */ 159 160int base64encode ( void ) 161{ 162 int err=0, c ; 163 uchar n=0, m=0, bits=0 ; 164 165 static uchar chartab[65] = 166 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 167 "abcdefghijklmnopqrstuvwxyz" 168 "0123456789+/" ; 169 170 while ( ( c = fgetc ( stdin ) ) >= 0 ) { 171 switch ( n ) { 172 case 0: 173 putc ( chartab [ c >> 2 ], stdout ) ; 174 bits = c & 0x3 ; 175 n = 1 ; 176 break ; 177 case 1: 178 putc ( chartab [ (bits << 4) | ( c >> 4 ) ], stdout ) ; 179 bits = c & 0xf ; 180 n = 2 ; 181 break ; 182 case 2: 183 putc ( chartab [ (bits << 2) | ( c >> 6 ) ], stdout ) ; 184 putc ( chartab [ c & 0x3f ], stdout ) ; 185 n = 0 ; 186 if ( ++m >= 18 ) { 187 putc ( '\n', stdout ) ; 188 m = 0 ; 189 } 190 break ; 191 } 192 193 } 194 195 switch ( n ) { 196 case 0: 197 break ; 198 case 1: 199 putc ( chartab [ (bits << 4) | ( 0 >> 4 ) ], stdout ) ; 200 putc ( '=', stdout ) ; 201 putc ( '=', stdout ) ; 202 break ; 203 case 2 : 204 putc ( chartab [ (bits << 2) | ( 0 >> 6 ) ], stdout ) ; 205 putc ( '=', stdout ) ; 206 break ; 207 } 208 209 putc ( '\n', stdout ) ; 210 211 return err ; 212} 213 214 215int main( int argc, char **argv) 216{ 217 int err=0, done=0, i, c ; 218 int nr, pels, ovnr, ovpels, no ; /* run/pixel/repeat counts */ 219 int linesout ; 220 int page, ilines, olines ; /* page & line counts */ 221 int xs, ys, w, h, ixsh, iysh ; /* integer scale, size & shift */ 222 short runs [ MAXRUNS ] , ovruns [ MAXRUNS ] ; 223 224 float /* defaults: */ 225 xsc=1.0, ysc=1.0, /* scale */ 226 xsh=0.0, ysh=0.0, /* shift */ 227 dxres = 204.145, /* o/p res'n: 1728/215mm * 25.4 x */ 228 dyres = 195.58, /* 7.7 * 25.4 */ 229 dxsz = 215 / 25.4, /* o/p size: 8.5" x A4 */ 230 dysz = 297 / 25.4 ; 231 232 float /* arguments: */ 233 axres = 0, ayres = 0, axsz = 0, aysz = 0, ainxres=0, ainyres=0 ; 234 235 float /* values used: */ 236 xres = 0, yres = 0, xsz = 0, ysz = 0 ; 237 238 IFILE ifile, ovfile ; 239 OFILE ofile ; 240 241 char **ifnames, *ovfnames [ 2 ] = { 0, 0 } ; 242 243 int iformat=I_AUTO, oformat=O_TIFF_FAX, pglines=0 ; 244 char *ofname=0 ; 245 246 faxfont font, *pfont=0 ; /* text font */ 247 248 /* initialize */ 249 250 argv0 = argv[0] ; 251 252 setlocale ( LC_ALL, "" ) ; 253 254 /* process arguments */ 255 256 while ( !err && (c=nextopt(argc,argv,"n:i:o:O:v:l:f:r:s:p:d:R:M") ) != -1) { 257 switch ( c ) { 258 case 'n': 259 ofname = nxtoptarg ; 260 break ; 261 case 'i': 262 if ( ( iformat = lookup ( iformatstr, nxtoptarg ) ) < 0 ) 263 err = msg ( "E2invalid input type (%s)", nxtoptarg ) ; 264 break ; 265 case 'o': 266 if ( ( oformat = lookup ( oformatstr, nxtoptarg ) ) < 0 ) 267 err = msg ( "E2invalid output type (%s)", nxtoptarg ) ; 268 break ; 269 case 'O': 270 ovfnames[0] = nxtoptarg ; 271 break ; 272 case 'v': 273 verb[0] = nxtoptarg ; 274 msg ( "A " Version ) ; 275 for ( i=0 ; i<argc ; i++ ) msg ( "Aargv[%d]=%s", i, argv[i]) ; 276 break ; 277 case 'l': 278 if ( sscanf ( nxtoptarg , "%d", &pglines ) != 1 || pglines <= 0 ) { 279 err = msg ( "E2bad page length (%s)", nxtoptarg ) ; 280 pglines = 0 ; 281 } 282 break ; 283 case 'f' : 284 if ( ! ( err = readfont ( nxtoptarg, &font ) ) ) 285 pfont = &font ; 286 break ; 287 case 's' : err = getxy ( nxtoptarg, &xsc , &ysc , 0 ) ; break ; 288 case 'r' : err = getxy ( nxtoptarg, &axres, &ayres, 0 ) ; break ; 289 case 'R' : err = getxy ( nxtoptarg, &ainxres, &ainyres, 0 ) ; break ; 290 case 'p' : err = getxy ( nxtoptarg, &axsz , &aysz , 1 ) ; break ; 291 case 'd' : err = getxy ( nxtoptarg, &xsh , &ysh , 1 ) ; break ; 292 case 'M' : err = base64encode() ; done=1 ; break ; 293 default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ; 294 } 295 } 296 297 msg ( "I " Version " " Copyright ) ; 298 299 if ( ! err && ! done ) { 300 301 if ( nxtoptind < argc ) { 302 ifnames = argv + nxtoptind ; 303 if ( argv [ argc ] ) { 304 err = msg ("E2can't happen(unterminated argv)") ; 305 } else { 306 newIFILE ( &ifile, ifnames ) ; 307 } 308 } else { 309 err = msg ( "E3 missing input file name" ) ; 310 } 311 312 if ( pfont ) ifile.font = pfont ; 313 if ( pglines ) ifile.pglines = pglines ; 314 315 newIFILE ( &ovfile, ovfnames ) ; 316 317 newOFILE ( &ofile, oformat, ofname, 0, 0, 0, 0 ) ; 318 319 } 320 321 for ( page = 0 ; ! err && ! done ; page++ ) { 322 323 if ( nextipage ( &ifile, page != 0 ) ) { 324 done=1 ; 325 continue ; 326 } 327 328 /* set output size and resolution equal to input if none specified */ 329 330 if ( ainxres > 0 ) ifile.page->xres = ainxres ; 331 if ( ainyres > 0 ) ifile.page->yres = ainyres ; 332 333 if ( ifile.page->xres <= 0 ) ifile.page->xres = dxres ; 334 if ( ifile.page->yres <= 0 ) ifile.page->yres = dyres ; 335 336 xres = axres > 0 ? axres : ifile.page->xres ; 337 yres = ayres > 0 ? ayres : ifile.page->yres ; 338 339 xsz = axsz > 0 ? axsz : ( ifile.page->w > 0 ? 340 ifile.page->w / ifile.page->xres : dxsz ) ; 341 ysz = aysz > 0 ? aysz : ( ifile.page->h > 0 ? 342 ifile.page->h / ifile.page->yres : dysz ) ; 343 344 345 w = xsz * xres + 0.5 ; /* output dimensions in pixels */ 346 h = ysz * yres + 0.5 ; 347 348 ixsh = xsh * xres ; /* x/y shifts in pixels/lines */ 349 iysh = ysh * yres ; 350 351 if ( ( w & 7 ) != 0 ) /* just about everything requires... */ 352 msg ("Iimage width rounded to %d pixels", 353 w = ( w + 7 ) & ~7 ) ; 354 355 if ( ofile.format == O_PGM && h & 3 ) /* PGM x4 decimation requires... */ 356 msg ("I PGM image height rounded up to %d lines", 357 h = ( h + 3 ) & ~3 ) ; 358 359 if ( w <= 0 || h <= 0 || xres < 0 || yres < 0 ) 360 err = msg ( "E2negative/zero scaling/size/resolution" ) ; 361 362 if ( ofile.format == O_PCL && /* check for strange PCL resolutions */ 363 ( xres != yres || ( xres != 300 && xres != 150 && xres != 75 ) ) ) 364 msg ( "Wstrange PCL resolution (%.0fx%.0f)", xres, yres ) ; 365 366 if ( w > MAXBITS*8 ) /* make sure output will fit... */ 367 err = msg( "E2requested output width too large (%d pixels)", w ) ; 368 369 ofile.w = w ; 370 ofile.h = h ; 371 ofile.xres = xres ; 372 ofile.yres = yres ; 373 374 /* scale according to input file resolution */ 375 376 xs = 256 * xsc * xres / ifile.page->xres + 0.5 ; 377 ys = 256 * ysc * yres / ifile.page->yres + 0.5 ; 378 379 if ( xs <= 0 || ys <= 0 ) 380 err = msg ( "E2negative/zero scaling" ) ; 381 382 if ( *ovfnames ) /* [re-]open overlay file */ 383 if ( nextipage ( &ovfile , 0 ) ) { 384 err=2 ; 385 continue ; 386 } 387 388 if ( nextopage ( &ofile, page ) ) { 389 err=2 ; 390 continue ; 391 } 392 linesout=0 ; 393 394 /* y-shift */ 395 396 if ( iysh > 0 ) { 397 writeline ( &ofile, ( ( *runs = w ), runs ), 1, iysh ) ; 398 linesout += iysh ; 399 } else { 400 for ( i=0 ; i < -iysh ; i++ ) 401 readline ( &ifile, runs, 0 ) ; 402 } 403 404 /* copy input to output */ 405 406 olines = ilines = 0 ; 407 408 while ( linesout < h ) { 409 410 if ( ! ifile.lines || ( nr = readline ( &ifile, runs, &pels ) ) < 0 ) { 411 break ; 412 } else { 413 ilines++ ; 414 } 415 416 if ( *ovfnames ) { 417 if ( ( ovnr = readline ( &ovfile, ovruns, &ovpels ) ) >= 0 ) 418 nr = runor ( runs, nr, ovruns, ovnr, 0, &pels ) ; 419 } 420 421 /* x-scale, x-shift & x-pad input line */ 422 423 pels = ( xs == 256 ) ? pels : xscale ( runs, nr, xs ) ; 424 pels += ( ixsh == 0 ) ? 0 : xshift ( runs, nr, ixsh ) ; 425 nr = ( pels == w ) ? nr : xpad ( runs, nr, w - pels ) ; 426 427 /* y-scale by deleting/duplicating lines. */ 428 429 no = ( ( ilines * ys ) >> 8 ) - olines ; 430 431 if ( linesout + no > h ) no = h - linesout ; 432 olines += no ; 433 434 writeline ( &ofile, runs, nr, no ) ; 435 linesout += no ; 436 } 437 438 /* y-pad */ 439 440 if ( linesout < h ) 441 writeline ( &ofile, ( ( *runs = w ), runs ), 1, h - linesout ) ; 442 443 if ( ferror ( ifile.f ) ) err = msg ( "ES2input error:" ) ; 444 } 445 446 nextopage ( &ofile, EOF ) ; 447 448 return err ; 449} 450