Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 1 | #include "fitz.h" |
2 | #include "muxps.h" |
||
3 | |||
4 | #include |
||
5 | |||
6 | xps_part * |
||
7 | xps_new_part(xps_context *ctx, char *name, int size) |
||
8 | { |
||
9 | xps_part *part; |
||
10 | |||
11 | part = fz_malloc(sizeof(xps_part)); |
||
12 | part->name = fz_strdup(name); |
||
13 | part->size = size; |
||
14 | part->data = fz_malloc(size + 1); |
||
15 | part->data[size] = 0; /* null-terminate for xml parser */ |
||
16 | |||
17 | return part; |
||
18 | } |
||
19 | |||
20 | void |
||
21 | xps_free_part(xps_context *ctx, xps_part *part) |
||
22 | { |
||
23 | fz_free(part->name); |
||
24 | fz_free(part->data); |
||
25 | fz_free(part); |
||
26 | } |
||
27 | |||
28 | static inline int getshort(fz_stream *file) |
||
29 | { |
||
30 | int a = fz_read_byte(file); |
||
31 | int b = fz_read_byte(file); |
||
32 | return a | b << 8; |
||
33 | } |
||
34 | |||
35 | static inline int getlong(fz_stream *file) |
||
36 | { |
||
37 | int a = fz_read_byte(file); |
||
38 | int b = fz_read_byte(file); |
||
39 | int c = fz_read_byte(file); |
||
40 | int d = fz_read_byte(file); |
||
41 | return a | b << 8 | c << 16 | d << 24; |
||
42 | } |
||
43 | |||
44 | static void * |
||
45 | xps_zip_alloc_items(xps_context *ctx, int items, int size) |
||
46 | { |
||
47 | return fz_calloc(items, size); |
||
48 | } |
||
49 | |||
50 | static void |
||
51 | xps_zip_free(xps_context *ctx, void *ptr) |
||
52 | { |
||
53 | fz_free(ptr); |
||
54 | } |
||
55 | |||
56 | static int |
||
57 | xps_compare_entries(const void *a0, const void *b0) |
||
58 | { |
||
59 | xps_entry *a = (xps_entry*) a0; |
||
60 | xps_entry *b = (xps_entry*) b0; |
||
61 | return xps_strcasecmp(a->name, b->name); |
||
62 | } |
||
63 | |||
64 | static xps_entry * |
||
65 | xps_find_zip_entry(xps_context *ctx, char *name) |
||
66 | { |
||
67 | int l = 0; |
||
68 | int r = ctx->zip_count - 1; |
||
69 | while (l <= r) |
||
70 | { |
||
71 | int m = (l + r) >> 1; |
||
72 | int c = xps_strcasecmp(name, ctx->zip_table[m].name); |
||
73 | if (c < 0) |
||
74 | r = m - 1; |
||
75 | else if (c > 0) |
||
76 | l = m + 1; |
||
77 | else |
||
78 | return &ctx->zip_table[m]; |
||
79 | } |
||
80 | return NULL; |
||
81 | } |
||
82 | |||
83 | static int |
||
84 | xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) |
||
85 | { |
||
86 | z_stream stream; |
||
87 | unsigned char *inbuf; |
||
88 | int sig; |
||
89 | int version, general, method; |
||
90 | int namelength, extralength; |
||
91 | int code; |
||
92 | |||
93 | fz_seek(ctx->file, ent->offset, 0); |
||
94 | |||
95 | sig = getlong(ctx->file); |
||
96 | if (sig != ZIP_LOCAL_FILE_SIG) |
||
97 | return fz_throw("wrong zip local file signature (0x%x)", sig); |
||
98 | |||
99 | version = getshort(ctx->file); |
||
100 | general = getshort(ctx->file); |
||
101 | method = getshort(ctx->file); |
||
102 | (void) getshort(ctx->file); /* file time */ |
||
103 | (void) getshort(ctx->file); /* file date */ |
||
104 | (void) getlong(ctx->file); /* crc-32 */ |
||
105 | (void) getlong(ctx->file); /* csize */ |
||
106 | (void) getlong(ctx->file); /* usize */ |
||
107 | namelength = getshort(ctx->file); |
||
108 | extralength = getshort(ctx->file); |
||
109 | |||
110 | fz_seek(ctx->file, namelength + extralength, 1); |
||
111 | |||
112 | if (method == 0) |
||
113 | { |
||
114 | fz_read(ctx->file, outbuf, ent->usize); |
||
115 | } |
||
116 | else if (method == 8) |
||
117 | { |
||
118 | inbuf = fz_malloc(ent->csize); |
||
119 | |||
120 | fz_read(ctx->file, inbuf, ent->csize); |
||
121 | |||
122 | memset(&stream, 0, sizeof(z_stream)); |
||
123 | stream.zalloc = (alloc_func) xps_zip_alloc_items; |
||
124 | stream.zfree = (free_func) xps_zip_free; |
||
125 | stream.opaque = ctx; |
||
126 | stream.next_in = inbuf; |
||
127 | stream.avail_in = ent->csize; |
||
128 | stream.next_out = outbuf; |
||
129 | stream.avail_out = ent->usize; |
||
130 | |||
131 | code = inflateInit2(&stream, -15); |
||
132 | if (code != Z_OK) |
||
133 | return fz_throw("zlib inflateInit2 error: %s", stream.msg); |
||
134 | code = inflate(&stream, Z_FINISH); |
||
135 | if (code != Z_STREAM_END) |
||
136 | { |
||
137 | inflateEnd(&stream); |
||
138 | return fz_throw("zlib inflate error: %s", stream.msg); |
||
139 | } |
||
140 | code = inflateEnd(&stream); |
||
141 | if (code != Z_OK) |
||
142 | return fz_throw("zlib inflateEnd error: %s", stream.msg); |
||
143 | |||
144 | fz_free(inbuf); |
||
145 | } |
||
146 | else |
||
147 | { |
||
148 | return fz_throw("unknown compression method (%d)", method); |
||
149 | } |
||
150 | |||
151 | return fz_okay; |
||
152 | } |
||
153 | |||
154 | /* |
||
155 | * Read the central directory in a zip file. |
||
156 | */ |
||
157 | |||
158 | static int |
||
159 | xps_read_zip_dir(xps_context *ctx, int start_offset) |
||
160 | { |
||
161 | int sig; |
||
162 | int offset, count; |
||
163 | int namesize, metasize, commentsize; |
||
164 | int i; |
||
165 | |||
166 | fz_seek(ctx->file, start_offset, 0); |
||
167 | |||
168 | sig = getlong(ctx->file); |
||
169 | if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) |
||
170 | return fz_throw("wrong zip end of central directory signature (0x%x)", sig); |
||
171 | |||
172 | (void) getshort(ctx->file); /* this disk */ |
||
173 | (void) getshort(ctx->file); /* start disk */ |
||
174 | (void) getshort(ctx->file); /* entries in this disk */ |
||
175 | count = getshort(ctx->file); /* entries in central directory disk */ |
||
176 | (void) getlong(ctx->file); /* size of central directory */ |
||
177 | offset = getlong(ctx->file); /* offset to central directory */ |
||
178 | |||
179 | ctx->zip_count = count; |
||
180 | ctx->zip_table = fz_calloc(count, sizeof(xps_entry)); |
||
181 | memset(ctx->zip_table, 0, sizeof(xps_entry) * count); |
||
182 | |||
183 | fz_seek(ctx->file, offset, 0); |
||
184 | |||
185 | for (i = 0; i < count; i++) |
||
186 | { |
||
187 | sig = getlong(ctx->file); |
||
188 | if (sig != ZIP_CENTRAL_DIRECTORY_SIG) |
||
189 | return fz_throw("wrong zip central directory signature (0x%x)", sig); |
||
190 | |||
191 | (void) getshort(ctx->file); /* version made by */ |
||
192 | (void) getshort(ctx->file); /* version to extract */ |
||
193 | (void) getshort(ctx->file); /* general */ |
||
194 | (void) getshort(ctx->file); /* method */ |
||
195 | (void) getshort(ctx->file); /* last mod file time */ |
||
196 | (void) getshort(ctx->file); /* last mod file date */ |
||
197 | (void) getlong(ctx->file); /* crc-32 */ |
||
198 | ctx->zip_table[i].csize = getlong(ctx->file); |
||
199 | ctx->zip_table[i].usize = getlong(ctx->file); |
||
200 | namesize = getshort(ctx->file); |
||
201 | metasize = getshort(ctx->file); |
||
202 | commentsize = getshort(ctx->file); |
||
203 | (void) getshort(ctx->file); /* disk number start */ |
||
204 | (void) getshort(ctx->file); /* int file atts */ |
||
205 | (void) getlong(ctx->file); /* ext file atts */ |
||
206 | ctx->zip_table[i].offset = getlong(ctx->file); |
||
207 | |||
208 | ctx->zip_table[i].name = fz_malloc(namesize + 1); |
||
209 | fz_read(ctx->file, (unsigned char*)ctx->zip_table[i].name, namesize); |
||
210 | ctx->zip_table[i].name[namesize] = 0; |
||
211 | |||
212 | fz_seek(ctx->file, metasize, 1); |
||
213 | fz_seek(ctx->file, commentsize, 1); |
||
214 | } |
||
215 | |||
216 | qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries); |
||
217 | |||
218 | return fz_okay; |
||
219 | } |
||
220 | |||
221 | static int |
||
222 | xps_find_and_read_zip_dir(xps_context *ctx) |
||
223 | { |
||
224 | unsigned char buf[512]; |
||
225 | int file_size, back, maxback; |
||
226 | int i, n; |
||
227 | |||
228 | fz_seek(ctx->file, 0, SEEK_END); |
||
229 | file_size = fz_tell(ctx->file); |
||
230 | |||
231 | maxback = MIN(file_size, 0xFFFF + sizeof buf); |
||
232 | back = MIN(maxback, sizeof buf); |
||
233 | |||
234 | while (back < maxback) |
||
235 | { |
||
236 | fz_seek(ctx->file, file_size - back, 0); |
||
237 | |||
238 | n = fz_read(ctx->file, buf, sizeof buf); |
||
239 | if (n < 0) |
||
240 | return fz_throw("cannot read end of central directory"); |
||
241 | |||
242 | for (i = n - 4; i > 0; i--) |
||
243 | if (!memcmp(buf + i, "PK\5\6", 4)) |
||
244 | return xps_read_zip_dir(ctx, file_size - back + i); |
||
245 | |||
246 | back += sizeof buf - 4; |
||
247 | } |
||
248 | |||
249 | return fz_throw("cannot find end of central directory"); |
||
250 | } |
||
251 | |||
252 | /* |
||
253 | * Read and interleave split parts from a ZIP file. |
||
254 | */ |
||
255 | static xps_part * |
||
256 | xps_read_zip_part(xps_context *ctx, char *partname) |
||
257 | { |
||
258 | char buf[2048]; |
||
259 | xps_entry *ent; |
||
260 | xps_part *part; |
||
261 | int count, size, offset, i; |
||
262 | char *name; |
||
263 | |||
264 | name = partname; |
||
265 | if (name[0] == '/') |
||
266 | name ++; |
||
267 | |||
268 | /* All in one piece */ |
||
269 | ent = xps_find_zip_entry(ctx, name); |
||
270 | if (ent) |
||
271 | { |
||
272 | part = xps_new_part(ctx, partname, ent->usize); |
||
273 | xps_read_zip_entry(ctx, ent, part->data); |
||
274 | return part; |
||
275 | } |
||
276 | |||
277 | /* Count the number of pieces and their total size */ |
||
278 | count = 0; |
||
279 | size = 0; |
||
280 | while (1) |
||
281 | { |
||
282 | sprintf(buf, "%s/[%d].piece", name, count); |
||
283 | ent = xps_find_zip_entry(ctx, buf); |
||
284 | if (!ent) |
||
285 | { |
||
286 | sprintf(buf, "%s/[%d].last.piece", name, count); |
||
287 | ent = xps_find_zip_entry(ctx, buf); |
||
288 | } |
||
289 | if (!ent) |
||
290 | break; |
||
291 | count ++; |
||
292 | size += ent->usize; |
||
293 | } |
||
294 | |||
295 | /* Inflate the pieces */ |
||
296 | if (count) |
||
297 | { |
||
298 | part = xps_new_part(ctx, partname, size); |
||
299 | offset = 0; |
||
300 | for (i = 0; i < count; i++) |
||
301 | { |
||
302 | if (i < count - 1) |
||
303 | sprintf(buf, "%s/[%d].piece", name, i); |
||
304 | else |
||
305 | sprintf(buf, "%s/[%d].last.piece", name, i); |
||
306 | ent = xps_find_zip_entry(ctx, buf); |
||
307 | xps_read_zip_entry(ctx, ent, part->data + offset); |
||
308 | offset += ent->usize; |
||
309 | } |
||
310 | return part; |
||
311 | } |
||
312 | |||
313 | return NULL; |
||
314 | } |
||
315 | |||
316 | /* |
||
317 | * Read and interleave split parts from files in the directory. |
||
318 | */ |
||
319 | static xps_part * |
||
320 | xps_read_dir_part(xps_context *ctx, char *name) |
||
321 | { |
||
322 | char buf[2048]; |
||
323 | xps_part *part; |
||
324 | FILE *file; |
||
325 | int count, size, offset, i, n; |
||
326 | |||
327 | fz_strlcpy(buf, ctx->directory, sizeof buf); |
||
328 | fz_strlcat(buf, name, sizeof buf); |
||
329 | |||
330 | /* All in one piece */ |
||
331 | file = fopen(buf, "rb"); |
||
332 | if (file) |
||
333 | { |
||
334 | fseek(file, 0, SEEK_END); |
||
335 | size = ftell(file); |
||
336 | fseek(file, 0, SEEK_SET); |
||
337 | part = xps_new_part(ctx, name, size); |
||
338 | fread(part->data, 1, size, file); |
||
339 | fclose(file); |
||
340 | return part; |
||
341 | } |
||
342 | |||
343 | /* Count the number of pieces and their total size */ |
||
344 | count = 0; |
||
345 | size = 0; |
||
346 | while (1) |
||
347 | { |
||
348 | sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count); |
||
349 | file = fopen(buf, "rb"); |
||
350 | if (!file) |
||
351 | { |
||
352 | sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count); |
||
353 | file = fopen(buf, "rb"); |
||
354 | } |
||
355 | if (!file) |
||
356 | break; |
||
357 | count ++; |
||
358 | fseek(file, 0, SEEK_END); |
||
359 | size += ftell(file); |
||
360 | fclose(file); |
||
361 | } |
||
362 | |||
363 | /* Inflate the pieces */ |
||
364 | if (count) |
||
365 | { |
||
366 | part = xps_new_part(ctx, name, size); |
||
367 | offset = 0; |
||
368 | for (i = 0; i < count; i++) |
||
369 | { |
||
370 | if (i < count - 1) |
||
371 | sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i); |
||
372 | else |
||
373 | sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i); |
||
374 | file = fopen(buf, "rb"); |
||
375 | n = fread(part->data + offset, 1, size - offset, file); |
||
376 | offset += n; |
||
377 | fclose(file); |
||
378 | } |
||
379 | return part; |
||
380 | } |
||
381 | |||
382 | return NULL; |
||
383 | } |
||
384 | |||
385 | xps_part * |
||
386 | xps_read_part(xps_context *ctx, char *partname) |
||
387 | { |
||
388 | if (ctx->directory) |
||
389 | return xps_read_dir_part(ctx, partname); |
||
390 | return xps_read_zip_part(ctx, partname); |
||
391 | } |
||
392 | |||
393 | static int |
||
394 | xps_open_directory(xps_context **ctxp, char *directory) |
||
395 | { |
||
396 | xps_context *ctx; |
||
397 | int code; |
||
398 | |||
399 | ctx = fz_malloc(sizeof(xps_context)); |
||
400 | memset(ctx, 0, sizeof(xps_context)); |
||
401 | |||
402 | ctx->directory = fz_strdup(directory); |
||
403 | |||
404 | code = xps_read_page_list(ctx); |
||
405 | if (code) |
||
406 | { |
||
407 | xps_free_context(ctx); |
||
408 | return fz_rethrow(code, "cannot read page list"); |
||
409 | } |
||
410 | |||
411 | *ctxp = ctx; |
||
412 | return fz_okay; |
||
413 | } |
||
414 | |||
415 | int |
||
416 | xps_open_stream(xps_context **ctxp, fz_stream *file) |
||
417 | { |
||
418 | xps_context *ctx; |
||
419 | int code; |
||
420 | |||
421 | ctx = fz_malloc(sizeof(xps_context)); |
||
422 | memset(ctx, 0, sizeof(xps_context)); |
||
423 | |||
424 | ctx->file = fz_keep_stream(file); |
||
425 | |||
426 | code = xps_find_and_read_zip_dir(ctx); |
||
427 | if (code < 0) |
||
428 | { |
||
429 | xps_free_context(ctx); |
||
430 | return fz_rethrow(code, "cannot read zip central directory"); |
||
431 | } |
||
432 | |||
433 | code = xps_read_page_list(ctx); |
||
434 | if (code) |
||
435 | { |
||
436 | xps_free_context(ctx); |
||
437 | return fz_rethrow(code, "cannot read page list"); |
||
438 | } |
||
439 | |||
440 | *ctxp = ctx; |
||
441 | return fz_okay; |
||
442 | } |
||
443 | |||
444 | int |
||
445 | xps_open_file(xps_context **ctxp, char *filename) |
||
446 | { |
||
447 | char buf[2048]; |
||
448 | fz_stream *file; |
||
449 | char *p; |
||
450 | int code; |
||
451 | |||
452 | if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) |
||
453 | { |
||
454 | fz_strlcpy(buf, filename, sizeof buf); |
||
455 | p = strstr(buf, "/_rels/.rels"); |
||
456 | if (!p) |
||
457 | p = strstr(buf, "\\_rels\\.rels"); |
||
458 | *p = 0; |
||
459 | return xps_open_directory(ctxp, buf); |
||
460 | } |
||
461 | |||
462 | file = fz_open_file(filename); |
||
463 | if (!file) |
||
464 | return fz_throw("cannot open file '%s': %s", filename, strerror(errno)); |
||
465 | |||
466 | code = xps_open_stream(ctxp, file); |
||
467 | fz_close(file); |
||
468 | if (code) |
||
469 | return fz_rethrow(code, "cannot load document '%s'", filename); |
||
470 | return fz_okay; |
||
471 | } |
||
472 | |||
473 | void |
||
474 | xps_free_context(xps_context *ctx) |
||
475 | { |
||
476 | xps_font_cache *font, *next; |
||
477 | int i; |
||
478 | |||
479 | if (ctx->file) |
||
480 | fz_close(ctx->file); |
||
481 | |||
482 | for (i = 0; i < ctx->zip_count; i++) |
||
483 | fz_free(ctx->zip_table[i].name); |
||
484 | fz_free(ctx->zip_table); |
||
485 | |||
486 | font = ctx->font_table; |
||
487 | while (font) |
||
488 | { |
||
489 | next = font->next; |
||
490 | fz_drop_font(font->font); |
||
491 | fz_free(font->name); |
||
492 | fz_free(font); |
||
493 | font = next; |
||
494 | } |
||
495 | |||
496 | xps_free_page_list(ctx); |
||
497 | |||
498 | fz_free(ctx->start_part); |
||
499 | fz_free(ctx->directory); |
||
500 | fz_free(ctx); |
||
501 | }>>>>>>>>>>=>><>><>><>><> |