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