1#include "mupdf/fitz.h"
2#include "fitz-imp.h"
3
4#include <string.h>
5
6enum
7{
8 FZ_DOCUMENT_HANDLER_MAX = 10
9};
10
11#define DEFW (450)
12#define DEFH (600)
13#define DEFEM (12)
14
15struct fz_document_handler_context_s
16{
17 int refs;
18 int count;
19 const fz_document_handler *handler[FZ_DOCUMENT_HANDLER_MAX];
20};
21
22void 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
28fz_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
35void 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*/
53void 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*/
82const fz_document_handler *
83fz_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
141extern 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*/
152fz_document *
153fz_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*/
183fz_document *
184fz_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
216void *
217fz_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
224fz_document *
225fz_keep_document(fz_context *ctx, fz_document *doc)
226{
227 return fz_keep_imp(ctx, doc, &doc->refs);
228}
229
230void
231fz_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
241static void
242fz_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*/
256int
257fz_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*/
267fz_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*/
277int 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*/
288int
289fz_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*/
313int
314fz_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*/
324int
325fz_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*/
337fz_outline *
338fz_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*/
353int
354fz_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*/
370void
371fz_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*/
385int
386fz_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*/
422int
423fz_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*/
435fz_colorspace *
436fz_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*/
452fz_page *
453fz_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*/
492fz_link *
493fz_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*/
503fz_rect
504fz_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*/
531void
532fz_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*/
552void
553fz_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*/
573void
574fz_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*/
610void
611fz_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
618fz_page *
619fz_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
626fz_page *
627fz_keep_page(fz_context *ctx, fz_page *page)
628{
629 return fz_keep_imp(ctx, page, &page->refs);
630}
631
632void
633fz_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*/
661fz_transition *
662fz_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*/
682fz_separations *
683fz_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
690int 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