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