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