diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/fitz/error.c | 7 | ||||
-rw-r--r-- | source/fitz/filter-dct.c | 2 | ||||
-rw-r--r-- | source/fitz/image.c | 2 | ||||
-rw-r--r-- | source/fitz/stream-prog.c | 181 | ||||
-rw-r--r-- | source/fitz/stream-read.c | 16 | ||||
-rw-r--r-- | source/pdf/js/pdf-js.c | 4 | ||||
-rw-r--r-- | source/pdf/pdf-annot.c | 11 | ||||
-rw-r--r-- | source/pdf/pdf-font.c | 82 | ||||
-rw-r--r-- | source/pdf/pdf-interpret.c | 60 | ||||
-rw-r--r-- | source/pdf/pdf-object.c | 2 | ||||
-rw-r--r-- | source/pdf/pdf-page.c | 37 | ||||
-rw-r--r-- | source/pdf/pdf-repair.c | 59 | ||||
-rw-r--r-- | source/pdf/pdf-stream.c | 2 | ||||
-rw-r--r-- | source/pdf/pdf-type3.c | 2 | ||||
-rw-r--r-- | source/pdf/pdf-write.c | 6 | ||||
-rw-r--r-- | source/pdf/pdf-xref.c | 707 | ||||
-rw-r--r-- | source/xps/xps-doc.c | 4 | ||||
-rw-r--r-- | source/xps/xps-glyphs.c | 4 | ||||
-rw-r--r-- | source/xps/xps-image.c | 4 | ||||
-rw-r--r-- | source/xps/xps-outline.c | 2 | ||||
-rw-r--r-- | source/xps/xps-resource.c | 2 |
21 files changed, 1116 insertions, 80 deletions
diff --git a/source/fitz/error.c b/source/fitz/error.c index fb0dc77e..0da7d29e 100644 --- a/source/fitz/error.c +++ b/source/fitz/error.c @@ -180,3 +180,10 @@ void fz_rethrow_message(fz_context *ctx, const char *fmt, ...) throw(ctx->error); } + +void fz_rethrow_if(fz_context *ctx, int err) +{ + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + if (ctx->error->errcode == err) + fz_rethrow(ctx); +} diff --git a/source/fitz/filter-dct.c b/source/fitz/filter-dct.c index 1a55e584..189d2aae 100644 --- a/source/fitz/filter-dct.c +++ b/source/fitz/filter-dct.c @@ -53,7 +53,7 @@ static boolean fill_input_buffer(j_decompress_ptr cinfo) } fz_catch(ctx) { - /* FIXME: TRYLATER */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); return 0; } src->next_input_byte = chain->rp; diff --git a/source/fitz/image.c b/source/fitz/image.c index 62561762..0aeb449d 100644 --- a/source/fitz/image.c +++ b/source/fitz/image.c @@ -157,7 +157,7 @@ fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, in } fz_catch(ctx) { - /* FIXME: TryLater? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring error at end of image"); } } diff --git a/source/fitz/stream-prog.c b/source/fitz/stream-prog.c new file mode 100644 index 00000000..e35a1ac5 --- /dev/null +++ b/source/fitz/stream-prog.c @@ -0,0 +1,181 @@ +#include "mupdf/fitz/stream.h" +#include "mupdf/fitz/string.h" + +#if defined(_WIN32) && !defined(NDEBUG) +#include "windows.h" + +static void +show_progress(int av, int pos) +{ + char text[80]; + sprintf(text, "Have %d, Want %d\n", av, pos); + OutputDebugStringA(text); +} +#else +#define show_progress(A,B) do {} while (0) +#endif + +/* File stream - progressive reading to simulate http download */ + +typedef struct prog_state +{ + int fd; + int length; + int available; + int bps; + clock_t start_time; +} prog_state; + +static int read_prog(fz_stream *stm, unsigned char *buf, int len) +{ + prog_state *ps = (prog_state *)stm->state; + int n; + + /* Simulate more data having arrived */ + if (ps->available < ps->length) + { + int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8)); + if (av > ps->length) + av = ps->length; + ps->available = av; + /* Limit any fetches to be within the data we have */ + if (av < ps->length && len + stm->pos > av) + { + len = av - stm->pos; + if (len <= 0) + { + show_progress(av, stm->pos); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data yet"); + } + } + } + + n = (len > 0 ? read(ps->fd, buf, len) : 0); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); + return n; +} + +static void seek_prog(fz_stream *stm, int offset, int whence) +{ + prog_state *ps = (prog_state *)stm->state; + int n; + + /* Simulate more data having arrived */ + if (ps->available < ps->length) + { + int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8)); + if (av > ps->length) + av = ps->length; + ps->available = av; + } + if (ps->available < ps->length) + { + if (whence == SEEK_END) + { + show_progress(ps->available, ps->length); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to end yet"); + } + } + if (whence == SEEK_CUR) + { + whence = SEEK_SET; + offset += stm->pos; + if (offset > ps->available) + { + show_progress(ps->available, offset); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek (relatively) to offset yet"); + } + } + if (whence == SEEK_SET) + { + if (offset > ps->available) + { + show_progress(ps->available, offset); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to offset yet"); + } + } + + n = lseek(ps->fd, offset, whence); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "cannot lseek: %s", strerror(errno)); + stm->pos = n; + stm->rp = stm->bp; + stm->wp = stm->bp; +} + +static void close_prog(fz_context *ctx, void *state) +{ + prog_state *ps = (prog_state *)state; + int n = close(ps->fd); + if (n < 0) + fz_warn(ctx, "close error: %s", strerror(errno)); + fz_free(ctx, state); +} + +static int meta_prog(fz_stream *stm, int key, int size, void *ptr) +{ + prog_state *ps = (prog_state *)stm->state; + switch(key) + { + case FZ_STREAM_META_PROGRESSIVE: + return 1; + break; + case FZ_STREAM_META_LENGTH: + return ps->length; + } + return -1; +} + +fz_stream * +fz_open_fd_progressive(fz_context *ctx, int fd, int bps) +{ + fz_stream *stm; + prog_state *state; + + state = fz_malloc_struct(ctx, prog_state); + state->fd = fd; + state->bps = bps; + state->start_time = clock(); + state->available = 0; + + state->length = lseek(state->fd, 0, SEEK_END); + lseek(state->fd, 0, SEEK_SET); + + fz_try(ctx) + { + stm = fz_new_stream(ctx, state, read_prog, close_prog); + } + fz_catch(ctx) + { + fz_free(ctx, state); + fz_rethrow(ctx); + } + stm->seek = seek_prog; + stm->meta = meta_prog; + + return stm; +} + +fz_stream * +fz_open_file_progressive(fz_context *ctx, const char *name, int bps) +{ +#ifdef _WIN32 + char *s = (char*)name; + wchar_t *wname, *d; + int c, fd; + d = wname = fz_malloc(ctx, (strlen(name)+1) * sizeof(wchar_t)); + while (*s) { + s += fz_chartorune(&c, s); + *d++ = c; + } + *d = 0; + fd = _wopen(wname, O_BINARY | O_RDONLY, 0); + fz_free(ctx, wname); +#else + int fd = open(name, O_BINARY | O_RDONLY, 0); +#endif + if (fd == -1) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name); + return fz_open_fd_progressive(ctx, fd, bps); +} diff --git a/source/fitz/stream-read.c b/source/fitz/stream-read.c index ee3d1cad..d433ce32 100644 --- a/source/fitz/stream-read.c +++ b/source/fitz/stream-read.c @@ -84,7 +84,7 @@ fz_fill_buffer(fz_stream *stm) } fz_catch(stm->ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(stm->ctx, FZ_ERROR_TRYLATER); fz_warn(stm->ctx, "read error; treating as end of file"); stm->error = 1; } @@ -134,7 +134,11 @@ fz_read_best(fz_stream *stm, int initial, int *truncated) } fz_catch(ctx) { - /* FIXME: TryLater */ + if (fz_caught(ctx) == FZ_ERROR_TRYLATER) + { + fz_drop_buffer(ctx, buf); + fz_rethrow(ctx); + } if (truncated) { *truncated = 1; @@ -184,6 +188,7 @@ fz_tell(fz_stream *stm) void fz_seek(fz_stream *stm, int offset, int whence) { + stm->avail = 0; /* Reset bit reading */ if (stm->seek) { if (whence == 1) @@ -217,3 +222,10 @@ fz_seek(fz_stream *stm, int offset, int whence) else fz_warn(stm->ctx, "cannot seek"); } + +int fz_stream_meta(fz_stream *stm, int key, int size, void *ptr) +{ + if (!stm || !stm->meta) + return -1; + return stm->meta(stm, key, size, ptr); +} diff --git a/source/pdf/js/pdf-js.c b/source/pdf/js/pdf-js.c index 26c6d6f8..d801716a 100644 --- a/source/pdf/js/pdf-js.c +++ b/source/pdf/js/pdf-js.c @@ -492,7 +492,7 @@ static pdf_jsimp_obj *doc_getField(void *jsctx, void *obj, int argc, pdf_jsimp_o } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "doc_getField failed: %s", fz_caught_message(ctx)); dict = NULL; } @@ -832,7 +832,7 @@ void pdf_js_load_document_level(pdf_js *js) } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "Warning: %s", fz_caught_message(ctx)); } } diff --git a/source/pdf/pdf-annot.c b/source/pdf/pdf-annot.c index c571a0a1..4c76fce9 100644 --- a/source/pdf/pdf-annot.c +++ b/source/pdf/pdf-annot.c @@ -331,6 +331,7 @@ pdf_load_link_annots(pdf_document *doc, pdf_obj *annots, const fz_matrix *page_c n = pdf_array_len(annots); for (i = 0; i < n; i++) { + /* FIXME: Move the try/catch out of the loop for performance? */ fz_try(doc->ctx) { obj = pdf_array_get(annots, i); @@ -338,7 +339,7 @@ pdf_load_link_annots(pdf_document *doc, pdf_obj *annots, const fz_matrix *page_c } fz_catch(doc->ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); link = NULL; } @@ -594,9 +595,13 @@ pdf_load_annots(pdf_document *doc, pdf_obj *annots, pdf_page *page) } fz_catch(ctx) { + if (fz_caught(ctx) == FZ_ERROR_TRYLATER) + { + pdf_free_annot(ctx, head); + fz_rethrow(ctx); + } keep_annot = 0; fz_warn(ctx, "ignoring broken annotation"); - /* FIXME: TryLater */ } if (!keep_annot) { @@ -657,8 +662,8 @@ pdf_update_annot(pdf_document *doc, pdf_annot *annot) } fz_catch(ctx) { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring broken annotation"); - /* FIXME: TryLater */ } } } diff --git a/source/pdf/pdf-font.c b/source/pdf/pdf-font.c index 2c18d96a..a5174979 100644 --- a/source/pdf/pdf-font.c +++ b/source/pdf/pdf-font.c @@ -397,7 +397,7 @@ pdf_new_font_desc(fz_context *ctx) */ static pdf_font_desc * -pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) +pdf_load_simple_font_by_name(pdf_document *doc, pdf_obj *dict, char *basefont) { pdf_obj *descriptor; pdf_obj *encoding; @@ -410,7 +410,6 @@ pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) int symbolic; int kind; - char *basefont; char *estrings[256]; char ebuffer[256][32]; int i, k, n; @@ -422,8 +421,6 @@ pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) fz_var(etable); fz_var(has_lock); - basefont = pdf_to_name(pdf_dict_gets(dict, "BaseFont")); - /* Load font file */ fz_try(ctx) { @@ -688,7 +685,7 @@ pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "cannot load ToUnicode CMap"); } @@ -744,6 +741,79 @@ pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) return fontdesc; } +static pdf_font_desc * +pdf_load_simple_font(pdf_document *doc, pdf_obj *dict) +{ + char *basefont = pdf_to_name(pdf_dict_gets(dict, "BaseFont")); + + return pdf_load_simple_font_by_name(doc, dict, basefont); +} + +static int +hail_mary_make_hash_key(fz_store_hash *hash, void *key_) +{ + hash->u.i.i0 = 0; + hash->u.i.i1 = 0; + return 1; +} + +static void * +hail_mary_keep_key(fz_context *ctx, void *key) +{ + return key; +} + +static void +hail_mary_drop_key(fz_context *ctx, void *key) +{ +} + +static int +hail_mary_cmp_key(void *k0, void *k1) +{ + return k0 == k1; +} + +#ifndef NDEBUG +static void +hail_mary_debug_key(FILE *out, void *key_) +{ + fprintf(out, "hail mary "); +} +#endif + +static fz_store_type hail_mary_store_type = +{ + hail_mary_make_hash_key, + hail_mary_keep_key, + hail_mary_drop_key, + hail_mary_cmp_key, +#ifndef NDEBUG + hail_mary_debug_key +#endif +}; + +pdf_font_desc * +pdf_load_hail_mary_font(pdf_document *doc) +{ + fz_context *ctx = doc->ctx; + pdf_font_desc *fontdesc; + pdf_font_desc *existing; + + if ((fontdesc = fz_find_item(ctx, pdf_free_font_imp, &hail_mary_store_type, &hail_mary_store_type))) + { + return fontdesc; + } + + /* FIXME: Get someone with a clue about fonts to fix this */ + fontdesc = pdf_load_simple_font_by_name(doc, NULL, "Helvetica"); + + existing = fz_store_item(ctx, &hail_mary_store_type, fontdesc, fontdesc->size, &hail_mary_store_type); + assert(existing == NULL); + + return fontdesc; +} + /* * CID Fonts */ @@ -1057,7 +1127,7 @@ pdf_load_font_descriptor(pdf_font_desc *fontdesc, pdf_document *doc, pdf_obj *di } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignored error when loading embedded font; attempting to load system font"); if (origname != fontname && !iscidfont) pdf_load_builtin_font(ctx, fontdesc, fontname); diff --git a/source/pdf/pdf-interpret.c b/source/pdf/pdf-interpret.c index cec0d1e5..515a87fa 100644 --- a/source/pdf/pdf-interpret.c +++ b/source/pdf/pdf-interpret.c @@ -372,7 +372,7 @@ begin_softmask(pdf_csi * csi, softmask_save *save) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* FIXME: Ignore error - nasty, but if we throw from * here the clip stack would be messed up. */ if (csi->cookie) @@ -1188,7 +1188,7 @@ pdf_grestore(pdf_csi *csi) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* Silently swallow the problem */ } clip_depth--; @@ -1590,6 +1590,31 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma } +static pdf_font_desc * +load_font_or_hail_mary(pdf_csi *csi, pdf_obj *rdb, pdf_obj *font, int depth) +{ + pdf_document *doc = csi->doc; + fz_context *ctx = doc->ctx; + pdf_font_desc *desc; + + fz_try(ctx) + { + desc = pdf_load_font(doc, rdb, font, depth); + } + fz_catch(ctx) + { + if (fz_caught(ctx) != FZ_ERROR_TRYLATER) + fz_rethrow(ctx); + if (!csi->cookie || !csi->cookie->incomplete_ok) + fz_rethrow(ctx); + desc = NULL; + csi->cookie->incomplete++; + } + if (desc == NULL) + desc = pdf_load_hail_mary_font(doc); + return desc; +} + static void pdf_run_extgstate(pdf_csi *csi, pdf_obj *rdb, pdf_obj *extgstate) { @@ -1619,7 +1644,7 @@ pdf_run_extgstate(pdf_csi *csi, pdf_obj *rdb, pdf_obj *extgstate) gstate->font = NULL; } - gstate->font = pdf_load_font(csi->doc, rdb, font, csi->nested_depth); + gstate->font = load_font_or_hail_mary(csi, rdb, font, csi->nested_depth); if (!gstate->font) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find font in store"); gstate->size = pdf_to_real(pdf_array_get(val, 1)); @@ -2191,7 +2216,7 @@ static void pdf_run_Tf(pdf_csi *csi, pdf_obj *rdb) if (!obj) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find font resource: '%s'", csi->name); - gstate->font = pdf_load_font(csi->doc, rdb, obj, csi->nested_depth); + gstate->font = load_font_or_hail_mary(csi, rdb, obj, csi->nested_depth); } static void pdf_run_Tr(pdf_csi *csi) @@ -2855,10 +2880,21 @@ pdf_run_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file, pdf_lexbuf *buf) } fz_catch(ctx) { - /* FIXME: TryLater */ - /* Swallow the error */ - if (csi->cookie) - csi->cookie->errors++; + if (!csi->cookie) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + } + else if (fz_caught(ctx) == FZ_ERROR_TRYLATER) + { + if (csi->cookie->incomplete_ok) + csi->cookie->incomplete++; + else + fz_rethrow(ctx); + } + else + { + csi->cookie->errors++; + } if (!ignoring_errors) { fz_warn(ctx, "Ignoring errors during rendering"); @@ -2901,7 +2937,7 @@ pdf_run_contents_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "Content stream parsing error - rendering truncated"); } while (csi->gtop > csi->gbot) @@ -3000,6 +3036,8 @@ static void pdf_run_page_contents_with_usage(pdf_document *doc, pdf_page *page, void pdf_run_page_contents(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie) { pdf_run_page_contents_with_usage(doc, page, dev, ctm, "View", cookie); + if (page->incomplete & PDF_PAGE_INCOMPLETE_CONTENTS) + fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering"); } static void pdf_run_annot_with_usage(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie) @@ -3047,6 +3085,8 @@ static void pdf_run_annot_with_usage(pdf_document *doc, pdf_page *page, pdf_anno void pdf_run_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie) { pdf_run_annot_with_usage(doc, page, annot, dev, ctm, "View", cookie); + if (page->incomplete & PDF_PAGE_INCOMPLETE_ANNOTS) + fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering"); } static void pdf_run_page_annots_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie) @@ -3080,6 +3120,8 @@ pdf_run_page_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const { pdf_run_page_contents_with_usage(doc, page, dev, ctm, event, cookie); pdf_run_page_annots_with_usage(doc, page, dev, ctm, event, cookie); + if (page->incomplete) + fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering"); } void diff --git a/source/pdf/pdf-object.c b/source/pdf/pdf-object.c index 6a77e7b4..708794ee 100644 --- a/source/pdf/pdf-object.c +++ b/source/pdf/pdf-object.c @@ -1392,7 +1392,7 @@ pdf_obj *pdf_new_obj_from_str(pdf_document *doc, const char *src) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); return NULL; } diff --git a/source/pdf/pdf-page.c b/source/pdf/pdf-page.c index 7de81f00..d4d8015d 100644 --- a/source/pdf/pdf-page.c +++ b/source/pdf/pdf-page.c @@ -349,7 +349,14 @@ pdf_load_page(pdf_document *doc, int number) float userunit; fz_matrix mat; - pageref = pdf_lookup_page_obj(doc, number); + if (doc->file_reading_linearly) + { + pageref = pdf_progressive_advance(doc, number); + if (pageref == NULL) + fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "page %d not available yet", number); + } + else + pageref = pdf_lookup_page_obj(doc, number); pageobj = pdf_resolve_indirect(pageref); page = fz_malloc_struct(ctx, pdf_page); @@ -361,6 +368,7 @@ pdf_load_page(pdf_document *doc, int number) page->deleted_annots = NULL; page->tmp_annots = NULL; page->me = pdf_keep_obj(pageobj); + page->incomplete = 0; obj = pdf_dict_gets(pageobj, "UserUnit"); if (pdf_is_real(obj)) @@ -409,11 +417,22 @@ pdf_load_page(pdf_document *doc, int number) fz_pre_scale(fz_translate(&mat, -realbox.x0, -realbox.y0), userunit, userunit); fz_concat(&page->ctm, &page->ctm, &mat); - obj = pdf_dict_gets(pageobj, "Annots"); - if (obj) + fz_try(ctx) { - page->links = pdf_load_link_annots(doc, obj, &page->ctm); - page->annots = pdf_load_annots(doc, obj, page); + obj = pdf_dict_gets(pageobj, "Annots"); + if (obj) + { + page->links = pdf_load_link_annots(doc, obj, &page->ctm); + page->annots = pdf_load_annots(doc, obj, page); + } + } + fz_catch(ctx) + { + if (fz_caught(ctx) != FZ_ERROR_TRYLATER) + fz_rethrow(ctx); + page->incomplete |= PDF_PAGE_INCOMPLETE_ANNOTS; + fz_drop_link(ctx, page->links); + page->links = NULL; } page->duration = pdf_to_real(pdf_dict_gets(pageobj, "Dur")); @@ -444,8 +463,12 @@ pdf_load_page(pdf_document *doc, int number) } fz_catch(ctx) { - pdf_free_page(doc, page); - fz_rethrow_message(ctx, "cannot load page %d contents (%d 0 R)", number + 1, pdf_to_num(pageref)); + if (fz_caught(ctx) != FZ_ERROR_TRYLATER) + { + pdf_free_page(doc, page); + fz_rethrow_message(ctx, "cannot load page %d contents (%d 0 R)", number + 1, pdf_to_num(pageref)); + } + page->incomplete |= PDF_PAGE_INCOMPLETE_CONTENTS; } return page; diff --git a/source/pdf/pdf-repair.c b/source/pdf/pdf-repair.c index 3db32de0..fc603d1d 100644 --- a/source/pdf/pdf-repair.c +++ b/source/pdf/pdf-repair.c @@ -14,8 +14,8 @@ struct entry int stm_len; }; -static int -pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, pdf_obj **encrypt, pdf_obj **id, int *tmpofs) +int +pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, pdf_obj **encrypt, pdf_obj **id, pdf_obj **page, int *tmpofs) { pdf_token tok; int stm_len; @@ -24,7 +24,8 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p fz_context *ctx = file->ctx; *stmofsp = 0; - *stmlenp = -1; + if (stmlenp) + *stmlenp = -1; stm_len = 0; @@ -45,7 +46,7 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* Don't let a broken object at EOF overwrite a good one */ if (file->eof) fz_rethrow_message(ctx, "broken object at EOF ignored"); @@ -53,21 +54,24 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p dict = pdf_new_dict(doc, 2); } - obj = pdf_dict_gets(dict, "Type"); - if (pdf_is_name(obj) && !strcmp(pdf_to_name(obj), "XRef")) + if (encrypt && id) { - obj = pdf_dict_gets(dict, "Encrypt"); - if (obj) + obj = pdf_dict_gets(dict, "Type"); + if (pdf_is_name(obj) && !strcmp(pdf_to_name(obj), "XRef")) { - pdf_drop_obj(*encrypt); - *encrypt = pdf_keep_obj(obj); - } + obj = pdf_dict_gets(dict, "Encrypt"); + if (obj) + { + pdf_drop_obj(*encrypt); + *encrypt = pdf_keep_obj(obj); + } - obj = pdf_dict_gets(dict, "ID"); - if (obj) - { - pdf_drop_obj(*id); - *id = pdf_keep_obj(obj); + obj = pdf_dict_gets(dict, "ID"); + if (obj) + { + pdf_drop_obj(*id); + *id = pdf_keep_obj(obj); + } } } @@ -75,6 +79,16 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p if (!pdf_is_indirect(obj) && pdf_is_int(obj)) stm_len = pdf_to_int(obj); + if (doc->file_reading_linearly && page) + { + obj = pdf_dict_gets(dict, "Type"); + if (!strcmp(pdf_to_name(obj), "Page")) + { + pdf_drop_obj(*page); + *page = pdf_keep_obj(dict); + } + } + pdf_drop_obj(dict); } @@ -112,7 +126,7 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "cannot find endstream token, falling back to scanning"); } if (tok == PDF_TOK_ENDSTREAM) @@ -133,7 +147,8 @@ pdf_repair_obj(pdf_document *doc, pdf_lexbuf *buf, int *stmofsp, int *stmlenp, p buf->scratch[8] = c; } - *stmlenp = fz_tell(file) - *stmofsp - 9; + if (stmlenp) + *stmlenp = fz_tell(file) - *stmofsp - 9; atobjend: *tmpofs = fz_tell(file); @@ -302,7 +317,7 @@ pdf_repair_xref(pdf_document *doc, pdf_lexbuf *buf) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring the rest of the file"); break; } @@ -324,11 +339,11 @@ pdf_repair_xref(pdf_document *doc, pdf_lexbuf *buf) { fz_try(ctx) { - tok = pdf_repair_obj(doc, buf, &stm_ofs, &stm_len, &encrypt, &id, &tmpofs); + tok = pdf_repair_obj(doc, buf, &stm_ofs, &stm_len, &encrypt, &id, NULL, &tmpofs); } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* If we haven't seen a root yet, there is nothing * we can do, but give up. Otherwise, we'll make * do. */ @@ -374,7 +389,7 @@ pdf_repair_xref(pdf_document *doc, pdf_lexbuf *buf) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* If we haven't seen a root yet, there is nothing * we can do, but give up. Otherwise, we'll make * do. */ diff --git a/source/pdf/pdf-stream.c b/source/pdf/pdf-stream.c index 9d25915b..a46cdcc7 100644 --- a/source/pdf/pdf-stream.c +++ b/source/pdf/pdf-stream.c @@ -536,7 +536,7 @@ pdf_open_object_array(pdf_document *doc, pdf_obj *list) } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "cannot load content stream part %d/%d", i + 1, n); continue; } diff --git a/source/pdf/pdf-type3.c b/source/pdf/pdf-type3.c index d4c9b31a..b216b950 100644 --- a/source/pdf/pdf-type3.c +++ b/source/pdf/pdf-type3.c @@ -198,7 +198,7 @@ void pdf_load_type3_glyphs(pdf_document *doc, pdf_font_desc *fontdesc, int neste } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "Type3 glyph load failed: %s", fz_caught_message(ctx)); } } diff --git a/source/pdf/pdf-write.c b/source/pdf/pdf-write.c index a1b0caa2..a161c763 100644 --- a/source/pdf/pdf-write.c +++ b/source/pdf/pdf-write.c @@ -532,7 +532,7 @@ static pdf_obj *sweepref(pdf_document *doc, pdf_write_options *opts, pdf_obj *ob } fz_catch(ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); /* Leave broken */ } @@ -1650,7 +1650,7 @@ static void writeobject(pdf_document *doc, pdf_write_options *opts, int num, int } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); if (opts->continue_on_error) { fprintf(opts->out, "%d %d obj\nnull\nendobj\n", num, gen); @@ -1732,7 +1732,7 @@ static void writeobject(pdf_document *doc, pdf_write_options *opts, int num, int } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); if (opts->continue_on_error) { fprintf(opts->out, "%d %d obj\nnull\nendobj\n", num, gen); diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index a50cafff..33bd6c22 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -1,5 +1,13 @@ #include "mupdf/pdf.h" +#undef DEBUG_PROGESSIVE_ADVANCE + +#ifdef DEBUG_PROGESSIVE_ADVANCE +#define DEBUGMESS(A) do { fz_warn A; } while (0) +#else +#define DEBUGMESS(A) do { } while (0) +#endif + static inline int iswhite(int ch) { return @@ -699,6 +707,7 @@ read_xref_section(pdf_document *doc, int ofs, pdf_lexbuf *buf, ofs_list *offsets pdf_set_populating_xref_trailer(doc, trailer); /* FIXME: do we overwrite free entries properly? */ + /* FIXME: Does this work properly with progression? */ xrefstmofs = pdf_to_int(pdf_dict_gets(trailer, "XRefStm")); if (xrefstmofs) { @@ -720,7 +729,6 @@ read_xref_section(pdf_document *doc, int ofs, pdf_lexbuf *buf, ofs_list *offsets fz_always(ctx) { pdf_drop_obj(trailer); - trailer = NULL; } fz_catch(ctx) { @@ -731,7 +739,7 @@ read_xref_section(pdf_document *doc, int ofs, pdf_lexbuf *buf, ofs_list *offsets } static void -pdf_read_xref_sections(pdf_document *doc, int ofs, pdf_lexbuf *buf) +pdf_read_xref_sections(pdf_document *doc, int ofs, pdf_lexbuf *buf, int read_previous) { fz_context *ctx = doc->ctx; ofs_list list; @@ -745,6 +753,8 @@ pdf_read_xref_sections(pdf_document *doc, int ofs, pdf_lexbuf *buf) { pdf_populate_next_xref_level(doc); ofs = read_xref_section(doc, ofs, buf, &list); + if (!read_previous) + break; } } fz_always(ctx) @@ -770,11 +780,9 @@ pdf_load_xref(pdf_document *doc, pdf_lexbuf *buf) int xref_len; fz_context *ctx = doc->ctx; - pdf_load_version(doc); - pdf_read_start_xref(doc); - pdf_read_xref_sections(doc, doc->startxref, buf); + pdf_read_xref_sections(doc, doc->startxref, buf, 1); if (pdf_xref_len(doc) == 0) fz_throw(ctx, FZ_ERROR_GENERIC, "found xref was empty"); @@ -803,6 +811,61 @@ pdf_load_xref(pdf_document *doc, pdf_lexbuf *buf) } } +static void +pdf_load_linear(pdf_document *doc) +{ + pdf_obj *dict = NULL; + pdf_obj *hint = NULL; + pdf_obj *o; + int num, gen, stmofs, lin, len; + fz_context *ctx = doc->ctx; + + fz_var(dict); + fz_var(hint); + + fz_try(ctx) + { + pdf_xref_entry *entry; + + dict = pdf_parse_ind_obj(doc, doc->file, &doc->lexbuf.base, &num, &gen, &stmofs); + if (!pdf_is_dict(dict)) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read linearized dictionary"); + o = pdf_dict_gets(dict, "Linearized"); + if (o == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read linearized dictionary"); + lin = pdf_to_int(o); + if (lin != 1) + fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected version of Linearized tag (%d)", lin); + len = pdf_to_int(pdf_dict_gets(dict, "L")); + if (len != doc->file_length) + fz_throw(ctx, FZ_ERROR_GENERIC, "File has been updated since linearization"); + + pdf_read_xref_sections(doc, fz_tell(doc->file), &doc->lexbuf.base, 0); + + doc->page_count = pdf_to_int(pdf_dict_gets(dict, "N")); + doc->linear_page_refs = fz_resize_array(ctx, doc->linear_page_refs, doc->page_count, sizeof(pdf_obj *)); + memset(doc->linear_page_refs, 0, doc->page_count * sizeof(pdf_obj*)); + doc->linear_obj = dict; + doc->linear_pos = fz_tell(doc->file); + doc->linear_page1_obj_num = pdf_to_int(pdf_dict_gets(dict, "O")); + doc->linear_page_refs[0] = pdf_new_indirect(doc, doc->linear_page1_obj_num, 0); + doc->linear_page_num = 0; + hint = pdf_dict_gets(dict, "H"); + doc->hint_object_offset = pdf_to_int(pdf_array_get(hint, 0)); + doc->hint_object_length = pdf_to_int(pdf_array_get(hint, 1)); + + entry = pdf_get_populating_xref_entry(doc, 0); + entry->type = 'f'; + } + fz_catch(ctx) + { + pdf_drop_obj(dict); + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + /* Drop back to non linearized reading mode */ + doc->file_reading_linearly = 0; + } +} + void pdf_ocg_set_config(pdf_document *doc, int config) { @@ -982,12 +1045,31 @@ pdf_init_document(pdf_document *doc) fz_try(ctx) { - pdf_load_xref(doc, &doc->lexbuf.base); + pdf_load_version(doc); + + doc->file_length = fz_stream_meta(doc->file, FZ_STREAM_META_LENGTH, 0, NULL); + if (doc->file_length < 0) + doc->file_length = 0; + + /* Check to see if we should work in progressive mode */ + if (fz_stream_meta(doc->file, FZ_STREAM_META_PROGRESSIVE, 0, NULL) > 0) + doc->file_reading_linearly = 1; + + /* Try to load the linearized file if we are in progressive + * mode. */ + if (doc->file_reading_linearly) + pdf_load_linear(doc); + + /* If we aren't in progressive mode (or the linear load failed + * and has set us back to non-progressive mode), load normally. + */ + if (!doc->file_reading_linearly) + pdf_load_xref(doc, &doc->lexbuf.base); } fz_catch(ctx) { - /* FIXME: TryLater ? */ pdf_free_xref_sections(doc); + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "trying to repair broken xref"); repaired = 1; } @@ -1027,7 +1109,7 @@ pdf_init_document(pdf_document *doc) } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring broken object (%d 0 R)", i); continue; } @@ -1066,7 +1148,6 @@ pdf_init_document(pdf_document *doc) { pdf_drop_obj(dict); pdf_drop_obj(nobj); - pdf_close_document(doc); fz_rethrow_message(ctx, "cannot open document"); } @@ -1076,7 +1157,6 @@ pdf_init_document(pdf_document *doc) } fz_catch(ctx) { - /* FIXME: TryLater ? */ fz_warn(ctx, "Ignoring Broken Optional Content"); } } @@ -1107,6 +1187,20 @@ pdf_close_document(pdf_document *doc) if (doc->crypt) pdf_free_crypt(ctx, doc->crypt); + pdf_drop_obj(doc->linear_obj); + if (doc->linear_page_refs) + { + for (i=0; i < doc->page_count; i++) + { + pdf_drop_obj(doc->linear_page_refs[i]); + } + fz_free(ctx, doc->linear_page_refs); + } + fz_free(ctx, doc->hint_page); + fz_free(ctx, doc->hint_shared_ref); + fz_free(ctx, doc->hint_shared); + fz_free(ctx, doc->hint_obj_offsets); + for (i=0; i < doc->num_type3_fonts; i++) { fz_decouple_type3_font(ctx, doc->type3_fonts[i], (void *)doc); @@ -1249,6 +1343,239 @@ pdf_load_obj_stm(pdf_document *doc, int num, int gen, pdf_lexbuf *buf) /* * object loading */ +static int +pdf_obj_read(pdf_document *doc, int *offset, int *nump, pdf_obj **page) +{ + int num, numofs, gen, genofs, stmofs, tmpofs, tok; + pdf_lexbuf *buf = &doc->lexbuf.base; + fz_context *ctx = doc->ctx; + int xref_len; + pdf_xref_entry *entry; + int newtmpofs; + + numofs = *offset; + fz_seek(doc->file, numofs, SEEK_SET); + + /* We expect to read 'num' here */ + tok = pdf_lex(doc->file, buf); + genofs = fz_tell(doc->file); + if (tok != PDF_TOK_INT) + { + /* Failed! */ + DEBUGMESS((ctx, "skipping unexpected data (tok=%d) at %d", tok, *offset)); + *offset = genofs; + return tok == PDF_TOK_EOF; + } + *nump = num = buf->i; + + /* We expect to read 'gen' here */ + tok = pdf_lex(doc->file, buf); + tmpofs = fz_tell(doc->file); + if (tok != PDF_TOK_INT) + { + /* Failed! */ + DEBUGMESS((ctx, "skipping unexpected data after \"%d\" (tok=%d) at %d", num, tok, *offset)); + *offset = tmpofs; + return tok == PDF_TOK_EOF; + } + gen = buf->i; + + /* We expect to read 'obj' here */ + do + { + tmpofs = fz_tell(doc->file); + tok = pdf_lex(doc->file, buf); + if (tok == PDF_TOK_OBJ) + break; + if (tok != PDF_TOK_INT) + { + DEBUGMESS((ctx, "skipping unexpected data (tok=%d) at %d", tok, tmpofs)); + *offset = fz_tell(doc->file); + return tok == PDF_TOK_EOF; + } + DEBUGMESS((ctx, "skipping unexpected int %d at %d", num, numofs)); + *nump = num = gen; + numofs = genofs; + gen = buf->i; + genofs = tmpofs; + } + while (1); + + /* Now we read the actual object */ + xref_len = pdf_xref_len(doc); + + /* When we are reading a progressive file, we typically see: + * File Header + * obj m (Linearization params) + * xref #1 (refers to objects m-n) + * obj m+1 + * ... + * obj n + * obj 1 + * ... + * obj n-1 + * xref #2 + * + * The linearisation params are read elsewhere, hence + * whenever we read an object it should just go into the + * previous xref. + */ + tok = pdf_repair_obj(doc, buf, &stmofs, NULL, NULL, NULL, page, &newtmpofs); + + do /* So we can break out of it */ + { + if (num <= 0 || num >= xref_len) + { + fz_warn(ctx, "Not a valid object number (%d %d obj)", num, gen); + break; + } + if (gen != 0) + { + fz_warn(ctx, "Unexpected non zero generation number in linearized file"); + } + entry = pdf_get_populating_xref_entry(doc, num); + if (entry->type != 0) + { + DEBUGMESS((ctx, "Duplicate object found (%d %d obj)", num, gen)); + break; + } + if (page && *page) + { + DEBUGMESS((ctx, "Successfully read object %d @ %d - and found page %d!", num, numofs, doc->linear_page_num)); + if (!entry->obj) + entry->obj = pdf_keep_obj(*page); + + if (doc->linear_page_refs[doc->linear_page_num] == NULL) + doc->linear_page_refs[doc->linear_page_num] = pdf_new_indirect(doc, num, gen); + } + else + { + DEBUGMESS((ctx, "Successfully read object %d @ %d", num, numofs)); + } + entry->type = 'n'; + entry->gen = 0; + entry->ofs = numofs; + entry->stm_ofs = stmofs; + } + while (0); + if (page && *page) + doc->linear_page_num++; + + if (tok == PDF_TOK_ENDOBJ) + { + *offset = fz_tell(doc->file); + } + else + { + *offset = newtmpofs; + } + return 0; +} + +static void +pdf_load_hinted_page(pdf_document *doc, int pagenum) +{ + fz_context *ctx = doc->ctx; + + if (!doc->hints_loaded || !doc->linear_page_refs) + return; + + if (doc->linear_page_refs[pagenum]) + return; + + fz_try(ctx) + { + int num = doc->hint_page[pagenum].number; + pdf_obj *page = pdf_load_object(doc, num, 0); + if (!strcmp("Page", pdf_to_name(pdf_dict_gets(page, "Type")))) + { + /* We have found the page object! */ + DEBUGMESS((ctx, "LoadHintedPage pagenum=%d num=%d", pagenum, num)); + doc->linear_page_refs[pagenum] = pdf_new_indirect(doc, num, 0); + } + pdf_drop_obj(page); + } + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + /* Silently swallow the error and proceed as normal */ + } + +} + +static int +read_hinted_object(pdf_document *doc, int num) +{ + /* Try to find the object using our hint table. Find the closest + * object <= the one we want that has a hint and read forward from + * there. */ + fz_context *ctx = doc->ctx; + int expected = num; + int curr_pos; + int start, offset; + + while (doc->hint_obj_offsets[expected] == 0 && expected > 0) + expected--; + if (expected != num) + DEBUGMESS((ctx, "object %d is unhinted, will search forward from %d", expected, num)); + if (expected == 0) /* No hints found, just bale */ + return 0; + + + curr_pos = fz_tell(doc->file); + offset = doc->hint_obj_offsets[expected]; + + fz_var(expected); + + fz_try(ctx) + { + int found; + + /* Try to read forward from there */ + do + { + start = offset; + DEBUGMESS((ctx, "Searching for object %d @ %d", expected, offset)); + pdf_obj_read(doc, &offset, &found, 0); + DEBUGMESS((ctx, "Found object %d - next will be @ %d", found, offset)); + if (found <= expected) + { + /* We found the right one (or one earlier than + * we expected). Update the hints. */ + doc->hint_obj_offsets[expected] = offset; + doc->hint_obj_offsets[found] = start; + doc->hint_obj_offsets[found+1] = offset; + /* Retry with the next one */ + expected = found+1; + } + else + { + /* We found one later than we expected. */ + doc->hint_obj_offsets[expected] = 0; + doc->hint_obj_offsets[found] = start; + doc->hint_obj_offsets[found+1] = offset; + while (doc->hint_obj_offsets[expected] == 0 && expected > 0) + expected--; + if (expected == 0) /* No hints found, just bale */ + return 0; + } + } + while (found != num); + } + fz_always(ctx) + { + fz_seek(doc->file, curr_pos, SEEK_SET); + } + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + /* FIXME: Currently we ignore the hint. Perhaps we should + * drop back to non-hinted operation here. */ + doc->hint_obj_offsets[expected] = 0; + fz_rethrow(ctx); + } + return 1; +} void pdf_cache_object(pdf_document *doc, int num, int gen) @@ -1260,6 +1587,7 @@ pdf_cache_object(pdf_document *doc, int num, int gen) if (num < 0 || num >= pdf_xref_len(doc)) fz_throw(ctx, FZ_ERROR_GENERIC, "object out of range (%d %d R); xref size %d", num, gen, pdf_xref_len(doc)); +object_updated: x = pdf_get_xref_entry(doc, num); if (x->obj) @@ -1309,6 +1637,14 @@ pdf_cache_object(pdf_document *doc, int num, int gen) fz_throw(ctx, FZ_ERROR_GENERIC, "object (%d %d R) was not found in its object stream", num, gen); } } + else if (doc->hint_obj_offsets && read_hinted_object(doc, num)) + { + goto object_updated; + } + else if (doc->file_length && doc->linear_pos < doc->file_length) + { + fz_throw(ctx, FZ_ERROR_TRYLATER, "cannot find object in xref (%d %d R) - not loaded yet?", num, gen); + } else { fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find object in xref (%d %d R)", num, gen); @@ -1368,7 +1704,7 @@ pdf_resolve_indirect(pdf_obj *ref) } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "cannot load object (%d %d R) into cache", num, gen); return NULL; } @@ -1591,7 +1927,18 @@ pdf_document * pdf_open_document_no_run_with_stream(fz_context *ctx, fz_stream *file) { pdf_document *doc = pdf_new_document(ctx, file); - pdf_init_document(doc); + + fz_var(doc); + + fz_try(ctx) + { + pdf_init_document(doc); + } + fz_catch(ctx) + { + pdf_close_document(doc); + fz_rethrow_message(ctx, "cannot load document from stream"); + } return doc; } @@ -1599,9 +1946,10 @@ pdf_document * pdf_open_document_no_run(fz_context *ctx, const char *filename) { fz_stream *file = NULL; - pdf_document *doc; + pdf_document *doc = NULL; fz_var(file); + fz_var(doc); fz_try(ctx) { @@ -1615,11 +1963,344 @@ pdf_open_document_no_run(fz_context *ctx, const char *filename) } fz_catch(ctx) { + pdf_close_document(doc); fz_rethrow_message(ctx, "cannot load document '%s'", filename); } return doc; } +static void +pdf_load_hints(pdf_document *doc, int objnum, int gennum) +{ + fz_stream *stream = NULL; + pdf_obj *dict; + fz_context *ctx = doc->ctx; + + fz_var(stream); + fz_var(dict); + + fz_try(ctx) + { + int i, j, least_num_page_objs, page_obj_num_bits; + int least_page_len, page_len_num_bits, shared_hint_offset; + int least_page_offset, page_offset_num_bits; + int least_content_stream_len, content_stream_len_num_bits; + int num_shared_obj_num_bits, shared_obj_num_bits; + int numerator_bits, denominator_bits, shared; + int shared_obj_num, shared_obj_offset, shared_obj_count_page1; + int shared_obj_count_total; + int least_shared_group_len, shared_group_len_num_bits; + int max_object_num = pdf_xref_len(doc); + + stream = pdf_open_stream(doc, objnum, gennum); + dict = pdf_get_xref_entry(doc, objnum)->obj; + if (dict == NULL || !pdf_is_dict(dict)) + fz_throw(ctx, FZ_ERROR_GENERIC, "malformed hint object"); + + shared_hint_offset = pdf_to_int(pdf_dict_gets(dict, "S")); + + /* Malloc the structures (use realloc to cope with the fact we + * may try this several times before enough data is loaded) */ + doc->hint_page = fz_resize_array(ctx, doc->hint_page, doc->page_count+1, sizeof(*doc->hint_page)); + memset(doc->hint_page, 0, sizeof(*doc->hint_page) * (doc->page_count+1)); + doc->hint_obj_offsets = fz_resize_array(ctx, doc->hint_obj_offsets, max_object_num, sizeof(*doc->hint_obj_offsets)); + memset(doc->hint_obj_offsets, 0, sizeof(*doc->hint_obj_offsets) * max_object_num); + doc->hint_obj_offsets_max = max_object_num; + + /* Read the page object hints table: Header first */ + least_num_page_objs = fz_read_bits(stream, 32); + /* The following is sometimes a lie, but we read this version, + * as other table values are built from it. In + * pdf_reference17.pdf, this points to 2 objects before the + * first pages page object. */ + doc->hint_page[0].offset = fz_read_bits(stream, 32); + if (doc->hint_page[0].offset > doc->hint_object_offset) + doc->hint_page[0].offset += doc->hint_object_length; + page_obj_num_bits = fz_read_bits(stream, 16); + least_page_len = fz_read_bits(stream, 32); + page_len_num_bits = fz_read_bits(stream, 16); + least_page_offset = fz_read_bits(stream, 32); + page_offset_num_bits = fz_read_bits(stream, 16); + least_content_stream_len = fz_read_bits(stream, 32); + content_stream_len_num_bits = fz_read_bits(stream, 16); + num_shared_obj_num_bits = fz_read_bits(stream, 16); + shared_obj_num_bits = fz_read_bits(stream, 16); + numerator_bits = fz_read_bits(stream, 16); + denominator_bits = fz_read_bits(stream, 16); + + /* Item 1: Page object numbers */ + doc->hint_page[0].number = doc->linear_page1_obj_num; + /* We don't care about the number of objects in the first page */ + (void)fz_read_bits(stream, page_obj_num_bits); + j = 1; + for (i = 1; i < doc->page_count; i++) + { + int delta_page_objs = fz_read_bits(stream, page_obj_num_bits); + + doc->hint_page[i].number = j; + j += least_num_page_objs + delta_page_objs; + } + doc->hint_page[i].number = j; /* Not a real page object */ + fz_sync_bits(stream); + /* Item 2: Page lengths */ + j = doc->hint_page[0].offset; + for (i = 0; i < doc->page_count; i++) + { + int delta_page_len = fz_read_bits(stream, page_len_num_bits); + int old = j; + + doc->hint_page[i].offset = j; + j += least_page_len + delta_page_len; + if (old <= doc->hint_object_offset && j > doc->hint_object_offset) + j += doc->hint_object_length; + } + doc->hint_page[i].offset = j; + fz_sync_bits(stream); + /* Item 3: Shared references */ + shared = 0; + for (i = 0; i < doc->page_count; i++) + { + int num_shared_objs = fz_read_bits(stream, num_shared_obj_num_bits); + doc->hint_page[i].index = shared; + shared += num_shared_objs; + } + doc->hint_page[i].index = shared; + doc->hint_shared_ref = fz_resize_array(ctx, doc->hint_shared_ref, shared, sizeof(*doc->hint_shared_ref)); + memset(doc->hint_shared_ref, 0, sizeof(*doc->hint_shared_ref) * shared); + fz_sync_bits(stream); + /* Item 4: Shared references */ + for (i = 0; i < shared; i++) + { + int ref = fz_read_bits(stream, shared_obj_num_bits); + doc->hint_shared_ref[i] = ref; + } + /* Skip items 5,6,7 as we don't use them */ + + fz_seek(stream, shared_hint_offset, SEEK_SET); + + /* Read the shared object hints table: Header first */ + shared_obj_num = fz_read_bits(stream, 32); + shared_obj_offset = fz_read_bits(stream, 32); + if (shared_obj_offset > doc->hint_object_offset) + shared_obj_offset += doc->hint_object_length; + shared_obj_count_page1 = fz_read_bits(stream, 32); + shared_obj_count_total = fz_read_bits(stream, 32); + shared_obj_num_bits = fz_read_bits(stream, 16); + least_shared_group_len = fz_read_bits(stream, 32); + shared_group_len_num_bits = fz_read_bits(stream, 16); + + /* Sanity check the references in Item 4 above to ensure we + * don't access out of range with malicious files. */ + for (i = 0; i < shared; i++) + { + if (doc->hint_shared_ref[i] >= shared_obj_count_total) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "malformed hint stream (shared refs)"); + } + } + + doc->hint_shared = fz_resize_array(ctx, doc->hint_shared, shared_obj_count_total+1, sizeof(*doc->hint_shared)); + memset(doc->hint_shared, 0, sizeof(*doc->hint_shared) * (shared_obj_count_total+1)); + + /* Item 1: Shared references */ + j = doc->hint_page[0].offset; + for (i = 0; i < shared_obj_count_page1; i++) + { + int off = fz_read_bits(stream, shared_group_len_num_bits); + int old = j; + doc->hint_shared[i].offset = j; + j += off + least_shared_group_len; + if (old <= doc->hint_object_offset && j > doc->hint_object_offset) + j += doc->hint_object_length; + } + /* FIXME: We would have problems recreating the length of the + * last page 1 shared reference group. But we'll never need + * to, so ignore it. */ + j = shared_obj_offset; + for (; i < shared_obj_count_total; i++) + { + int off = fz_read_bits(stream, shared_group_len_num_bits); + int old = j; + doc->hint_shared[i].offset = j; + j += off + least_shared_group_len; + if (old <= doc->hint_object_offset && j > doc->hint_object_offset) + j += doc->hint_object_length; + } + doc->hint_shared[i].offset = j; + fz_sync_bits(stream); + /* Item 2: Signature flags: read these just so we can skip */ + for (i = 0; i < shared_obj_count_total; i++) + { + doc->hint_shared[i].number = fz_read_bits(stream, 1); + } + fz_sync_bits(stream); + /* Item 3: Signatures: just skip */ + for (i = 0; i < shared_obj_count_total; i++) + { + if (doc->hint_shared[i].number) + { + int dummy = fz_read_bits(stream, 128); + } + } + fz_sync_bits(stream); + /* Item 4: Shared object object numbers */ + j = doc->linear_page1_obj_num; /* FIXME: This is a lie! */ + for (i = 0; i < shared_obj_count_page1; i++) + { + doc->hint_shared[i].number = j; + j += fz_read_bits(stream, shared_obj_num_bits) + 1; + } + j = shared_obj_num; + for (; i < shared_obj_count_total; i++) + { + doc->hint_shared[i].number = j; + j += fz_read_bits(stream, shared_obj_num_bits) + 1; + } + doc->hint_shared[i].number = j; + + /* Now, actually use the data we have gathered. */ + for (i = 0 /*shared_obj_count_page1*/; i < shared_obj_count_total; i++) + { + doc->hint_obj_offsets[doc->hint_shared[i].number] = doc->hint_shared[i].offset; + } + for (i = 0; i < doc->page_count; i++) + { + doc->hint_obj_offsets[doc->hint_page[i].number] = doc->hint_page[i].offset; + } + } + fz_always(ctx) + { + fz_close(stream); + } + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + /* Don't try to load hints again */ + doc->hints_loaded = 1; + /* We won't use the linearized object any more. */ + doc->file_reading_linearly = 0; + /* Any other error becomes a TRYLATER */ + fz_throw(ctx, FZ_ERROR_TRYLATER, "malformed hints object"); + } + doc->hints_loaded = 1; +} + +static void +pdf_load_hint_object(pdf_document *doc) +{ + fz_context *ctx = doc->ctx; + pdf_lexbuf *buf = &doc->lexbuf.base; + int curr_pos; + + curr_pos = fz_tell(doc->file); + fz_seek(doc->file, doc->hint_object_offset, SEEK_SET); + fz_try(ctx) + { + while (1) + { + pdf_obj *page = NULL; + int tmpofs, num, gen, tok; + + tok = pdf_lex(doc->file, buf); + if (tok != PDF_TOK_INT) + break; + num = buf->i; + tok = pdf_lex(doc->file, buf); + if (tok != PDF_TOK_INT) + break; + gen = buf->i; + tok = pdf_lex(doc->file, buf); + if (tok != PDF_TOK_OBJ) + break; + (void)pdf_repair_obj(doc, buf, &tmpofs, NULL, NULL, NULL, &page, &tmpofs); + pdf_load_hints(doc, num, gen); + } + } + fz_always(ctx) + { + fz_seek(doc->file, curr_pos, SEEK_SET); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +pdf_obj *pdf_progressive_advance(pdf_document *doc, int pagenum) +{ + fz_context *ctx = doc->ctx; + pdf_lexbuf *buf = &doc->lexbuf.base; + int curr_pos; + pdf_obj *page; + + pdf_load_hinted_page(doc, pagenum); + + if (pagenum < 0 || pagenum >= doc->page_count) + fz_throw(doc->ctx, FZ_ERROR_GENERIC, "page load out of range (%d of %d)", pagenum, doc->page_count); + + if (doc->linear_pos == doc->file_length) + return doc->linear_page_refs[pagenum]; + + /* Only load hints once, and then only after we have got page 0 */ + if (pagenum > 0 && !doc->hints_loaded && doc->hint_object_offset > 0 && doc->linear_pos >= doc->hint_object_offset) + { + /* Found hint object */ + pdf_load_hint_object(doc); + } + + DEBUGMESS((ctx, "continuing to try to advance from %d", doc->linear_pos)); + curr_pos = fz_tell(doc->file); + + fz_var(page); + + fz_try(ctx) + { + int eof; + do + { + int num; + page = NULL; + eof = pdf_obj_read(doc, &doc->linear_pos, &num, &page); + pdf_drop_obj(page); + page = NULL; + } + while (!eof); + + { + pdf_obj *catalog; + pdf_obj *pages; + doc->linear_pos = doc->file_length; + pdf_load_xref(doc, buf); + catalog = pdf_dict_gets(pdf_trailer(doc), "Root"); + pages = pdf_dict_gets(catalog, "Pages"); + + if (!pdf_is_dict(pages)) + fz_throw(ctx, FZ_ERROR_GENERIC, "missing page tree"); + break; + } + } + fz_always(ctx) + { + fz_seek(doc->file, curr_pos, SEEK_SET); + } + fz_catch(ctx) + { + pdf_drop_obj(page); + if (fz_caught(ctx) == FZ_ERROR_TRYLATER) + { + if (doc->linear_page_refs[pagenum] == NULL) + { + /* Still not got a page */ + fz_rethrow(ctx); + } + } + else + fz_rethrow(ctx); + } + + return doc->linear_page_refs[pagenum]; +} + pdf_document *pdf_specifics(fz_document *doc) { return (pdf_document *)((doc && doc->close == (void *)pdf_close_document) ? doc : NULL); diff --git a/source/xps/xps-doc.c b/source/xps/xps-doc.c index 9953cb37..097392e8 100644 --- a/source/xps/xps-doc.c +++ b/source/xps/xps-doc.c @@ -414,7 +414,7 @@ xps_read_page_list(xps_document *doc) } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); fz_warn(doc->ctx, "cannot process FixedDocument rels part"); } xps_read_and_process_metadata_part(doc, fixdoc->name, fixdoc); @@ -447,7 +447,7 @@ xps_load_fixed_page(xps_document *doc, xps_page *page) } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); root = NULL; } if (!root) diff --git a/source/xps/xps-glyphs.c b/source/xps/xps-glyphs.c index 72f37ca1..52cc0042 100644 --- a/source/xps/xps-glyphs.c +++ b/source/xps/xps-glyphs.c @@ -510,7 +510,7 @@ xps_parse_glyphs(xps_document *doc, const fz_matrix *ctm, } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); fz_warn(doc->ctx, "cannot find font resource part '%s'", partname); return; } @@ -527,7 +527,7 @@ xps_parse_glyphs(xps_document *doc, const fz_matrix *ctm, } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); fz_warn(doc->ctx, "cannot load font resource '%s'", partname); xps_free_part(doc, part); return; diff --git a/source/xps/xps-image.c b/source/xps/xps-image.c index eb0d876f..cff7860d 100644 --- a/source/xps/xps-image.c +++ b/source/xps/xps-image.c @@ -102,7 +102,7 @@ xps_parse_image_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *ar } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); fz_warn(doc->ctx, "cannot find image source"); return; } @@ -117,7 +117,7 @@ xps_parse_image_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *ar } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); fz_warn(doc->ctx, "cannot decode image resource"); return; } diff --git a/source/xps/xps-outline.c b/source/xps/xps-outline.c index b87460a4..d8b573a9 100644 --- a/source/xps/xps-outline.c +++ b/source/xps/xps-outline.c @@ -131,7 +131,7 @@ xps_load_outline(xps_document *doc) } fz_catch(doc->ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(doc->ctx, FZ_ERROR_TRYLATER); outline = NULL; } if (!outline) diff --git a/source/xps/xps-resource.c b/source/xps/xps-resource.c index 5c927e1d..ef699619 100644 --- a/source/xps/xps-resource.c +++ b/source/xps/xps-resource.c @@ -75,7 +75,7 @@ xps_parse_remote_resource_dictionary(xps_document *doc, char *base_uri, char *so } fz_catch(ctx) { - /* FIXME: TryLater ? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); xml = NULL; } |