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