/src/ghostpdl/xps/xpszip.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* XPS interpreter - zip container parsing */ |
18 | | |
19 | | #include "ghostxps.h" |
20 | | #include "pagelist.h" |
21 | | |
22 | | static int isfile(gs_memory_t *mem, char *path) |
23 | 0 | { |
24 | 0 | gp_file *file = gp_fopen(mem, path, "rb"); |
25 | 0 | if (file) |
26 | 0 | { |
27 | 0 | gp_fclose(file); |
28 | 0 | return 1; |
29 | 0 | } |
30 | 0 | return 0; |
31 | 0 | } |
32 | | |
33 | | static inline int getshort(gp_file *file) |
34 | 11.2k | { |
35 | 11.2k | int a = xps_getc(file); |
36 | 11.2k | int b = xps_getc(file); |
37 | 11.2k | return a | (b << 8); |
38 | 11.2k | } |
39 | | |
40 | | static inline int getlong(gp_file *file) |
41 | 6.27k | { |
42 | 6.27k | unsigned int a = (unsigned int)xps_getc(file); |
43 | 6.27k | unsigned int b = (unsigned int)xps_getc(file); |
44 | 6.27k | unsigned int c = (unsigned int)xps_getc(file); |
45 | 6.27k | unsigned int d = (unsigned int)xps_getc(file); |
46 | 6.27k | return a | (b << 8) | (c << 16) | (d << 24); |
47 | 6.27k | } |
48 | | |
49 | | static void * |
50 | | xps_zip_alloc_items(xps_context_t *ctx, int items, int size) |
51 | 280 | { |
52 | 280 | void *item; |
53 | 280 | uint32_t total; |
54 | | |
55 | 280 | if (check_uint32_multiply((uint32_t)items, (uint32_t)size, &total) != 0) { |
56 | 0 | gs_throw(gs_error_limitcheck, "item is too large"); |
57 | 0 | return NULL; |
58 | 0 | } |
59 | | |
60 | 280 | item = xps_alloc(ctx, total); |
61 | 280 | if (!item) { |
62 | 0 | gs_throw(gs_error_VMerror, "out of memory: item.\n"); |
63 | 0 | return NULL; |
64 | 0 | } |
65 | 280 | return item; |
66 | 280 | } |
67 | | |
68 | | static void |
69 | | xps_zip_free(xps_context_t *ctx, void *ptr) |
70 | 280 | { |
71 | 280 | xps_free(ctx, ptr); |
72 | 280 | } |
73 | | |
74 | | static int |
75 | | xps_compare_entries(const void *a0, const void *b0) |
76 | 2.02k | { |
77 | 2.02k | xps_entry_t *a = (xps_entry_t*) a0; |
78 | 2.02k | xps_entry_t *b = (xps_entry_t*) b0; |
79 | 2.02k | return xps_strcasecmp(a->name, b->name); |
80 | 2.02k | } |
81 | | |
82 | | static xps_entry_t * |
83 | | xps_find_zip_entry(xps_context_t *ctx, const char *name) |
84 | 284 | { |
85 | 284 | int l = 0; |
86 | 284 | int r = ctx->zip_count - 1; |
87 | 987 | while (l <= r) |
88 | 987 | { |
89 | 987 | int m = (l + r) >> 1; |
90 | 987 | int c = xps_strcasecmp(name, ctx->zip_table[m].name); |
91 | 987 | if (c < 0) |
92 | 246 | r = m - 1; |
93 | 741 | else if (c > 0) |
94 | 457 | l = m + 1; |
95 | 284 | else |
96 | 284 | return &ctx->zip_table[m]; |
97 | 987 | } |
98 | 0 | return NULL; |
99 | 284 | } |
100 | | |
101 | | /* |
102 | | * Inflate the data in a zip entry. |
103 | | */ |
104 | | |
105 | | static int |
106 | | xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) |
107 | 284 | { |
108 | 284 | z_stream stream; |
109 | 284 | unsigned char *inbuf; |
110 | 284 | int sig; |
111 | 284 | int version, general, method; |
112 | 284 | int namelength, extralength; |
113 | 284 | int code; |
114 | | |
115 | 284 | if_debug1m('|', ctx->memory, "zip: inflating entry '%s'\n", ent->name); |
116 | | |
117 | 284 | if (xps_fseek(ctx->file, ent->offset, 0) < 0) |
118 | 0 | return gs_throw1(-1, "seek to offset %d failed.", ent->offset); |
119 | | |
120 | 284 | sig = getlong(ctx->file); |
121 | 284 | if (sig != ZIP_LOCAL_FILE_SIG) |
122 | 5 | return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig); |
123 | | |
124 | 279 | version = getshort(ctx->file); |
125 | 279 | (void)version; /* Avoid unused variable compiler warning */ |
126 | 279 | general = getshort(ctx->file); |
127 | 279 | if (general & ZIP_ENCRYPTED_FLAG) |
128 | 0 | return gs_throw(-1, "zip file content is encrypted"); |
129 | 279 | method = getshort(ctx->file); |
130 | 279 | (void) getshort(ctx->file); /* file time */ |
131 | 279 | (void) getshort(ctx->file); /* file date */ |
132 | 279 | (void) getlong(ctx->file); /* crc-32 */ |
133 | 279 | (void) getlong(ctx->file); /* csize */ |
134 | 279 | (void) getlong(ctx->file); /* usize */ |
135 | 279 | namelength = getshort(ctx->file); |
136 | 279 | extralength = getshort(ctx->file); |
137 | | |
138 | 279 | if (namelength < 0 || namelength > 65535) |
139 | 0 | return gs_rethrow(gs_error_ioerror, "Illegal namelength (can't happen).\n"); |
140 | 279 | if (extralength < 0 || extralength > 65535) |
141 | 0 | return gs_rethrow(gs_error_ioerror, "Illegal extralength (can't happen).\n"); |
142 | | |
143 | 279 | if (xps_fseek(ctx->file, namelength + extralength, 1) != 0) |
144 | 0 | return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", namelength + extralength); |
145 | | |
146 | 279 | if (method == 0) |
147 | 0 | { |
148 | 0 | code = xps_fread(outbuf, 1, ent->usize, ctx->file); |
149 | 0 | if (code != ent->usize) |
150 | 0 | return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->usize); |
151 | 0 | } |
152 | 279 | else if (method == 8) |
153 | 279 | { |
154 | 279 | inbuf = xps_alloc(ctx, ent->csize); |
155 | 279 | if (!inbuf) { |
156 | 0 | return gs_rethrow(gs_error_VMerror, "out of memory.\n"); |
157 | 0 | } |
158 | | |
159 | 279 | code = xps_fread(inbuf, 1, ent->csize, ctx->file); |
160 | 279 | if (code != ent->csize) { |
161 | 0 | xps_free(ctx, inbuf); |
162 | 0 | return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->csize); |
163 | 0 | } |
164 | | |
165 | 279 | memset(&stream, 0, sizeof(z_stream)); |
166 | 279 | stream.zalloc = (alloc_func) xps_zip_alloc_items; |
167 | 279 | stream.zfree = (free_func) xps_zip_free; |
168 | 279 | stream.opaque = ctx; |
169 | 279 | stream.next_in = inbuf; |
170 | 279 | stream.avail_in = ent->csize; |
171 | 279 | stream.next_out = outbuf; |
172 | 279 | stream.avail_out = ent->usize; |
173 | | |
174 | 279 | code = inflateInit2(&stream, -15); |
175 | 279 | if (code != Z_OK) |
176 | 0 | { |
177 | 0 | xps_free(ctx, inbuf); |
178 | 0 | return gs_throw1(-1, "zlib inflateInit2 error: %s", stream.msg); |
179 | 0 | } |
180 | 279 | code = inflate(&stream, Z_FINISH); |
181 | 279 | if (code != Z_STREAM_END) |
182 | 2 | { |
183 | 2 | inflateEnd(&stream); |
184 | 2 | xps_free(ctx, inbuf); |
185 | 2 | return gs_throw1(-1, "zlib inflate error: %s", stream.msg); |
186 | 2 | } |
187 | 277 | code = inflateEnd(&stream); |
188 | 277 | if (code != Z_OK) |
189 | 0 | { |
190 | 0 | xps_free(ctx, inbuf); |
191 | 0 | return gs_throw1(-1, "zlib inflateEnd error: %s", stream.msg); |
192 | 0 | } |
193 | | |
194 | 277 | xps_free(ctx, inbuf); |
195 | | |
196 | | /* If the stream has less data than advertised, then zero the remainder. */ |
197 | 277 | if (stream.avail_out > 0) |
198 | 61 | { |
199 | 61 | gs_warn("truncated zipfile entry; possibly corrupt data"); |
200 | 61 | memset(stream.next_out, 0, stream.avail_out); |
201 | 61 | } |
202 | 277 | } |
203 | 0 | else |
204 | 0 | { |
205 | 0 | return gs_throw1(-1, "unknown compression method (%d)", method); |
206 | 0 | } |
207 | | |
208 | 277 | return gs_okay; |
209 | 279 | } |
210 | | |
211 | | /* |
212 | | * Read the central directory in a zip file. |
213 | | */ |
214 | | |
215 | | static int |
216 | | xps_read_zip_dir(xps_context_t *ctx, int start_offset) |
217 | 100 | { |
218 | 100 | int sig; |
219 | 100 | int offset, count, read; |
220 | 100 | int namesize, metasize, commentsize; |
221 | 100 | int i; |
222 | | |
223 | 100 | if (xps_fseek(ctx->file, start_offset, 0) != 0) |
224 | 0 | return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.", start_offset); |
225 | | |
226 | 100 | sig = getlong(ctx->file); |
227 | 100 | if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) |
228 | 0 | return gs_throw1(-1, "wrong zip end of central directory signature (0x%x)", sig); |
229 | | |
230 | 100 | (void) getshort(ctx->file); /* this disk */ |
231 | 100 | (void) getshort(ctx->file); /* start disk */ |
232 | 100 | (void) getshort(ctx->file); /* entries in this disk */ |
233 | 100 | count = getshort(ctx->file); /* entries in central directory disk */ |
234 | 100 | (void) getlong(ctx->file); /* size of central directory */ |
235 | 100 | offset = getlong(ctx->file); /* offset to central directory */ |
236 | | |
237 | 100 | if (count < 0 || count > 65535) |
238 | 0 | return gs_rethrow(gs_error_rangecheck, "invalid number of entries in central directory disk (can't happen)"); |
239 | | |
240 | 100 | ctx->zip_count = count; |
241 | 100 | if (count > INT_MAX / sizeof(xps_entry_t *)) |
242 | 0 | return gs_rethrow(gs_error_limitcheck, "entry table too large"); |
243 | 100 | ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * (size_t)count); |
244 | 100 | if (!ctx->zip_table) |
245 | 0 | return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry table"); |
246 | | |
247 | 100 | memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count); |
248 | | |
249 | 100 | if (xps_fseek(ctx->file, offset, 0) != 0) |
250 | 2 | return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", offset); |
251 | | |
252 | 902 | for (i = 0; i < count; i++) |
253 | 830 | { |
254 | 830 | sig = getlong(ctx->file); |
255 | 830 | if (sig != ZIP_CENTRAL_DIRECTORY_SIG) |
256 | 25 | return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig); |
257 | | |
258 | 805 | (void) getshort(ctx->file); /* version made by */ |
259 | 805 | (void) getshort(ctx->file); /* version to extract */ |
260 | 805 | (void) getshort(ctx->file); /* general */ |
261 | 805 | (void) getshort(ctx->file); /* method */ |
262 | 805 | (void) getshort(ctx->file); /* last mod file time */ |
263 | 805 | (void) getshort(ctx->file); /* last mod file date */ |
264 | 805 | (void) getlong(ctx->file); /* crc-32 */ |
265 | 805 | ctx->zip_table[i].csize = getlong(ctx->file); |
266 | 805 | ctx->zip_table[i].usize = getlong(ctx->file); |
267 | 805 | namesize = getshort(ctx->file); |
268 | 805 | if (namesize < 0 || namesize > 65535) |
269 | 0 | return gs_rethrow(gs_error_rangecheck, "illegal namesize (can't happen)"); |
270 | 805 | metasize = getshort(ctx->file); |
271 | 805 | commentsize = getshort(ctx->file); |
272 | 805 | (void) getshort(ctx->file); /* disk number start */ |
273 | 805 | (void) getshort(ctx->file); /* int file atts */ |
274 | 805 | (void) getlong(ctx->file); /* ext file atts */ |
275 | 805 | ctx->zip_table[i].offset = getlong(ctx->file); |
276 | | |
277 | 805 | if (ctx->zip_table[i].csize < 0 || ctx->zip_table[i].usize < 0) |
278 | 0 | return gs_throw(gs_error_ioerror, "cannot read zip entries larger than 2GB"); |
279 | | |
280 | 805 | if (namesize == INT_MAX) |
281 | 0 | return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name"); |
282 | 805 | ctx->zip_table[i].name = xps_alloc(ctx, (size_t)namesize + 1); |
283 | 805 | if (!ctx->zip_table[i].name) |
284 | 0 | return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name"); |
285 | | |
286 | 805 | read = xps_fread(ctx->zip_table[i].name, 1, (size_t)namesize, ctx->file); |
287 | 805 | if (read != namesize) |
288 | 1 | return gs_throw1(gs_error_ioerror, "failed to read %d bytes", namesize); |
289 | | |
290 | 804 | ctx->zip_table[i].name[namesize] = 0; |
291 | | |
292 | 804 | if (xps_fseek(ctx->file, metasize, 1) != 0) |
293 | 0 | return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", metasize); |
294 | 804 | if (xps_fseek(ctx->file, commentsize, 1) != 0) |
295 | 0 | return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", commentsize); |
296 | 804 | } |
297 | | |
298 | 72 | qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries); |
299 | | |
300 | 858 | for (i = 0; i < ctx->zip_count; i++) |
301 | 786 | { |
302 | 786 | if_debug3m('|', ctx->memory, "zip entry '%s' csize=%d usize=%d\n", |
303 | 786 | ctx->zip_table[i].name, |
304 | 786 | ctx->zip_table[i].csize, |
305 | 786 | ctx->zip_table[i].usize); |
306 | 786 | } |
307 | | |
308 | 72 | return gs_okay; |
309 | 98 | } |
310 | | |
311 | | static int |
312 | | xps_find_and_read_zip_dir(xps_context_t *ctx) |
313 | 136 | { |
314 | 136 | int filesize, back, maxback; |
315 | 136 | int i, n; |
316 | 136 | char buf[512]; |
317 | | |
318 | 136 | if (xps_fseek(ctx->file, 0, SEEK_END) < 0) |
319 | 0 | return gs_throw(-1, "seek to end failed."); |
320 | | |
321 | 136 | filesize = xps_ftell(ctx->file); |
322 | | |
323 | 136 | maxback = MIN(filesize, 0xFFFF + sizeof buf); |
324 | 136 | back = MIN(maxback, sizeof buf); |
325 | | |
326 | 1.08k | while (back < maxback) |
327 | 1.04k | { |
328 | 1.04k | if (xps_fseek(ctx->file, filesize - back, 0) < 0) |
329 | 0 | return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", filesize - back); |
330 | | |
331 | 1.04k | n = xps_fread(buf, 1, sizeof buf, ctx->file); |
332 | 1.04k | if (n < 0) |
333 | 0 | return gs_throw(-1, "cannot read end of central directory"); |
334 | | |
335 | 485k | for (i = n - 4; i > 0; i--) |
336 | 484k | if (!memcmp(buf + i, "PK\5\6", 4)) |
337 | 100 | return xps_read_zip_dir(ctx, filesize - back + i); |
338 | | |
339 | 946 | back += sizeof buf - 4; |
340 | 946 | } |
341 | | |
342 | 36 | return gs_throw(-1, "cannot find end of central directory"); |
343 | 136 | } |
344 | | |
345 | | /* |
346 | | * Read and interleave split parts from a ZIP file. |
347 | | */ |
348 | | |
349 | | static xps_part_t * |
350 | | xps_read_zip_part(xps_context_t *ctx, const char *partname) |
351 | 284 | { |
352 | 284 | char buf[2048]; |
353 | 284 | xps_entry_t *ent; |
354 | 284 | xps_part_t *part; |
355 | 284 | int count, size, offset, i; |
356 | 284 | int code = 0; |
357 | 284 | const char *name; |
358 | 284 | int seen_last = 0; |
359 | | |
360 | 284 | name = partname; |
361 | 284 | if (name[0] == '/') |
362 | 284 | name ++; |
363 | | |
364 | | /* All in one piece */ |
365 | 284 | ent = xps_find_zip_entry(ctx, name); |
366 | 284 | if (ent) |
367 | 284 | { |
368 | 284 | part = xps_new_part(ctx, partname, ent->usize); |
369 | 284 | if (part != NULL) |
370 | 284 | code = xps_read_zip_entry(ctx, ent, part->data); |
371 | 0 | else |
372 | 0 | return NULL; |
373 | 284 | if (code < 0) |
374 | 7 | { |
375 | 7 | xps_free_part(ctx, part); |
376 | 7 | gs_rethrow1(-1, "cannot read zip entry '%s'", name); |
377 | 7 | return NULL; |
378 | 7 | } |
379 | 277 | return part; |
380 | 284 | } |
381 | | |
382 | | /* Count the number of pieces and their total size */ |
383 | 0 | count = 0; |
384 | 0 | size = 0; |
385 | 0 | while (!seen_last) |
386 | 0 | { |
387 | 0 | gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, count); |
388 | 0 | ent = xps_find_zip_entry(ctx, buf); |
389 | 0 | if (!ent) |
390 | 0 | { |
391 | 0 | gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, count); |
392 | 0 | ent = xps_find_zip_entry(ctx, buf); |
393 | 0 | seen_last = !!ent; |
394 | 0 | } |
395 | 0 | if (!ent) |
396 | 0 | break; |
397 | 0 | count ++; |
398 | | /* check for integer overflow */ |
399 | 0 | if (size > INT_MAX - ent->usize) |
400 | 0 | { |
401 | 0 | gs_throw1(-1, "part '%s' is too large", partname); |
402 | 0 | return NULL; |
403 | 0 | } |
404 | 0 | size += ent->usize; |
405 | 0 | } |
406 | 0 | if (!seen_last) |
407 | 0 | { |
408 | 0 | gs_throw1(-1, "cannot find all pieces for part '%s'", partname); |
409 | 0 | return NULL; |
410 | 0 | } |
411 | | |
412 | | /* Inflate the pieces */ |
413 | 0 | if (count) |
414 | 0 | { |
415 | 0 | part = xps_new_part(ctx, partname, size); |
416 | 0 | offset = 0; |
417 | 0 | for (i = 0; i < count; i++) |
418 | 0 | { |
419 | 0 | if (i < count - 1) |
420 | 0 | gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, i); |
421 | 0 | else |
422 | 0 | gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, i); |
423 | 0 | ent = xps_find_zip_entry(ctx, buf); |
424 | 0 | if (!ent) |
425 | 0 | gs_warn("missing piece"); |
426 | 0 | else |
427 | 0 | { |
428 | 0 | code = xps_read_zip_entry(ctx, ent, part->data + offset); |
429 | 0 | if (code < 0) |
430 | 0 | { |
431 | 0 | xps_free_part(ctx, part); |
432 | 0 | gs_rethrow1(-1, "cannot read zip entry '%s'", buf); |
433 | 0 | return NULL; |
434 | 0 | } |
435 | 0 | offset += ent->usize; |
436 | 0 | } |
437 | 0 | } |
438 | 0 | return part; |
439 | 0 | } |
440 | | |
441 | 0 | return NULL; |
442 | 0 | } |
443 | | |
444 | | /* |
445 | | * Read and interleave split parts from files in the directory. |
446 | | */ |
447 | | |
448 | | static xps_part_t * |
449 | | xps_read_dir_part(xps_context_t *ctx, const char *name) |
450 | 0 | { |
451 | 0 | char buf[2048]; |
452 | 0 | xps_part_t *part; |
453 | 0 | gp_file *file; |
454 | 0 | int count, size, offset, i, n; |
455 | 0 | int seen_last = 0; |
456 | |
|
457 | 0 | gs_strlcpy(buf, ctx->directory, sizeof buf); |
458 | 0 | gs_strlcat(buf, name, sizeof buf); |
459 | | |
460 | | /* All in one piece */ |
461 | 0 | file = gp_fopen(ctx->memory, buf, "rb"); |
462 | 0 | if (file) |
463 | 0 | { |
464 | 0 | if (xps_fseek(file, 0, SEEK_END) != 0) { |
465 | 0 | gp_fclose(file); |
466 | 0 | return NULL; |
467 | 0 | } |
468 | | |
469 | 0 | size = xps_ftell(file); |
470 | 0 | if (size < 0) { |
471 | 0 | gp_fclose(file); |
472 | 0 | return NULL; |
473 | 0 | } |
474 | | |
475 | 0 | if (xps_fseek(file, 0, SEEK_SET) != 0) { |
476 | 0 | gp_fclose(file); |
477 | 0 | return NULL; |
478 | 0 | } |
479 | | |
480 | 0 | part = xps_new_part(ctx, name, size); |
481 | 0 | count = xps_fread(part->data, 1, size, file); |
482 | 0 | gp_fclose(file); |
483 | 0 | if (count == size) |
484 | 0 | return part; |
485 | 0 | else |
486 | 0 | return NULL; |
487 | 0 | } |
488 | | |
489 | | /* Count the number of pieces and their total size */ |
490 | 0 | count = 0; |
491 | 0 | size = 0; |
492 | 0 | while (!seen_last) |
493 | 0 | { |
494 | 0 | gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, count); |
495 | 0 | file = gp_fopen(ctx->memory, buf, "rb"); |
496 | 0 | if (!file) |
497 | 0 | { |
498 | 0 | gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, count); |
499 | 0 | file = gp_fopen(ctx->memory, buf, "rb"); |
500 | 0 | seen_last = !!file; |
501 | 0 | } |
502 | 0 | if (!file) |
503 | 0 | break; |
504 | 0 | count ++; |
505 | 0 | if (xps_fseek(file, 0, SEEK_END) < 0) |
506 | 0 | break;; |
507 | 0 | size += xps_ftell(file); |
508 | 0 | gp_fclose(file); |
509 | 0 | } |
510 | 0 | if (!seen_last) |
511 | 0 | { |
512 | 0 | gs_throw1(-1, "cannot find all pieces for part '%s'", name); |
513 | 0 | return NULL; |
514 | 0 | } |
515 | | |
516 | | /* Inflate the pieces */ |
517 | | |
518 | 0 | part = xps_new_part(ctx, name, size); |
519 | 0 | if (!part) |
520 | 0 | { |
521 | 0 | gs_rethrow1(-1, "failed to create part '%s'", name); |
522 | 0 | return NULL; |
523 | 0 | } |
524 | | |
525 | 0 | offset = 0; |
526 | 0 | for (i = 0; i < count; i++) |
527 | 0 | { |
528 | 0 | if (i < count - 1) |
529 | 0 | gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, i); |
530 | 0 | else |
531 | 0 | gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, i); |
532 | 0 | file = gp_fopen(ctx->memory, buf, "rb"); |
533 | 0 | n = xps_fread(part->data + offset, 1, size - offset, file); |
534 | 0 | offset += n; |
535 | 0 | gp_fclose(file); |
536 | 0 | } |
537 | 0 | return part; |
538 | |
|
539 | 0 | } |
540 | | |
541 | | xps_part_t * |
542 | | xps_read_part(xps_context_t *ctx, const char *partname) |
543 | 284 | { |
544 | 284 | if (ctx->directory) |
545 | 0 | return xps_read_dir_part(ctx, partname); |
546 | 284 | return xps_read_zip_part(ctx, partname); |
547 | 284 | } |
548 | | |
549 | | /* |
550 | | * Read and process the XPS document. |
551 | | */ |
552 | | |
553 | | static int |
554 | | xps_read_and_process_metadata_part(xps_context_t *ctx, const char *name) |
555 | 212 | { |
556 | 212 | xps_part_t *part; |
557 | 212 | int code; |
558 | | |
559 | 212 | part = xps_read_part(ctx, name); |
560 | 212 | if (!part) |
561 | 3 | return gs_rethrow1(-1, "cannot read zip part '%s'", name); |
562 | | |
563 | 209 | code = xps_parse_metadata(ctx, part); |
564 | 209 | if (code) |
565 | 1 | { |
566 | 1 | xps_free_part(ctx, part); |
567 | 1 | return gs_rethrow1(code, "cannot process metadata part '%s'", name); |
568 | 1 | } |
569 | | |
570 | 208 | xps_free_part(ctx, part); |
571 | | |
572 | 208 | return gs_okay; |
573 | 209 | } |
574 | | |
575 | | static int |
576 | | xps_read_and_process_page_part(xps_context_t *ctx, char *name) |
577 | 68 | { |
578 | 68 | xps_part_t *part; |
579 | 68 | int code; |
580 | | |
581 | 68 | part = xps_read_part(ctx, name); |
582 | 68 | if (!part) |
583 | 3 | return gs_rethrow1(-1, "cannot read zip part '%s'", name); |
584 | | |
585 | 65 | code = xps_parse_fixed_page(ctx, part); |
586 | 65 | if (code) |
587 | 61 | { |
588 | 61 | xps_free_part(ctx, part); |
589 | 61 | return gs_rethrow1(code, "cannot parse fixed page part '%s'", name); |
590 | 61 | } |
591 | | |
592 | 4 | xps_free_part(ctx, part); |
593 | | |
594 | 4 | return gs_okay; |
595 | 65 | } |
596 | | |
597 | | /* XPS page reordering based upon Device PageList setting */ |
598 | | static int |
599 | | xps_reorder_add_page(xps_context_t* ctx, xps_page_t ***page_ptr, xps_page_t* page_to_add) |
600 | 0 | { |
601 | 0 | xps_page_t* new_page; |
602 | |
|
603 | 0 | new_page = xps_alloc(ctx, sizeof(xps_page_t)); |
604 | 0 | if (!new_page) |
605 | 0 | { |
606 | 0 | return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n"); |
607 | 0 | } |
608 | | |
609 | 0 | new_page->name = xps_strdup(ctx, page_to_add->name); |
610 | 0 | if (!new_page->name) |
611 | 0 | { |
612 | 0 | return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n"); |
613 | 0 | } |
614 | 0 | new_page->height = page_to_add->height; |
615 | 0 | new_page->width = page_to_add->width; |
616 | 0 | new_page->next = NULL; |
617 | |
|
618 | 0 | **page_ptr = new_page; |
619 | 0 | *page_ptr = &(new_page->next); |
620 | |
|
621 | 0 | return 0; |
622 | 0 | } |
623 | | |
624 | | |
625 | | static int |
626 | | xps_reorder_pages(xps_context_t *ctx) |
627 | 0 | { |
628 | 0 | char *page_list = ctx->page_range->page_list; |
629 | 0 | xps_page_t **page_ptr_array, *page = ctx->first_page; |
630 | 0 | int count = 0, k; |
631 | 0 | int code = 0; |
632 | 0 | int start; |
633 | 0 | int end; |
634 | 0 | xps_page_t* first_page = NULL; |
635 | 0 | xps_page_t* last_page; |
636 | 0 | xps_page_t** page_tail = &first_page; |
637 | 0 | int *page_range_array; |
638 | 0 | int ranges_count = 0; |
639 | |
|
640 | 0 | if (page == NULL) |
641 | 0 | return 0; |
642 | | |
643 | 0 | while (page != NULL) |
644 | 0 | { |
645 | 0 | count++; |
646 | 0 | page = page->next; |
647 | 0 | } |
648 | |
|
649 | 0 | if (count > INT_MAX / sizeof(xps_page_t *)) |
650 | 0 | return gs_throw(gs_error_limitcheck, "too many pages\n"); |
651 | | /* Create an array of pointers to the current pages */ |
652 | 0 | page_ptr_array = xps_alloc(ctx, (size_t)sizeof(xps_page_t*) * count); |
653 | 0 | if (page_ptr_array == NULL) |
654 | 0 | return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_pages\n"); |
655 | | |
656 | 0 | page = ctx->first_page; |
657 | 0 | for (k = 0; k < count; k++) |
658 | 0 | { |
659 | 0 | page_ptr_array[k] = page; |
660 | 0 | page = page->next; |
661 | 0 | } |
662 | | |
663 | | /* Use the common function to parse the page_list into a 'page_range_array' */ |
664 | 0 | ranges_count = pagelist_parse_to_array(page_list, ctx->memory, count, &page_range_array); |
665 | 0 | if (ranges_count <= 0) |
666 | 0 | return gs_throw(gs_error_typecheck, "Bad page list: xps_reorder_pages\n"); |
667 | | |
668 | 0 | ranges_count--; /* ignore the final marker range 0, 0, 0 */ |
669 | | |
670 | | /* start processing ranges, ignoring the "ordered" flag at the start of the array */ |
671 | 0 | for (k = 1; k < 1 + 3 * ranges_count; k += 3) { |
672 | 0 | start = page_range_array[k+1]; |
673 | 0 | end = page_range_array[k+2]; |
674 | |
|
675 | 0 | if (start == end) |
676 | 0 | code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]); |
677 | 0 | else if (start < end) { |
678 | 0 | do { |
679 | 0 | code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]); |
680 | 0 | if (code < 0) |
681 | 0 | break; |
682 | 0 | start += ((page_range_array[k] == 0) ? 1 : 2); /* double bump for even/odd */ |
683 | 0 | } while (start <= end); |
684 | 0 | } else { /* start > end -- reverse direction */ |
685 | 0 | do { |
686 | 0 | code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]); |
687 | 0 | if (code < 0) |
688 | 0 | break; |
689 | 0 | start -= ((page_range_array[k] == 0) ? 1 : 2); /* double bump for even/odd */ |
690 | 0 | } while (start >= end); |
691 | 0 | } |
692 | 0 | } |
693 | 0 | pagelist_free_range_array(ctx->memory, page_range_array); /* done with all ranges */ |
694 | | |
695 | | /* Replace the pages. */ |
696 | 0 | if (first_page != NULL) |
697 | 0 | { |
698 | | /* Set to the last page not its next pointer (Thanks to Robin Watts) */ |
699 | 0 | last_page = (xps_page_t*)(((char*)page_tail) - offsetof(xps_page_t, next)); |
700 | |
|
701 | 0 | xps_free_fixed_pages(ctx); |
702 | 0 | xps_free(ctx, page_ptr_array); |
703 | 0 | ctx->first_page = first_page; |
704 | 0 | ctx->last_page = last_page; |
705 | 0 | } |
706 | 0 | else |
707 | 0 | return gs_throw(gs_error_rangecheck, "Bad page list: xps_reorder_pages\n"); |
708 | | |
709 | 0 | return code; |
710 | 0 | } |
711 | | |
712 | | /* |
713 | | * Called by xpstop.c |
714 | | */ |
715 | | |
716 | | int |
717 | | xps_process_file(xps_context_t *ctx, const char *filename) |
718 | 136 | { |
719 | 136 | char buf[2048]; |
720 | 136 | xps_document_t *doc; |
721 | 136 | xps_page_t *page; |
722 | 136 | int code; |
723 | 136 | char *p; |
724 | | |
725 | 136 | ctx->file = xps_fopen(ctx->memory, filename, "rb"); |
726 | 136 | if (!ctx->file) |
727 | 0 | return gs_throw1(-1, "cannot open file: '%s'", filename); |
728 | | |
729 | 136 | if (strstr(filename, ".fpage")) |
730 | 0 | { |
731 | 0 | xps_part_t *part; |
732 | 0 | int size; |
733 | |
|
734 | 0 | if_debug0m('|', ctx->memory, "zip: single page mode\n"); |
735 | 0 | gs_strlcpy(buf, filename, sizeof buf); |
736 | 0 | while (1) |
737 | 0 | { |
738 | 0 | p = strrchr(buf, '/'); |
739 | 0 | if (!p) |
740 | 0 | p = strrchr(buf, '\\'); |
741 | 0 | if (!p) |
742 | 0 | break; |
743 | 0 | gs_strlcpy(p, "/_rels/.rels", buf + sizeof buf - p); |
744 | 0 | if_debug1m('|', ctx->memory, "zip: testing if '%s' exists\n", buf); |
745 | 0 | if (isfile(ctx->memory, buf)) |
746 | 0 | { |
747 | 0 | *p = 0; |
748 | 0 | ctx->directory = xps_strdup(ctx, buf); |
749 | 0 | if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory); |
750 | 0 | break; |
751 | 0 | } |
752 | 0 | *p = 0; |
753 | 0 | } |
754 | 0 | if (!ctx->directory) |
755 | 0 | { |
756 | 0 | if_debug0m('|', ctx->memory, "zip: no /_rels/.rels found; assuming absolute paths\n"); |
757 | 0 | ctx->directory = xps_strdup(ctx, ""); |
758 | 0 | } |
759 | |
|
760 | 0 | if (xps_fseek(ctx->file, 0, SEEK_END) != 0) { |
761 | 0 | code = gs_rethrow(gs_error_ioerror, "xps_fseek to file end failed"); |
762 | 0 | goto cleanup; |
763 | 0 | } |
764 | | |
765 | 0 | size = xps_ftell(ctx->file); |
766 | 0 | if (size < 0) { |
767 | 0 | code = gs_rethrow(gs_error_ioerror, "xps_ftell raised an error"); |
768 | 0 | goto cleanup; |
769 | 0 | } |
770 | | |
771 | 0 | if (xps_fseek(ctx->file, 0, SEEK_SET) != 0) { |
772 | 0 | code = gs_rethrow(gs_error_ioerror, "xps_fseek to file begin failed"); |
773 | 0 | goto cleanup; |
774 | 0 | } |
775 | | |
776 | 0 | part = xps_new_part(ctx, filename, size); |
777 | 0 | code = xps_fread(part->data, 1, size, ctx->file); |
778 | 0 | if (code != size) { |
779 | 0 | code = gs_rethrow1(gs_error_ioerror, "failed to read %d bytes", size); |
780 | 0 | xps_free_part(ctx, part); |
781 | 0 | goto cleanup; |
782 | 0 | } |
783 | | |
784 | 0 | code = xps_parse_fixed_page(ctx, part); |
785 | 0 | if (code) |
786 | 0 | { |
787 | 0 | code = gs_rethrow1(code, "cannot parse fixed page part '%s'", part->name); |
788 | 0 | xps_free_part(ctx, part); |
789 | 0 | goto cleanup; |
790 | 0 | } |
791 | | |
792 | 0 | xps_free_part(ctx, part); |
793 | 0 | code = gs_okay; |
794 | 0 | goto cleanup; |
795 | 0 | } |
796 | | |
797 | 136 | if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) |
798 | 0 | { |
799 | 0 | gs_strlcpy(buf, filename, sizeof buf); |
800 | 0 | p = strstr(buf, "/_rels/.rels"); |
801 | 0 | if (!p) |
802 | 0 | p = strstr(buf, "\\_rels\\.rels"); |
803 | 0 | *p = 0; |
804 | 0 | ctx->directory = xps_strdup(ctx, buf); |
805 | 0 | if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory); |
806 | 0 | } |
807 | 136 | else |
808 | 136 | { |
809 | 136 | code = xps_find_and_read_zip_dir(ctx); |
810 | 136 | if (code < 0) |
811 | 64 | { |
812 | 64 | code = gs_rethrow(code, "cannot read zip central directory"); |
813 | 64 | goto cleanup; |
814 | 64 | } |
815 | 136 | } |
816 | | |
817 | 72 | code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); |
818 | 72 | if (code) |
819 | 1 | { |
820 | 1 | code = gs_rethrow(code, "cannot process root relationship part"); |
821 | 1 | goto cleanup; |
822 | 1 | } |
823 | | |
824 | 71 | if (!ctx->start_part) |
825 | 0 | { |
826 | 0 | code = gs_rethrow(-1, "cannot find fixed document sequence start part"); |
827 | 0 | goto cleanup; |
828 | 0 | } |
829 | | |
830 | 71 | code = xps_read_and_process_metadata_part(ctx, ctx->start_part); |
831 | 71 | if (code) |
832 | 2 | { |
833 | 2 | code = gs_rethrow(code, "cannot process FixedDocumentSequence part"); |
834 | 2 | goto cleanup; |
835 | 2 | } |
836 | | |
837 | 137 | for (doc = ctx->first_fixdoc; doc; doc = doc->next) |
838 | 69 | { |
839 | 69 | code = xps_read_and_process_metadata_part(ctx, doc->name); |
840 | 69 | if (code) |
841 | 1 | { |
842 | 1 | code = gs_rethrow(code, "cannot process FixedDocument part"); |
843 | 1 | goto cleanup; |
844 | 1 | } |
845 | 69 | } |
846 | | |
847 | | /* If we have a page list, adjust pages now */ |
848 | 68 | if (ctx->page_range && ctx->page_range->page_list) |
849 | 0 | { |
850 | 0 | code = xps_reorder_pages(ctx); |
851 | 0 | if (code) |
852 | 0 | { |
853 | 0 | code = gs_rethrow(code, "invalid page range setting"); |
854 | 0 | goto cleanup; |
855 | 0 | } |
856 | 0 | } |
857 | | |
858 | 72 | for (page = ctx->first_page; page; page = page->next) |
859 | 68 | { |
860 | 68 | code = xps_read_and_process_page_part(ctx, page->name); |
861 | 68 | if (code) |
862 | 64 | { |
863 | 64 | code = gs_rethrow(code, "cannot process FixedPage part"); |
864 | 64 | goto cleanup; |
865 | 64 | } |
866 | 68 | } |
867 | | |
868 | 4 | code = gs_okay; |
869 | | |
870 | 136 | cleanup: |
871 | 136 | if (ctx->directory) |
872 | 136 | xps_free(ctx, ctx->directory); |
873 | 136 | if (ctx->file) |
874 | 136 | xps_fclose(ctx->file); |
875 | 136 | return code; |
876 | 4 | } |