2 |
- |
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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 |
}
|