1 | #include "mupdf/fitz.h" |
2 | #include "fitz-imp.h" |
3 | |
4 | #include <string.h> |
5 | |
6 | enum |
7 | { |
8 | FZ_DOCUMENT_HANDLER_MAX = 10 |
9 | }; |
10 | |
11 | #define DEFW (450) |
12 | #define DEFH (600) |
13 | #define DEFEM (12) |
14 | |
15 | struct fz_document_handler_context_s |
16 | { |
17 | int refs; |
18 | int count; |
19 | const fz_document_handler *handler[FZ_DOCUMENT_HANDLER_MAX]; |
20 | }; |
21 | |
22 | void fz_new_document_handler_context(fz_context *ctx) |
23 | { |
24 | ctx->handler = fz_malloc_struct(ctx, fz_document_handler_context); |
25 | ctx->handler->refs = 1; |
26 | } |
27 | |
28 | fz_document_handler_context *fz_keep_document_handler_context(fz_context *ctx) |
29 | { |
30 | if (!ctx || !ctx->handler) |
31 | return NULL; |
32 | return fz_keep_imp(ctx, ctx->handler, &ctx->handler->refs); |
33 | } |
34 | |
35 | void fz_drop_document_handler_context(fz_context *ctx) |
36 | { |
37 | if (!ctx) |
38 | return; |
39 | |
40 | if (fz_drop_imp(ctx, ctx->handler, &ctx->handler->refs)) |
41 | { |
42 | fz_free(ctx, ctx->handler); |
43 | ctx->handler = NULL; |
44 | } |
45 | } |
46 | |
47 | /* |
48 | Register a handler |
49 | for a document type. |
50 | |
51 | handler: The handler to register. |
52 | */ |
53 | void fz_register_document_handler(fz_context *ctx, const fz_document_handler *handler) |
54 | { |
55 | fz_document_handler_context *dc; |
56 | int i; |
57 | |
58 | if (!handler) |
59 | return; |
60 | |
61 | dc = ctx->handler; |
62 | if (dc == NULL) |
63 | fz_throw(ctx, FZ_ERROR_GENERIC, "Document handler list not found" ); |
64 | |
65 | for (i = 0; i < dc->count; i++) |
66 | if (dc->handler[i] == handler) |
67 | return; |
68 | |
69 | if (dc->count >= FZ_DOCUMENT_HANDLER_MAX) |
70 | fz_throw(ctx, FZ_ERROR_GENERIC, "Too many document handlers" ); |
71 | |
72 | dc->handler[dc->count++] = handler; |
73 | } |
74 | |
75 | /* |
76 | Given a magic find a document |
77 | handler that can handle a document of this type. |
78 | |
79 | magic: Can be a filename extension (including initial period) or |
80 | a mimetype. |
81 | */ |
82 | const fz_document_handler * |
83 | fz_recognize_document(fz_context *ctx, const char *magic) |
84 | { |
85 | fz_document_handler_context *dc; |
86 | int i, best_score, best_i; |
87 | const char *ext, *needle; |
88 | |
89 | dc = ctx->handler; |
90 | if (dc->count == 0) |
91 | fz_throw(ctx, FZ_ERROR_GENERIC, "No document handlers registered" ); |
92 | |
93 | ext = strrchr(magic, '.'); |
94 | if (ext) |
95 | needle = ext + 1; |
96 | else |
97 | needle = magic; |
98 | |
99 | best_score = 0; |
100 | best_i = -1; |
101 | |
102 | for (i = 0; i < dc->count; i++) |
103 | { |
104 | int score = 0; |
105 | const char **entry; |
106 | |
107 | if (dc->handler[i]->recognize) |
108 | score = dc->handler[i]->recognize(ctx, magic); |
109 | |
110 | if (!ext) |
111 | { |
112 | for (entry = &dc->handler[i]->mimetypes[0]; *entry; entry++) |
113 | if (!fz_strcasecmp(needle, *entry) && score < 100) |
114 | { |
115 | score = 100; |
116 | break; |
117 | } |
118 | } |
119 | |
120 | for (entry = &dc->handler[i]->extensions[0]; *entry; entry++) |
121 | if (!fz_strcasecmp(needle, *entry) && score < 100) |
122 | { |
123 | score = 100; |
124 | break; |
125 | } |
126 | |
127 | if (best_score < score) |
128 | { |
129 | best_score = score; |
130 | best_i = i; |
131 | } |
132 | } |
133 | |
134 | if (best_i < 0) |
135 | return NULL; |
136 | |
137 | return dc->handler[best_i]; |
138 | } |
139 | |
140 | #if FZ_ENABLE_PDF |
141 | extern fz_document_handler pdf_document_handler; |
142 | #endif |
143 | |
144 | /* |
145 | Open a PDF, XPS or CBZ document. |
146 | |
147 | Open a document using the specified stream object rather than |
148 | opening a file on disk. |
149 | |
150 | magic: a string used to detect document type; either a file name or mime-type. |
151 | */ |
152 | fz_document * |
153 | fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream) |
154 | { |
155 | const fz_document_handler *handler; |
156 | |
157 | if (magic == NULL || stream == NULL) |
158 | fz_throw(ctx, FZ_ERROR_GENERIC, "no document to open" ); |
159 | |
160 | handler = fz_recognize_document(ctx, magic); |
161 | if (!handler) |
162 | #if FZ_ENABLE_PDF |
163 | handler = &pdf_document_handler; |
164 | #else |
165 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find document handler for file type: %s" , magic); |
166 | #endif |
167 | |
168 | return handler->open_with_stream(ctx, stream); |
169 | } |
170 | |
171 | /* |
172 | Open a PDF, XPS or CBZ document. |
173 | |
174 | Open a document file and read its basic structure so pages and |
175 | objects can be located. MuPDF will try to repair broken |
176 | documents (without actually changing the file contents). |
177 | |
178 | The returned fz_document is used when calling most other |
179 | document related functions. |
180 | |
181 | filename: a path to a file as it would be given to open(2). |
182 | */ |
183 | fz_document * |
184 | fz_open_document(fz_context *ctx, const char *filename) |
185 | { |
186 | const fz_document_handler *handler; |
187 | fz_stream *file; |
188 | fz_document *doc = NULL; |
189 | |
190 | if (filename == NULL) |
191 | fz_throw(ctx, FZ_ERROR_GENERIC, "no document to open" ); |
192 | |
193 | handler = fz_recognize_document(ctx, filename); |
194 | if (!handler) |
195 | #if FZ_ENABLE_PDF |
196 | handler = &pdf_document_handler; |
197 | #else |
198 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find document handler for file: %s" , filename); |
199 | #endif |
200 | |
201 | if (handler->open) |
202 | return handler->open(ctx, filename); |
203 | |
204 | file = fz_open_file(ctx, filename); |
205 | |
206 | fz_try(ctx) |
207 | doc = handler->open_with_stream(ctx, file); |
208 | fz_always(ctx) |
209 | fz_drop_stream(ctx, file); |
210 | fz_catch(ctx) |
211 | fz_rethrow(ctx); |
212 | |
213 | return doc; |
214 | } |
215 | |
216 | void * |
217 | fz_new_document_of_size(fz_context *ctx, int size) |
218 | { |
219 | fz_document *doc = fz_calloc(ctx, 1, size); |
220 | doc->refs = 1; |
221 | return doc; |
222 | } |
223 | |
224 | fz_document * |
225 | fz_keep_document(fz_context *ctx, fz_document *doc) |
226 | { |
227 | return fz_keep_imp(ctx, doc, &doc->refs); |
228 | } |
229 | |
230 | void |
231 | fz_drop_document(fz_context *ctx, fz_document *doc) |
232 | { |
233 | if (fz_drop_imp(ctx, doc, &doc->refs)) |
234 | { |
235 | if (doc->drop_document) |
236 | doc->drop_document(ctx, doc); |
237 | fz_free(ctx, doc); |
238 | } |
239 | } |
240 | |
241 | static void |
242 | fz_ensure_layout(fz_context *ctx, fz_document *doc) |
243 | { |
244 | if (doc && doc->layout && !doc->did_layout) |
245 | { |
246 | doc->layout(ctx, doc, DEFW, DEFH, DEFEM); |
247 | doc->did_layout = 1; |
248 | } |
249 | } |
250 | |
251 | /* |
252 | Is the document reflowable. |
253 | |
254 | Returns 1 to indicate reflowable documents, otherwise 0. |
255 | */ |
256 | int |
257 | fz_is_document_reflowable(fz_context *ctx, fz_document *doc) |
258 | { |
259 | return doc ? doc->is_reflowable : 0; |
260 | } |
261 | |
262 | /* |
263 | Create a bookmark for the given page, which can be used to find the |
264 | same location after the document has been laid out with different |
265 | parameters. |
266 | */ |
267 | fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, int page) |
268 | { |
269 | if (doc && doc->make_bookmark) |
270 | return doc->make_bookmark(ctx, doc, page); |
271 | return (fz_bookmark)page; |
272 | } |
273 | |
274 | /* |
275 | Find a bookmark and return its page number. |
276 | */ |
277 | int fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark) |
278 | { |
279 | if (doc && doc->lookup_bookmark) |
280 | return doc->lookup_bookmark(ctx, doc, mark); |
281 | return (int)mark; |
282 | } |
283 | |
284 | /* |
285 | Check if a document is encrypted with a |
286 | non-blank password. |
287 | */ |
288 | int |
289 | fz_needs_password(fz_context *ctx, fz_document *doc) |
290 | { |
291 | if (doc && doc->needs_password) |
292 | return doc->needs_password(ctx, doc); |
293 | return 0; |
294 | } |
295 | |
296 | /* |
297 | Test if the given password can |
298 | decrypt the document. |
299 | |
300 | password: The password string to be checked. Some document |
301 | specifications do not specify any particular text encoding, so |
302 | neither do we. |
303 | |
304 | Returns 0 for failure to authenticate, non-zero for success. |
305 | |
306 | For PDF documents, further information can be given by examining |
307 | the bits in the return code. |
308 | |
309 | Bit 0 => No password required |
310 | Bit 1 => User password authenticated |
311 | Bit 2 => Owner password authenticated |
312 | */ |
313 | int |
314 | fz_authenticate_password(fz_context *ctx, fz_document *doc, const char *password) |
315 | { |
316 | if (doc && doc->authenticate_password) |
317 | return doc->authenticate_password(ctx, doc, password); |
318 | return 1; |
319 | } |
320 | |
321 | /* |
322 | Check permission flags on document. |
323 | */ |
324 | int |
325 | fz_has_permission(fz_context *ctx, fz_document *doc, fz_permission p) |
326 | { |
327 | if (doc && doc->has_permission) |
328 | return doc->has_permission(ctx, doc, p); |
329 | return 1; |
330 | } |
331 | |
332 | /* |
333 | Load the hierarchical document outline. |
334 | |
335 | Should be freed by fz_drop_outline. |
336 | */ |
337 | fz_outline * |
338 | fz_load_outline(fz_context *ctx, fz_document *doc) |
339 | { |
340 | fz_ensure_layout(ctx, doc); |
341 | if (doc && doc->load_outline) |
342 | return doc->load_outline(ctx, doc); |
343 | return NULL; |
344 | } |
345 | |
346 | /* |
347 | Resolve an internal link to a page number. |
348 | |
349 | xp, yp: Pointer to store coordinate of destination on the page. |
350 | |
351 | Returns -1 if the URI cannot be resolved. |
352 | */ |
353 | int |
354 | fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp) |
355 | { |
356 | fz_ensure_layout(ctx, doc); |
357 | if (xp) *xp = 0; |
358 | if (yp) *yp = 0; |
359 | if (doc && doc->resolve_link) |
360 | return doc->resolve_link(ctx, doc, uri, xp, yp); |
361 | return -1; |
362 | } |
363 | |
364 | /* |
365 | Layout reflowable document types. |
366 | |
367 | w, h: Page size in points. |
368 | em: Default font size in points. |
369 | */ |
370 | void |
371 | fz_layout_document(fz_context *ctx, fz_document *doc, float w, float h, float em) |
372 | { |
373 | if (doc && doc->layout) |
374 | { |
375 | doc->layout(ctx, doc, w, h, em); |
376 | doc->did_layout = 1; |
377 | } |
378 | } |
379 | |
380 | /* |
381 | Return the number of pages in document |
382 | |
383 | May return 0 for documents with no pages. |
384 | */ |
385 | int |
386 | fz_count_pages(fz_context *ctx, fz_document *doc) |
387 | { |
388 | fz_ensure_layout(ctx, doc); |
389 | if (doc && doc->count_pages) |
390 | return doc->count_pages(ctx, doc); |
391 | return 0; |
392 | } |
393 | |
394 | /* |
395 | Retrieve document meta data strings. |
396 | |
397 | doc: The document to query. |
398 | |
399 | key: Which meta data key to retrieve... |
400 | |
401 | Basic information: |
402 | 'format' -- Document format and version. |
403 | 'encryption' -- Description of the encryption used. |
404 | |
405 | From the document information dictionary: |
406 | 'info:Title' |
407 | 'info:Author' |
408 | 'info:Subject' |
409 | 'info:Keywords' |
410 | 'info:Creator' |
411 | 'info:Producer' |
412 | 'info:CreationDate' |
413 | 'info:ModDate' |
414 | |
415 | buf: The buffer to hold the results (a nul-terminated UTF-8 string). |
416 | |
417 | size: Size of 'buf'. |
418 | |
419 | Returns the size of the output string (may be larger than 'size' if |
420 | the output was truncated), or -1 if the key is not recognized or found. |
421 | */ |
422 | int |
423 | fz_lookup_metadata(fz_context *ctx, fz_document *doc, const char *key, char *buf, int size) |
424 | { |
425 | if (buf && size > 0) |
426 | buf[0] = 0; |
427 | if (doc && doc->lookup_metadata) |
428 | return doc->lookup_metadata(ctx, doc, key, buf, size); |
429 | return -1; |
430 | } |
431 | |
432 | /* |
433 | Find the output intent colorspace if the document has defined one. |
434 | */ |
435 | fz_colorspace * |
436 | fz_document_output_intent(fz_context *ctx, fz_document *doc) |
437 | { |
438 | if (doc && doc->get_output_intent) |
439 | return doc->get_output_intent(ctx, doc); |
440 | return NULL; |
441 | } |
442 | |
443 | /* |
444 | Load a page. |
445 | |
446 | After fz_load_page is it possible to retrieve the size of the |
447 | page using fz_bound_page, or to render the page using |
448 | fz_run_page_*. Free the page by calling fz_drop_page. |
449 | |
450 | number: page number, 0 is the first page of the document. |
451 | */ |
452 | fz_page * |
453 | fz_load_page(fz_context *ctx, fz_document *doc, int number) |
454 | { |
455 | fz_page *page; |
456 | |
457 | fz_ensure_layout(ctx, doc); |
458 | |
459 | for (page = doc->open; page; page = page->next) |
460 | if (page->number == number) |
461 | return fz_keep_page(ctx, page); |
462 | |
463 | if (doc && doc->load_page) |
464 | { |
465 | page = doc->load_page(ctx, doc, number); |
466 | page->number = number; |
467 | |
468 | /* Insert new page at the head of the list of open pages. */ |
469 | if (!page->incomplete) |
470 | { |
471 | if ((page->next = doc->open) != NULL) |
472 | doc->open->prev = &page->next; |
473 | doc->open = page; |
474 | page->prev = &doc->open; |
475 | } |
476 | return page; |
477 | } |
478 | |
479 | return NULL; |
480 | } |
481 | |
482 | /* |
483 | Load the list of links for a page. |
484 | |
485 | Returns a linked list of all the links on the page, each with |
486 | its clickable region and link destination. Each link is |
487 | reference counted so drop and free the list of links by |
488 | calling fz_drop_link on the pointer return from fz_load_links. |
489 | |
490 | page: Page obtained from fz_load_page. |
491 | */ |
492 | fz_link * |
493 | fz_load_links(fz_context *ctx, fz_page *page) |
494 | { |
495 | if (page && page->load_links) |
496 | return page->load_links(ctx, page); |
497 | return NULL; |
498 | } |
499 | |
500 | /* |
501 | Determine the size of a page at 72 dpi. |
502 | */ |
503 | fz_rect |
504 | fz_bound_page(fz_context *ctx, fz_page *page) |
505 | { |
506 | if (page && page->bound_page) |
507 | return page->bound_page(ctx, page); |
508 | return fz_empty_rect; |
509 | } |
510 | |
511 | /* |
512 | Run a page through a device. Just the main |
513 | page content, without the annotations, if any. |
514 | |
515 | page: Page obtained from fz_load_page. |
516 | |
517 | dev: Device obtained from fz_new_*_device. |
518 | |
519 | transform: Transform to apply to page. May include for example |
520 | scaling and rotation, see fz_scale, fz_rotate and fz_concat. |
521 | Set to fz_identity if no transformation is desired. |
522 | |
523 | cookie: Communication mechanism between caller and library |
524 | rendering the page. Intended for multi-threaded applications, |
525 | while single-threaded applications set cookie to NULL. The |
526 | caller may abort an ongoing rendering of a page. Cookie also |
527 | communicates progress information back to the caller. The |
528 | fields inside cookie are continually updated while the page is |
529 | rendering. |
530 | */ |
531 | void |
532 | fz_run_page_contents(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
533 | { |
534 | if (page && page->run_page_contents) |
535 | { |
536 | fz_try(ctx) |
537 | { |
538 | page->run_page_contents(ctx, page, dev, transform, cookie); |
539 | } |
540 | fz_catch(ctx) |
541 | { |
542 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
543 | if (fz_caught(ctx) != FZ_ERROR_ABORT) |
544 | fz_rethrow(ctx); |
545 | } |
546 | } |
547 | } |
548 | |
549 | /* |
550 | Run the annotations on a page through a device. |
551 | */ |
552 | void |
553 | fz_run_page_annots(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
554 | { |
555 | if (page && page->run_page_annots) |
556 | { |
557 | fz_try(ctx) |
558 | { |
559 | page->run_page_annots(ctx, page, dev, transform, cookie); |
560 | } |
561 | fz_catch(ctx) |
562 | { |
563 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
564 | if (fz_caught(ctx) != FZ_ERROR_ABORT) |
565 | fz_rethrow(ctx); |
566 | } |
567 | } |
568 | } |
569 | |
570 | /* |
571 | Run the widgets on a page through a device. |
572 | */ |
573 | void |
574 | fz_run_page_widgets(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
575 | { |
576 | if (page && page->run_page_widgets) |
577 | { |
578 | fz_try(ctx) |
579 | { |
580 | page->run_page_widgets(ctx, page, dev, transform, cookie); |
581 | } |
582 | fz_catch(ctx) |
583 | { |
584 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
585 | if (fz_caught(ctx) != FZ_ERROR_ABORT) |
586 | fz_rethrow(ctx); |
587 | } |
588 | } |
589 | } |
590 | |
591 | /* |
592 | Run a page through a device. |
593 | |
594 | page: Page obtained from fz_load_page. |
595 | |
596 | dev: Device obtained from fz_new_*_device. |
597 | |
598 | transform: Transform to apply to page. May include for example |
599 | scaling and rotation, see fz_scale, fz_rotate and fz_concat. |
600 | Set to fz_identity if no transformation is desired. |
601 | |
602 | cookie: Communication mechanism between caller and library |
603 | rendering the page. Intended for multi-threaded applications, |
604 | while single-threaded applications set cookie to NULL. The |
605 | caller may abort an ongoing rendering of a page. Cookie also |
606 | communicates progress information back to the caller. The |
607 | fields inside cookie are continually updated while the page is |
608 | rendering. |
609 | */ |
610 | void |
611 | fz_run_page(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
612 | { |
613 | fz_run_page_contents(ctx, page, dev, transform, cookie); |
614 | fz_run_page_annots(ctx, page, dev, transform, cookie); |
615 | fz_run_page_widgets(ctx, page, dev, transform, cookie); |
616 | } |
617 | |
618 | fz_page * |
619 | fz_new_page_of_size(fz_context *ctx, int size) |
620 | { |
621 | fz_page *page = Memento_label(fz_calloc(ctx, 1, size), "fz_page" ); |
622 | page->refs = 1; |
623 | return page; |
624 | } |
625 | |
626 | fz_page * |
627 | fz_keep_page(fz_context *ctx, fz_page *page) |
628 | { |
629 | return fz_keep_imp(ctx, page, &page->refs); |
630 | } |
631 | |
632 | void |
633 | fz_drop_page(fz_context *ctx, fz_page *page) |
634 | { |
635 | if (fz_drop_imp(ctx, page, &page->refs)) |
636 | { |
637 | /* Remove page from the list of open pages */ |
638 | if (page->next != NULL) |
639 | page->next->prev = page->prev; |
640 | if (page->prev != NULL) |
641 | *page->prev = page->next; |
642 | |
643 | if (page->drop_page) |
644 | page->drop_page(ctx, page); |
645 | |
646 | fz_free(ctx, page); |
647 | } |
648 | } |
649 | |
650 | /* |
651 | Get the presentation details for a given page. |
652 | |
653 | transition: A pointer to a transition struct to fill out. |
654 | |
655 | duration: A pointer to a place to set the page duration in seconds. |
656 | Will be set to 0 if no transition is specified for the page. |
657 | |
658 | Returns: a pointer to the transition structure, or NULL if there is no |
659 | transition specified for the page. |
660 | */ |
661 | fz_transition * |
662 | fz_page_presentation(fz_context *ctx, fz_page *page, fz_transition *transition, float *duration) |
663 | { |
664 | float dummy; |
665 | if (duration) |
666 | *duration = 0; |
667 | else |
668 | duration = &dummy; |
669 | if (page && page->page_presentation && page) |
670 | return page->page_presentation(ctx, page, transition, duration); |
671 | return NULL; |
672 | } |
673 | |
674 | /* |
675 | Get the separations details for a page. |
676 | This will be NULL, unless the format specifically supports |
677 | separations (such as PDF files). May be NULL even |
678 | so, if there are no separations on a page. |
679 | |
680 | Returns a reference that must be dropped. |
681 | */ |
682 | fz_separations * |
683 | fz_page_separations(fz_context *ctx, fz_page *page) |
684 | { |
685 | if (page && page->separations) |
686 | return page->separations(ctx, page); |
687 | return NULL; |
688 | } |
689 | |
690 | int fz_page_uses_overprint(fz_context *ctx, fz_page *page) |
691 | { |
692 | if (page && page->overprint) |
693 | return page->overprint(ctx, page); |
694 | return 0; |
695 | } |
696 | |