xref: /plan9/sys/src/cmd/gs/src/gdevtifs.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1994, 1995 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevtifs.c,v 1.7 2002/10/07 08:28:56 ghostgum Exp $ */
18 /* TIFF-writing substructure */
19 #include "stdio_.h"
20 #include "time_.h"
21 #include "gstypes.h"
22 #include "gscdefs.h"
23 #include "gdevprn.h"
24 #include "gdevtifs.h"
25 
26 /*
27  * Define the standard contents of a TIFF directory.
28  * Clients may add more items, also sorted in increasing tag order.
29  */
30 typedef struct TIFF_std_directory_entries_s {
31     TIFF_dir_entry SubFileType;
32     TIFF_dir_entry ImageWidth;
33     TIFF_dir_entry ImageLength;
34     TIFF_dir_entry StripOffsets;
35     TIFF_dir_entry Orientation;
36     TIFF_dir_entry RowsPerStrip;
37     TIFF_dir_entry StripByteCounts;
38     TIFF_dir_entry XResolution;
39     TIFF_dir_entry YResolution;
40     TIFF_dir_entry PlanarConfig;
41     TIFF_dir_entry ResolutionUnit;
42     TIFF_dir_entry PageNumber;
43     TIFF_dir_entry Software;
44     TIFF_dir_entry DateTime;
45 } TIFF_std_directory_entries;
46 
47 /* Define values that follow the directory entries. */
48 typedef struct TIFF_std_directory_values_s {
49     TIFF_ulong diroff;		/* offset to next directory */
50     TIFF_ulong xresValue[2];	/* XResolution indirect value */
51     TIFF_ulong yresValue[2];	/* YResolution indirect value */
52 #define maxSoftware 40
53     char softwareValue[maxSoftware];	/* Software indirect value */
54     char dateTimeValue[20];	/* DateTime indirect value */
55 } TIFF_std_directory_values;
56 private const TIFF_std_directory_entries std_entries_initial =
57 {
58     {TIFFTAG_SubFileType, TIFF_LONG, 1, SubFileType_page},
59     {TIFFTAG_ImageWidth, TIFF_LONG, 1},
60     {TIFFTAG_ImageLength, TIFF_LONG, 1},
61     {TIFFTAG_StripOffsets, TIFF_LONG, 1},
62     {TIFFTAG_Orientation, TIFF_SHORT, 1, Orientation_top_left},
63     {TIFFTAG_RowsPerStrip, TIFF_LONG, 1},
64     {TIFFTAG_StripByteCounts, TIFF_LONG, 1},
65     {TIFFTAG_XResolution, TIFF_RATIONAL | TIFF_INDIRECT, 1,
66      offset_of(TIFF_std_directory_values, xresValue[0])},
67     {TIFFTAG_YResolution, TIFF_RATIONAL | TIFF_INDIRECT, 1,
68      offset_of(TIFF_std_directory_values, yresValue[0])},
69     {TIFFTAG_PlanarConfig, TIFF_SHORT, 1, PlanarConfig_contig},
70     {TIFFTAG_ResolutionUnit, TIFF_SHORT, 1, ResolutionUnit_inch},
71     {TIFFTAG_PageNumber, TIFF_SHORT, 2},
72     {TIFFTAG_Software, TIFF_ASCII | TIFF_INDIRECT, 0,
73      offset_of(TIFF_std_directory_values, softwareValue[0])},
74     {TIFFTAG_DateTime, TIFF_ASCII | TIFF_INDIRECT, 20,
75      offset_of(TIFF_std_directory_values, dateTimeValue[0])}
76 };
77 private const TIFF_std_directory_values std_values_initial =
78 {
79     0,
80     {0, 1},
81     {0, 1},
82     {0},
83     {0, 0}
84 };
85 
86 /* Fix up tag values on big-endian machines if necessary. */
87 #if arch_is_big_endian
88 private void
tiff_fixup_tag(TIFF_dir_entry * dp)89 tiff_fixup_tag(TIFF_dir_entry * dp)
90 {
91     switch (dp->type) {
92 	case TIFF_SHORT:
93 	case TIFF_SSHORT:
94 	    /* We may have two shorts packed into a TIFF_ulong. */
95 	    dp->value = (dp->value << 16) + (dp->value >> 16);
96 	    break;
97 	case TIFF_BYTE:
98 	case TIFF_SBYTE:
99 	    dp->value <<= 24;
100 	    break;
101     }
102 }
103 #else
104 #  define tiff_fixup_tag(dp) DO_NOTHING
105 #endif
106 
107 /* Begin a TIFF page. */
108 int
gdev_tiff_begin_page(gx_device_printer * pdev,gdev_tiff_state * tifs,FILE * fp,const TIFF_dir_entry * entries,int entry_count,const byte * values,int value_size,long max_strip_size)109 gdev_tiff_begin_page(gx_device_printer * pdev, gdev_tiff_state * tifs,
110 		     FILE * fp,
111 		     const TIFF_dir_entry * entries, int entry_count,
112 		     const byte * values, int value_size, long max_strip_size)
113 {
114     gs_memory_t *mem = pdev->memory;
115     TIFF_std_directory_entries std_entries;
116     const TIFF_dir_entry *pse;
117     const TIFF_dir_entry *pce;
118     TIFF_dir_entry entry;
119 #define std_entry_count\
120   (sizeof(TIFF_std_directory_entries) / sizeof(TIFF_dir_entry))
121     int nse, nce, ntags;
122     TIFF_std_directory_values std_values;
123 
124 #define std_value_size sizeof(TIFF_std_directory_values)
125 
126     tifs->mem = mem;
127     if (gdev_prn_file_is_new(pdev)) {
128 	/* This is a new file; write the TIFF header. */
129 	static const TIFF_header hdr = {
130 #if arch_is_big_endian
131 	    TIFF_magic_big_endian,
132 #else
133 	    TIFF_magic_little_endian,
134 #endif
135 	    TIFF_version_value,
136 	    sizeof(TIFF_header)
137 	};
138 
139 	fwrite((const char *)&hdr, sizeof(hdr), 1, fp);
140 	tifs->prev_dir = 0;
141     } else {			/* Patch pointer to this directory from previous. */
142 	TIFF_ulong offset = (TIFF_ulong) tifs->dir_off;
143 
144 	fseek(fp, tifs->prev_dir, SEEK_SET);
145 	fwrite((char *)&offset, sizeof(offset), 1, fp);
146 	fseek(fp, tifs->dir_off, SEEK_SET);
147     }
148 
149     /* We're going to shuffle the two tag lists together. */
150     /* Both lists are sorted; entries in the client list */
151     /* replace entries with the same tag in the standard list. */
152     for (ntags = 0, pse = (const TIFF_dir_entry *)&std_entries_initial,
153 	 nse = std_entry_count, pce = entries, nce = entry_count;
154 	 nse && nce; ++ntags
155 	) {
156 	if (pse->tag < pce->tag)
157 	    ++pse, --nse;
158 	else if (pce->tag < pse->tag)
159 	    ++pce, --nce;
160 	else
161 	    ++pse, --nse, ++pce, --nce;
162     }
163     ntags += nse + nce;
164     tifs->ntags = ntags;
165 
166     /* Write count of tags in directory. */
167     {
168 	TIFF_short dircount = ntags;
169 
170 	fwrite((char *)&dircount, sizeof(dircount), 1, fp);
171     }
172     tifs->dir_off = ftell(fp);
173 
174     /* Fill in standard directory tags. */
175     std_entries = std_entries_initial;
176     std_values = std_values_initial;
177     std_entries.ImageWidth.value = pdev->width;
178     std_entries.ImageLength.value = pdev->height;
179     if (max_strip_size == 0) {
180 	tifs->strip_count = 1;
181 	tifs->rows = pdev->height;
182 	std_entries.RowsPerStrip.value = pdev->height;
183     } else {
184 	int max_strip_rows =
185 	    max_strip_size / gdev_mem_bytes_per_scan_line((gx_device *)pdev);
186         int rps = max(1, max_strip_rows);
187 
188 	tifs->strip_count = (pdev->height + rps - 1) / rps;
189 	tifs->rows = rps;
190 	std_entries.RowsPerStrip.value = rps;
191 	std_entries.StripOffsets.count = tifs->strip_count;
192 	std_entries.StripByteCounts.count = tifs->strip_count;
193     }
194     tifs->StripOffsets = (TIFF_ulong *)gs_alloc_bytes(mem,
195     		    (tifs->strip_count)*2*sizeof(TIFF_ulong),
196 		    "gdev_tiff_begin_page(StripOffsets)");
197     tifs->StripByteCounts = &(tifs->StripOffsets[tifs->strip_count]);
198     if (tifs->StripOffsets == 0)
199 	return_error(gs_error_VMerror);
200     std_entries.PageNumber.value = (TIFF_ulong) pdev->PageCount;
201     std_values.xresValue[0] = (TIFF_ulong)pdev->x_pixels_per_inch;
202     std_values.yresValue[0] = (TIFF_ulong)pdev->y_pixels_per_inch;
203     {
204 	char revs[10];
205 
206 	strncpy(std_values.softwareValue, gs_product, maxSoftware);
207 	std_values.softwareValue[maxSoftware - 1] = 0;
208 	sprintf(revs, " %1.2f", gs_revision / 100.0);
209 	strncat(std_values.softwareValue, revs,
210 		maxSoftware - strlen(std_values.softwareValue) - 1);
211 	std_entries.Software.count =
212 	    strlen(std_values.softwareValue) + 1;
213     }
214     {
215 	struct tm tms;
216 	time_t t;
217 
218 	time(&t);
219 	tms = *localtime(&t);
220 	sprintf(std_values.dateTimeValue,
221 		"%04d:%02d:%02d %02d:%02d:%02d",
222 		tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
223 		tms.tm_hour, tms.tm_min, tms.tm_sec);
224     }
225 
226     /* Write the merged directory. */
227     for (pse = (const TIFF_dir_entry *)&std_entries,
228 	 nse = std_entry_count, pce = entries, nce = entry_count;
229 	 nse || nce;
230 	) {
231 	bool std;
232 
233 	if (nce == 0 || (nse != 0 && pse->tag < pce->tag))
234 	    std = true, entry = *pse++, --nse;
235 	else if (nse == 0 || (nce != 0 && pce->tag < pse->tag))
236 	    std = false, entry = *pce++, --nce;
237 	else
238 	    std = false, ++pse, --nse, entry = *pce++, --nce;
239 	if (entry.tag == TIFFTAG_StripOffsets)  {
240 	    if (tifs->strip_count > 1) {
241 		tifs->offset_StripOffsets = tifs->dir_off +
242 		    (ntags * sizeof(TIFF_dir_entry)) + std_value_size + value_size;
243 		entry.value = tifs->offset_StripOffsets;
244 	    } else {
245 		tifs->offset_StripOffsets = ftell(fp) +
246 		    offset_of(TIFF_dir_entry, value);
247 	    }
248 	}
249 	if (entry.tag == TIFFTAG_StripByteCounts) {
250 	    if (tifs->strip_count > 1) {
251 	        tifs->offset_StripByteCounts = tifs->dir_off +
252 		    (ntags * sizeof(TIFF_dir_entry)) + std_value_size + value_size +
253 		    (sizeof(TIFF_ulong) * tifs->strip_count);
254 	        entry.value = tifs->offset_StripByteCounts;
255 	    } else {
256 		tifs->offset_StripByteCounts = ftell(fp) +
257 		    offset_of(TIFF_dir_entry, value);
258 	    }
259 	}
260 	tiff_fixup_tag(&entry);	/* don't fix up indirects */
261 	if (entry.type & TIFF_INDIRECT) {
262 	    /* Fix up the offset for an indirect value. */
263 	    entry.type -= TIFF_INDIRECT;
264 	    entry.value +=
265 		tifs->dir_off + ntags * sizeof(TIFF_dir_entry) +
266 		(std ? 0 : std_value_size);
267 	}
268 	fwrite((char *)&entry, sizeof(entry), 1, fp);
269     }
270 
271     /* Write the indirect values. */
272     fwrite((const char *)&std_values, sizeof(std_values), 1, fp);
273     fwrite((const char *)values, value_size, 1, fp);
274     /* Write placeholders for the strip offsets. */
275     fwrite(tifs->StripOffsets, sizeof(TIFF_ulong), 2 * tifs->strip_count, fp);
276     tifs->strip_index = 0;
277     tifs->StripOffsets[0] = ftell(fp);
278     return 0;
279 }
280 
281 /* End a TIFF strip. */
282 /* Record the size of the current strip, update the	*/
283 /* start of the next strip  and bump the strip_index	*/
284 int
gdev_tiff_end_strip(gdev_tiff_state * tifs,FILE * fp)285 gdev_tiff_end_strip(gdev_tiff_state * tifs, FILE * fp)
286 {
287     TIFF_ulong strip_size;
288     TIFF_ulong next_strip_start;
289     int pad = 0;
290 
291     next_strip_start = (TIFF_ulong)ftell(fp);
292     strip_size = next_strip_start - tifs->StripOffsets[tifs->strip_index];
293     if (next_strip_start & 1) {
294         next_strip_start++;	/* WORD alignment */
295 	fwrite(&pad, 1, 1, fp);
296     }
297     tifs->StripByteCounts[tifs->strip_index++] = strip_size;
298     if (tifs->strip_index < tifs->strip_count)
299 	tifs->StripOffsets[tifs->strip_index] = next_strip_start;
300     return 0;
301 }
302 
303 /* End a TIFF page. */
304 int
gdev_tiff_end_page(gdev_tiff_state * tifs,FILE * fp)305 gdev_tiff_end_page(gdev_tiff_state * tifs, FILE * fp)
306 {
307     gs_memory_t *mem = tifs->mem;
308     long dir_off = tifs->dir_off;
309     int tags_size = tifs->ntags * sizeof(TIFF_dir_entry);
310 
311     tifs->prev_dir =
312 	dir_off + tags_size + offset_of(TIFF_std_directory_values, diroff);
313     tifs->dir_off = ftell(fp);
314     /* Patch strip byte counts and offsets values. */
315     /* The offset in the file was determined at begin_page and may be indirect */
316     fseek(fp, tifs->offset_StripOffsets, SEEK_SET);
317     fwrite(tifs->StripOffsets, sizeof(TIFF_ulong), tifs->strip_count, fp);
318     fseek(fp, tifs->offset_StripByteCounts, SEEK_SET);
319     fwrite(tifs->StripByteCounts, sizeof(TIFF_ulong), tifs->strip_count, fp);
320     gs_free_object(mem, tifs->StripOffsets, "gdev_tiff_begin_page(StripOffsets)");
321     return 0;
322 }
323