1/* $Id: fax2ps.c 276 2010-06-30 12:18:30Z nijtmans $" */ 2 3/* 4 * Copyright (c) 1991-1997 Sam Leffler 5 * Copyright (c) 1991-1997 Silicon Graphics, Inc. 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and 8 * its documentation for any purpose is hereby granted without fee, provided 9 * that (i) the above copyright notices and this permission notice appear in 10 * all copies of the software and related documentation, and (ii) the names of 11 * Sam Leffler and Silicon Graphics may not be used in any advertising or 12 * publicity relating to the software without the specific, prior written 13 * permission of Sam Leffler and Silicon Graphics. 14 * 15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 18 * 19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR 20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, 21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24 * OF THIS SOFTWARE. 25 */ 26#include "tif_config.h" 27 28#include <stdlib.h> 29#include <stdio.h> 30#include <string.h> 31#include <math.h> 32#include <time.h> 33 34#ifdef HAVE_UNISTD_H 35# include <unistd.h> 36#endif 37 38#ifdef HAVE_FCNTL_H 39# include <fcntl.h> 40#endif 41 42#ifdef HAVE_IO_H 43# include <io.h> 44#endif 45 46#include "tiffio.h" 47 48float defxres = 204.; /* default x resolution (pixels/inch) */ 49float defyres = 98.; /* default y resolution (lines/inch) */ 50const float half = 0.5; 51const float points = 72.0; 52float pageWidth = 0; /* image page width (inches) */ 53float pageHeight = 0; /* image page length (inches) */ 54int scaleToPage = 0; /* if true, scale raster to page dimensions */ 55int totalPages = 0; /* total # pages printed */ 56int row; /* current output row */ 57int maxline = 512; /* max output line of PostScript */ 58 59/* 60 * Turn a bit-mapped scanline into the appropriate sequence 61 * of PostScript characters to be rendered. 62 * 63 * Original version written by Bret D. Whissel, 64 * Florida State University Meteorology Department 65 * March 13-15, 1995. 66 */ 67static void 68printruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx) 69{ 70 static struct { 71 char white, black; 72 unsigned short width; 73 } WBarr[] = { 74 { 'd', 'n', 512 }, { 'e', 'o', 256 }, { 'f', 'p', 128 }, 75 { 'g', 'q', 64 }, { 'h', 'r', 32 }, { 'i', 's', 16 }, 76 { 'j', 't', 8 }, { 'k', 'u', 4 }, { 'l', 'v', 2 }, 77 { 'm', 'w', 1 } 78 }; 79 static char* svalue = 80 " !\"#$&'*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abc"; 81 int colormode = 1; /* 0 for white, 1 for black */ 82 uint32 runlength = 0; 83 int n = maxline; 84 uint32 x = 0; 85 int l; 86 87 (void) buf; 88 printf("%d m(", row++); 89 while (runs < erun) { 90 if (runlength <= 0) { 91 colormode ^= 1; 92 runlength = *runs++; 93 if (x+runlength > lastx) 94 runlength = runs[-1] = lastx-x; 95 x += runlength; 96 if (!colormode && runs == erun) 97 break; /* don't bother printing the final white run */ 98 } 99 /* 100 * If a runlength is greater than 6 pixels, then spit out 101 * black or white characters until the runlength drops to 102 * 6 or less. Once a runlength is <= 6, then combine black 103 * and white runlengths until a 6-pixel pattern is obtained. 104 * Then write out the special character. Six-pixel patterns 105 * were selected since 64 patterns is the largest power of 106 * two less than the 92 "easily printable" PostScript 107 * characters (i.e., no escape codes or octal chars). 108 */ 109 l = 0; 110 while (runlength > 6) { /* Run is greater than six... */ 111 if (runlength >= WBarr[l].width) { 112 if (n == 0) { 113 putchar('\n'); 114 n = maxline; 115 } 116 putchar(colormode ? WBarr[l].black : WBarr[l].white), n--; 117 runlength -= WBarr[l].width; 118 } else 119 l++; 120 } 121 while (runlength > 0 && runlength <= 6) { 122 uint32 bitsleft = 6; 123 int t = 0; 124 while (bitsleft) { 125 if (runlength <= bitsleft) { 126 if (colormode) 127 t |= ((1 << runlength)-1) << (bitsleft-runlength); 128 bitsleft -= runlength; 129 runlength = 0; 130 if (bitsleft) { 131 if (runs >= erun) 132 break; 133 colormode ^= 1; 134 runlength = *runs++; 135 if (x+runlength > lastx) 136 runlength = runs[-1] = lastx-x; 137 x += runlength; 138 } 139 } else { /* runlength exceeds bits left */ 140 if (colormode) 141 t |= ((1 << bitsleft)-1); 142 runlength -= bitsleft; 143 bitsleft = 0; 144 } 145 } 146 if (n == 0) { 147 putchar('\n'); 148 n = maxline; 149 } 150 putchar(svalue[t]), n--; 151 } 152 } 153 printf(")s\n"); 154} 155 156/* 157 * Create a special PostScript font for printing FAX documents. By taking 158 * advantage of the font-cacheing mechanism, a substantial speed-up in 159 * rendering time is realized. 160 */ 161static void 162emitFont(FILE* fd) 163{ 164 static const char* fontPrologue[] = { 165 "/newfont 10 dict def newfont begin /FontType 3 def /FontMatrix [1", 166 "0 0 1 0 0] def /FontBBox [0 0 512 1] def /Encoding 256 array def", 167 "0 1 31{Encoding exch /255 put}for 120 1 255{Encoding exch /255", 168 "put}for Encoding 37 /255 put Encoding 40 /255 put Encoding 41 /255", 169 "put Encoding 92 /255 put /count 0 def /ls{Encoding exch count 3", 170 "string cvs cvn put /count count 1 add def}def 32 1 36{ls}for", 171 "38 1 39{ls}for 42 1 91{ls}for 93 1 99{ls}for /count 100", 172 "def 100 1 119{ls}for /CharDict 5 dict def CharDict begin /white", 173 "{dup 255 eq{pop}{1 dict begin 100 sub neg 512 exch bitshift", 174 "/cw exch def cw 0 0 0 cw 1 setcachedevice end}ifelse}def /black", 175 "{dup 255 eq{pop}{1 dict begin 110 sub neg 512 exch bitshift", 176 "/cw exch def cw 0 0 0 cw 1 setcachedevice 0 0 moveto cw 0 rlineto", 177 "0 1 rlineto cw neg 0 rlineto closepath fill end}ifelse}def /numbuild", 178 "{dup 255 eq{pop}{6 0 0 0 6 1 setcachedevice 0 1 5{0 moveto", 179 "dup 32 and 32 eq{1 0 rlineto 0 1 rlineto -1 0 rlineto closepath", 180 "fill newpath}if 1 bitshift}for pop}ifelse}def /.notdef {}", 181 "def /255 {}def end /BuildChar{exch begin dup 110 ge{Encoding", 182 "exch get 3 string cvs cvi CharDict /black get}{dup 100 ge {Encoding", 183 "exch get 3 string cvs cvi CharDict /white get}{Encoding exch get", 184 "3 string cvs cvi CharDict /numbuild get}ifelse}ifelse exec end", 185 "}def end /Bitfont newfont definefont 1 scalefont setfont", 186 NULL 187 }; 188 int i; 189 for (i = 0; fontPrologue[i] != NULL; i++) 190 fprintf(fd, "%s\n", fontPrologue[i]); 191} 192 193void 194printTIF(TIFF* tif, uint16 pageNumber) 195{ 196 uint32 w, h; 197 uint16 unit, compression; 198 float xres, yres, scale = 1.0; 199 tstrip_t s, ns; 200 time_t creation_time; 201 202 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); 203 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); 204 if (!TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression) 205 || compression < COMPRESSION_CCITTRLE 206 || compression > COMPRESSION_CCITT_T6) 207 return; 208 if (!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) || !xres) { 209 TIFFWarning(TIFFFileName(tif), 210 "No x-resolution, assuming %g dpi", defxres); 211 xres = defxres; 212 } 213 if (!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) || !yres) { 214 TIFFWarning(TIFFFileName(tif), 215 "No y-resolution, assuming %g lpi", defyres); 216 yres = defyres; /* XXX */ 217 } 218 if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &unit) && 219 unit == RESUNIT_CENTIMETER) { 220 xres *= 2.54F; 221 yres *= 2.54F; 222 } 223 if (pageWidth == 0) 224 pageWidth = w / xres; 225 if (pageHeight == 0) 226 pageHeight = h / yres; 227 228 printf("%%!PS-Adobe-3.0\n"); 229 printf("%%%%Creator: fax2ps\n"); 230#ifdef notdef 231 printf("%%%%Title: %s\n", file); 232#endif 233 creation_time = time(0); 234 printf("%%%%CreationDate: %s", ctime(&creation_time)); 235 printf("%%%%Origin: 0 0\n"); 236 printf("%%%%BoundingBox: 0 0 %u %u\n", 237 (int)(pageWidth * points), (int)(pageHeight * points)); /* XXX */ 238 printf("%%%%Pages: (atend)\n"); 239 printf("%%%%EndComments\n"); 240 printf("%%%%BeginProlog\n"); 241 emitFont(stdout); 242 printf("/d{bind def}def\n"); /* bind and def proc */ 243 printf("/m{0 exch moveto}d\n"); 244 printf("/s{show}d\n"); 245 printf("/p{showpage}d \n"); /* end page */ 246 printf("%%%%EndProlog\n"); 247 printf("%%%%Page: \"%u\" %u\n", pageNumber, pageNumber); 248 printf("/$pageTop save def gsave\n"); 249 if (scaleToPage) 250 scale = pageHeight / (h/yres) < pageWidth / (w/xres) ? 251 pageHeight / (h/yres) : pageWidth / (w/xres); 252 printf("%g %g translate\n", 253 points * (pageWidth - scale*w/xres) * half, 254 points * (scale*h/yres + (pageHeight - scale*h/yres) * half)); 255 printf("%g %g scale\n", points/xres*scale, -points/yres*scale); 256 printf("0 setgray\n"); 257 TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, printruns); 258 ns = TIFFNumberOfStrips(tif); 259 row = 0; 260 for (s = 0; s < ns; s++) 261 (void) TIFFReadEncodedStrip(tif, s, (tdata_t) NULL, (tsize_t) -1); 262 printf("p\n"); 263 printf("grestore $pageTop restore\n"); 264 totalPages++; 265} 266 267#define GetPageNumber(tif) \ 268TIFFGetField(tif, TIFFTAG_PAGENUMBER, &pn, &ptotal) 269 270int 271findPage(TIFF* tif, uint16 pageNumber) 272{ 273 uint16 pn = (uint16) -1; 274 uint16 ptotal = (uint16) -1; 275 if (GetPageNumber(tif)) { 276 while (pn != pageNumber && TIFFReadDirectory(tif) && GetPageNumber(tif)) 277 ; 278 return (pn == pageNumber); 279 } else 280 return (TIFFSetDirectory(tif, (tdir_t)(pageNumber-1))); 281} 282 283void 284fax2ps(TIFF* tif, uint16 npages, uint16* pages, char* filename) 285{ 286 if (npages > 0) { 287 uint16 pn, ptotal; 288 int i; 289 290 if (!GetPageNumber(tif)) 291 fprintf(stderr, "%s: No page numbers, counting directories.\n", 292 filename); 293 for (i = 0; i < npages; i++) { 294 if (findPage(tif, pages[i])) 295 printTIF(tif, pages[i]); 296 else 297 fprintf(stderr, "%s: No page number %d\n", filename, pages[i]); 298 } 299 } else { 300 uint16 pageNumber = 0; 301 do 302 printTIF(tif, pageNumber++); 303 while (TIFFReadDirectory(tif)); 304 } 305} 306 307#undef GetPageNumber 308 309static int 310pcompar(const void* va, const void* vb) 311{ 312 const int* pa = (const int*) va; 313 const int* pb = (const int*) vb; 314 return (*pa - *pb); 315} 316 317static void usage(int code); 318 319int 320main(int argc, char** argv) 321{ 322 extern int optind; 323 extern char* optarg; 324 uint16 *pages = NULL, npages = 0, pageNumber; 325 int c, dowarnings = 0; /* if 1, enable library warnings */ 326 TIFF* tif; 327 328 while ((c = getopt(argc, argv, "l:p:x:y:W:H:wS")) != -1) 329 switch (c) { 330 case 'H': /* page height */ 331 pageHeight = (float)atof(optarg); 332 break; 333 case 'S': /* scale to page */ 334 scaleToPage = 1; 335 break; 336 case 'W': /* page width */ 337 pageWidth = (float)atof(optarg); 338 break; 339 case 'p': /* print specific page */ 340 pageNumber = (uint16)atoi(optarg); 341 if (pages) 342 pages = (uint16*) realloc(pages, (npages+1)*sizeof(uint16)); 343 else 344 pages = (uint16*) malloc(sizeof(uint16)); 345 pages[npages++] = pageNumber; 346 break; 347 case 'w': 348 dowarnings = 1; 349 break; 350 case 'x': 351 defxres = (float)atof(optarg); 352 break; 353 case 'y': 354 defyres = (float)atof(optarg); 355 break; 356 case 'l': 357 maxline = atoi(optarg); 358 break; 359 case '?': 360 usage(-1); 361 } 362 if (npages > 0) 363 qsort(pages, npages, sizeof(uint16), pcompar); 364 if (!dowarnings) 365 TIFFSetWarningHandler(0); 366 if (optind < argc) { 367 do { 368 tif = TIFFOpen(argv[optind], "r"); 369 if (tif) { 370 fax2ps(tif, npages, pages, argv[optind]); 371 TIFFClose(tif); 372 } else 373 fprintf(stderr, "%s: Can not open, or not a TIFF file.\n", 374 argv[optind]); 375 } while (++optind < argc); 376 } else { 377 int n; 378 FILE* fd; 379 char buf[16*1024]; 380 381 fd = tmpfile(); 382 if (fd == NULL) { 383 fprintf(stderr, "Could not create temporary file, exiting.\n"); 384 fclose(fd); 385 exit(-2); 386 } 387#if defined(HAVE_SETMODE) && defined(O_BINARY) 388 setmode(fileno(stdin), O_BINARY); 389#endif 390 while ((n = read(fileno(stdin), buf, sizeof (buf))) > 0) 391 write(fileno(fd), buf, n); 392 lseek(fileno(fd), 0, SEEK_SET); 393#if defined(_WIN32) && defined(USE_WIN32_FILEIO) 394 tif = TIFFFdOpen(_get_osfhandle(fileno(fd)), "temp", "r"); 395#else 396 tif = TIFFFdOpen(fileno(fd), "temp", "r"); 397#endif 398 if (tif) { 399 fax2ps(tif, npages, pages, "<stdin>"); 400 TIFFClose(tif); 401 } else 402 fprintf(stderr, "Can not open, or not a TIFF file.\n"); 403 fclose(fd); 404 } 405 printf("%%%%Trailer\n"); 406 printf("%%%%Pages: %u\n", totalPages); 407 printf("%%%%EOF\n"); 408 409 return (0); 410} 411 412char* stuff[] = { 413"usage: fax2ps [options] [input.tif ...]", 414"where options are:", 415" -w suppress warning messages", 416" -l chars set maximum output line length for generated PostScript", 417" -p page# select page to print (can use multiple times)", 418" -x xres set default horizontal resolution of input data (dpi)", 419" -y yres set default vertical resolution of input data (lpi)", 420" -S scale output to page size", 421" -W width set output page width (inches), default is 8.5", 422" -H height set output page height (inches), default is 11", 423NULL 424}; 425 426static void 427usage(int code) 428{ 429 char buf[BUFSIZ]; 430 int i; 431 432 setbuf(stderr, buf); 433 fprintf(stderr, "%s\n\n", TIFFGetVersion()); 434 for (i = 0; stuff[i] != NULL; i++) 435 fprintf(stderr, "%s\n", stuff[i]); 436 exit(code); 437} 438 439/* vim: set ts=8 sts=8 sw=8 noet: */ 440/* 441 * Local Variables: 442 * mode: c 443 * c-basic-offset: 8 444 * fill-column: 78 445 * End: 446 */ 447