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