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