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