[gs-commits] mupdf 1.16.1.93 Chapter based API for faster EPUB loading
[email protected] (Robin Watts) Wed, 16 Oct 2019 12:56:29 +0000 (UTC)
| Newsgroups | gmane.comp.printing.ghostscript.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit 80606caf66c9bd1306ae264f972c5c859e154f17 Author: Tor Andersson <[email protected]> Date: Tue Sep 10 12:04:01 2019 +0200 Chapter based API for faster EPUB loading. * Navigate using locations. * Show current location in outline in single-chapter documents. * Add chapter based page API to JNI bindings; reimplement old countPages and loadPage on top of new API. * Add accelerator API and hook up stubs for epub. * Fill in stubs to read/write accelerator data for epub. * Remove dirty flag from epub_chapter. * fz_html now knows the size it was last laid out to, and so we can check it there to avoid unnecessary relayouts. * Make fz_html storable. * All epub chapters to be dropped between uses. * Make accelerator depend on user css and document css flag. * Change fz_bookmark functions to use fz_location. diff --git a/include/mupdf/fitz/document.h b/include/mupdf/fitz/document.h index 379b5d6..bd7fcfd 100644 --- a/include/mupdf/fitz/document.h +++ b/include/mupdf/fitz/document.h @@ -15,6 +15,18 @@ typedef struct fz_document_handler_s fz_document_handler; typedef struct fz_page_s fz_page; typedef intptr_t fz_bookmark; +typedef struct fz_location_s +{ + int chapter; + int page; +} fz_location; + +static inline fz_location fz_make_location(int chapter, int page) +{ + fz_location loc = { chapter, page }; + return loc; +} + enum { /* 6in at 4:3 */ @@ -115,20 +127,27 @@ typedef void (fz_document_layout_fn)(fz_context *ctx, fz_document *doc, float w, resolve an internal link to a page number. See fz_resolve_link for more information. */ -typedef int (fz_document_resolve_link_fn)(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp); +typedef fz_location (fz_document_resolve_link_fn)(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp); + +/* + Type for a function to be called to + count the number of chapters in a document. See fz_count_chapters for + more information. +*/ +typedef int (fz_document_count_chapters_fn)(fz_context *ctx, fz_document *doc); /* Type for a function to be called to count the number of pages in a document. See fz_count_pages for more information. */ -typedef int (fz_document_count_pages_fn)(fz_context *ctx, fz_document *doc); +typedef int (fz_document_count_pages_fn)(fz_context *ctx, fz_document *doc, int chapter); /* Type for a function to load a given page from a document. See fz_load_page for more information. */ -typedef fz_page *(fz_document_load_page_fn)(fz_context *ctx, fz_document *doc, int number); +typedef fz_page *(fz_document_load_page_fn)(fz_context *ctx, fz_document *doc, int chapter, int page); /* Type for a function to query @@ -143,16 +162,21 @@ typedef int (fz_document_lookup_metadata_fn)(fz_context *ctx, fz_document *doc, typedef fz_colorspace* (fz_document_output_intent_fn)(fz_context *ctx, fz_document *doc); /* + Write document accelerator data +*/ +typedef void (fz_document_output_accelerator_fn)(fz_context *ctx, fz_document *doc, fz_output *out); + +/* Type for a function to make a bookmark. See fz_make_bookmark for more information. */ -typedef fz_bookmark (fz_document_make_bookmark_fn)(fz_context *ctx, fz_document *doc, int page); +typedef fz_bookmark (fz_document_make_bookmark_fn)(fz_context *ctx, fz_document *doc, fz_location loc); /* Type for a function to lookup a bookmark. See fz_lookup_bookmark for more information. */ -typedef int (fz_document_lookup_bookmark_fn)(fz_context *ctx, fz_document *doc, fz_bookmark mark); +typedef fz_location (fz_document_lookup_bookmark_fn)(fz_context *ctx, fz_document *doc, fz_bookmark mark); /* Type for a function to release all the @@ -223,7 +247,8 @@ typedef int (fz_page_uses_overprint_fn)(fz_context *ctx, fz_page *page); struct fz_page_s { int refs; - int number; /* page number */ + int chapter; /* chapter number */ + int number; /* page number in chapter */ int incomplete; /* incomplete from progressive loading; don't cache! */ fz_page_drop_page_fn *drop_page; fz_page_bound_page_fn *bound_page; @@ -257,10 +282,12 @@ struct fz_document_s fz_document_make_bookmark_fn *make_bookmark; fz_document_lookup_bookmark_fn *lookup_bookmark; fz_document_resolve_link_fn *resolve_link; + fz_document_count_chapters_fn *count_chapters; fz_document_count_pages_fn *count_pages; fz_document_load_page_fn *load_page; fz_document_lookup_metadata_fn *lookup_metadata; fz_document_output_intent_fn *get_output_intent; + fz_document_output_accelerator_fn *output_accelerator; int did_layout; int is_reflowable; fz_page *open; /* linked list of currently open pages */ @@ -288,6 +315,32 @@ typedef fz_document *(fz_document_open_fn)(fz_context *ctx, const char *filename typedef fz_document *(fz_document_open_with_stream_fn)(fz_context *ctx, fz_stream *stream); /* + Function type to open a document from a + file, with accelerator data. + + filename: file to open + + accel: accelerator file + + Pointer to opened document. Throws exception in case of error. +*/ +typedef fz_document *(fz_document_open_accel_fn)(fz_context *ctx, const char *filename, const char *accel); + +/* + Function type to open a document from a file, + with accelerator data. + + stream: fz_stream to read document data from. Must be + seekable for formats that require it. + + accel: fz_stream to read accelerator data from. Must be + seekable for formats that require it. + + Pointer to opened document. Throws exception in case of error. +*/ +typedef fz_document *(fz_document_open_accel_with_stream_fn)(fz_context *ctx, fz_stream *stream, fz_stream *accel); + +/* Recognize a document type from a magic string. @@ -307,6 +360,8 @@ struct fz_document_handler_s fz_document_open_with_stream_fn *open_with_stream; const char **extensions; const char **mimetypes; + fz_document_open_accel_fn *open_accel; + fz_document_open_accel_with_stream_fn *open_accel_with_stream; }; void fz_register_document_handler(fz_context *ctx, const fz_document_handler *handler); @@ -317,8 +372,18 @@ const fz_document_handler *fz_recognize_document(fz_context *ctx, const char *ma fz_document *fz_open_document(fz_context *ctx, const char *filename); +fz_document *fz_open_accelerated_document(fz_context *ctx, const char *filename, const char *accel); + fz_document *fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream); +fz_document *fz_open_accelerated_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel); + +int fz_document_supports_accelerator(fz_context *ctx, fz_document *doc); + +void fz_save_accelerator(fz_context *ctx, fz_document *doc, const char *accel); + +void fz_output_accelerator(fz_context *ctx, fz_document *doc, fz_output *accel); + void *fz_new_document_of_size(fz_context *ctx, int size); #define fz_new_derived_document(C,M) ((M*)Memento_label(fz_new_document_of_size(C, sizeof(M)), #M)) @@ -335,16 +400,26 @@ int fz_is_document_reflowable(fz_context *ctx, fz_document *doc); void fz_layout_document(fz_context *ctx, fz_document *doc, float w, float h, float em); -fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, int page); +fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, fz_location loc); -int fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark); +fz_location fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark); int fz_count_pages(fz_context *ctx, fz_document *doc); -int fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp); +fz_location fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp); +fz_location fz_last_page(fz_context *ctx, fz_document *doc); +fz_location fz_next_page(fz_context *ctx, fz_document *doc, fz_location loc); +fz_location fz_previous_page(fz_context *ctx, fz_document *doc, fz_location loc); +fz_location fz_clamp_location(fz_context *ctx, fz_document *doc, fz_location loc); +fz_location fz_location_from_page_number(fz_context *ctx, fz_document *doc, int number); +int fz_page_number_from_location(fz_context *ctx, fz_document *doc, fz_location loc); fz_page *fz_load_page(fz_context *ctx, fz_document *doc, int number); +int fz_count_chapters(fz_context *ctx, fz_document *doc); +int fz_count_chapter_pages(fz_context *ctx, fz_document *doc, int chapter); +fz_page *fz_load_chapter_page(fz_context *ctx, fz_document *doc, int chapter, int page); + fz_link *fz_load_links(fz_context *ctx, fz_page *page); fz_page *fz_new_page_of_size(fz_context *ctx, int size); diff --git a/include/mupdf/fitz/util.h b/include/mupdf/fitz/util.h index a787ee1..2b5fb0f 100644 --- a/include/mupdf/fitz/util.h +++ b/include/mupdf/fitz/util.h @@ -34,6 +34,7 @@ fz_buffer *fz_new_buffer_from_display_list(fz_context *ctx, fz_display_list *lis int fz_search_page(fz_context *ctx, fz_page *page, const char *needle, fz_quad *hit_bbox, int hit_max); int fz_search_page_number(fz_context *ctx, fz_document *doc, int number, const char *needle, fz_quad *hit_bbox, int hit_max); +int fz_search_chapter_page_number(fz_context *ctx, fz_document *doc, int chapter, int page, const char *needle, fz_quad *hit_bbox, int hit_max); int fz_search_display_list(fz_context *ctx, fz_display_list *list, const char *needle, fz_quad *hit_bbox, int hit_max); fz_display_list *fz_new_display_list_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip, float *w, float *h); diff --git a/include/mupdf/pdf/annot.h b/include/mupdf/pdf/annot.h index f532d18..afaf618 100644 --- a/include/mupdf/pdf/annot.h +++ b/include/mupdf/pdf/annot.h @@ -115,6 +115,7 @@ pdf_obj *pdf_load_name_tree(fz_context *ctx, pdf_document *doc, pdf_obj *which); pdf_obj *pdf_lookup_number(fz_context *ctx, pdf_obj *root, int needle); int pdf_resolve_link(fz_context *ctx, pdf_document *doc, const char *uri, float *xp, float *yp); +fz_location pdf_resolve_link_imp(fz_context *ctx, fz_document *doc_, const char *uri, float *xp, float *yp); fz_link *pdf_load_link_annots(fz_context *ctx, pdf_document *, pdf_obj *annots, int pagenum, fz_matrix page_ctm); fz_matrix pdf_annot_transform(fz_context *ctx, pdf_annot *annot); diff --git a/include/mupdf/pdf/page.h b/include/mupdf/pdf/page.h index e42986c..4926a65 100644 --- a/include/mupdf/pdf/page.h +++ b/include/mupdf/pdf/page.h @@ -5,6 +5,7 @@ int pdf_lookup_page_number(fz_context *ctx, pdf_document *doc, pdf_obj *pageobj); int pdf_count_pages(fz_context *ctx, pdf_document *doc); +int pdf_count_pages_imp(fz_context *ctx, fz_document *doc, int chapter); pdf_obj *pdf_lookup_page_obj(fz_context *ctx, pdf_document *doc, int needle); void pdf_load_page_tree(fz_context *ctx, pdf_document *doc); void pdf_drop_page_tree(fz_context *ctx, pdf_document *doc); @@ -14,6 +15,7 @@ int pdf_lookup_anchor(fz_context *ctx, pdf_document *doc, const char *name, floa void pdf_flatten_inheritable_page_items(fz_context *ctx, pdf_obj *page); pdf_page *pdf_load_page(fz_context *ctx, pdf_document *doc, int number); +fz_page *pdf_load_page_imp(fz_context *ctx, fz_document *doc, int chapter, int number); void pdf_page_obj_transform(fz_context *ctx, pdf_obj *pageobj, fz_rect *page_mediabox, fz_matrix *page_ctm); void pdf_page_transform(fz_context *ctx, pdf_page *page, fz_rect *mediabox, fz_matrix *ctm); diff --git a/platform/gl/gl-main.c b/platform/gl/gl-main.c index ffc5dd3..30a1c28 100644 --- a/platform/gl/gl-main.c +++ b/platform/gl/gl-main.c @@ -165,7 +165,7 @@ static int oldinvert = 0, currentinvert = 0; static int oldicc = 1, currenticc = 1; static int oldaa = 8, currentaa = 8; static int oldseparations = 0, currentseparations = 0; -static int oldpage = 0, currentpage = 0; +static fz_location oldpage = {0,0}, currentpage = {0,0}; static float oldzoom = DEFRES, currentzoom = DEFRES; static float oldrotate = 0, currentrotate = 0; @@ -181,7 +181,7 @@ static const char *tooltip = NULL; struct mark { - int page; + fz_location loc; fz_point scroll; }; @@ -245,6 +245,37 @@ static void read_history_file_as_json(js_State *J) fz_drop_buffer(ctx, buf); } +static fz_location try_location(js_State *J) +{ + fz_location loc; + if (js_isnumber(J, -1)) + loc = fz_make_location(0, js_tryinteger(J, -1, 1) - 1); + else + { + js_getindex(J, -1, 0); + loc.chapter = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + js_getindex(J, -1, 1); + loc.page = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + } + return loc; +} + +static void push_location(js_State *J, fz_location loc) +{ + if (loc.chapter == 0) + js_pushnumber(J, loc.page+1); + else + { + js_newarray(J); + js_pushnumber(J, loc.chapter+1); + js_setindex(J, -2, 0); + js_pushnumber(J, loc.page+1); + js_setindex(J, -2, 1); + } +} + static void load_history(void) { js_State *J; @@ -262,7 +293,7 @@ static void load_history(void) { if (js_hasproperty(J, -1, "current")) { - currentpage = js_tryinteger(J, -1, 1) - 1; + currentpage = try_location(J); js_pop(J, 1); } @@ -274,7 +305,7 @@ static void load_history(void) for (i = 0; i < history_count; ++i) { js_getindex(J, -1, i); - history[i].page = js_tryinteger(J, -1, 1) - 1; + history[i].loc = try_location(J); js_pop(J, 1); } } @@ -289,7 +320,7 @@ static void load_history(void) for (i = 0; i < future_count; ++i) { js_getindex(J, -1, i); - future[i].page = js_tryinteger(J, -1, 1) - 1; + future[i].loc = try_location(J); js_pop(J, 1); } } @@ -304,7 +335,7 @@ static void load_history(void) for (i = 0; i < n; ++i) { js_getindex(J, -1, i); - marks[i].page = js_tryinteger(J, -1, 1) - 1; + marks[i].loc = try_location(J); js_pop(J, 1); } } @@ -337,13 +368,13 @@ static void save_history(void) js_newobject(J); { - js_pushnumber(J, currentpage+1); + push_location(J, currentpage); js_setproperty(J, -2, "current"); js_newarray(J); for (i = 0; i < history_count; ++i) { - js_pushnumber(J, history[i].page+1); + push_location(J, history[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "history"); @@ -351,7 +382,7 @@ static void save_history(void) js_newarray(J); for (i = 0; i < future_count; ++i) { - js_pushnumber(J, future[i].page+1); + push_location(J, future[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "future"); @@ -359,7 +390,7 @@ static void save_history(void) js_newarray(J); for (i = 0; i < (int)nelem(marks); ++i) { - js_pushnumber(J, marks[i].page+1); + push_location(J, marks[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "marks"); @@ -395,8 +426,8 @@ static int search_active = 0; static struct input search_input = { { 0 }, 0 }; static char *search_needle = 0; static int search_dir = 1; -static int search_page = -1; -static int search_hit_page = -1; +static fz_location search_page = {-1, -1}; +static fz_location search_hit_page = {-1, -1}; static int search_hit_count = 0; static fz_quad search_hit_quads[5000]; @@ -520,6 +551,8 @@ void update_title(void) char *extra = ""; size_t n; + int nc = fz_count_chapters(ctx, doc); + title = strrchr(filename, '/'); if (!title) title = strrchr(filename, '\\'); @@ -533,9 +566,24 @@ void update_title(void) n = strlen(title); if (n > 50) - sprintf(buf, "...%s%s - %d / %d", title + n - 50, extra, currentpage + 1, fz_count_pages(ctx, doc)); + { + if (nc == 1) + sprintf(buf, "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); + else + sprintf(buf, "...%s%s - %d/%d - %d/%d", title + n - 50, extra, + currentpage.chapter + 1, nc, + currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); + } else - sprintf(buf, "%s%s - %d / %d", title, extra, currentpage + 1, fz_count_pages(ctx, doc)); + { + if (nc == 1) + sprintf(buf, "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); + else + + sprintf(buf, "%s%s - %d/%d - %d/%d", title, extra, + currentpage.chapter + 1, nc, + currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); + } glutSetWindowTitle(buf); glutSetIconTitle(buf); } @@ -564,7 +612,7 @@ void load_page(void) fz_drop_page(ctx, fzpage); fzpage = NULL; - fzpage = fz_load_page(ctx, doc, currentpage); + fzpage = fz_load_chapter_page(ctx, doc, currentpage.chapter, currentpage.page); if (pdf) page = (pdf_page*)fzpage; @@ -625,7 +673,8 @@ void render_page(void) void render_page_if_changed(void) { - if (oldpage != currentpage || + if (oldpage.chapter != currentpage.chapter || + oldpage.page != currentpage.page || oldzoom != currentzoom || oldrotate != currentrotate || oldinvert != currentinvert || @@ -649,22 +698,38 @@ void render_page_if_changed(void) static struct mark save_mark() { struct mark mark; - mark.page = currentpage; + mark.loc = currentpage; mark.scroll = fz_transform_point_xy(scroll_x, scroll_y, view_page_inv_ctm); return mark; } static void restore_mark(struct mark mark) { - currentpage = mark.page; + currentpage = mark.loc; mark.scroll = fz_transform_point(mark.scroll, draw_page_ctm); scroll_x = mark.scroll.x; scroll_y = mark.scroll.y; } +static int eqloc(fz_location a, fz_location b) +{ + return a.chapter == b.chapter && a.page == b.page; +} + +static int is_first_page(fz_location loc) +{ + return (loc.chapter == 0 && loc.page == 0); +} + +static int is_last_page(fz_location loc) +{ + fz_location last = fz_last_page(ctx, doc); + return (loc.chapter == last.chapter && loc.page == last.page); +} + static void push_history(void) { - if (history_count > 0 && history[history_count-1].page == currentpage) + if (history_count > 0 && eqloc(history[history_count-1].loc, currentpage)) return; if (history_count + 1 >= (int)nelem(history)) { @@ -695,22 +760,41 @@ static void clear_future(void) future_count = 0; } +static void jump_to_location(fz_location loc) +{ + clear_future(); + push_history(); + currentpage = fz_clamp_location(ctx, doc, loc); + push_history(); +} + +static void jump_to_location_xy(fz_location loc, float x, float y) +{ + fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); + clear_future(); + push_history(); + currentpage = fz_clamp_location(ctx, doc, loc); + scroll_x = p.x; + scroll_y = p.y; + push_history(); +} + static void jump_to_page(int newpage) { - newpage = fz_clampi(newpage, 0, fz_count_pages(ctx, doc) - 1); clear_future(); push_history(); - currentpage = newpage; + currentpage = fz_location_from_page_number(ctx, doc, newpage); + currentpage = fz_clamp_location(ctx, doc, currentpage); push_history(); } static void jump_to_page_xy(int newpage, float x, float y) { fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); - newpage = fz_clampi(newpage, 0, fz_count_pages(ctx, doc) - 1); clear_future(); push_history(); - currentpage = newpage; + currentpage = fz_location_from_page_number(ctx, doc, newpage); + currentpage = fz_clamp_location(ctx, doc, currentpage); scroll_x = p.x; scroll_y = p.y; push_history(); @@ -718,17 +802,17 @@ static void jump_to_page_xy(int newpage, float x, float y) static void pop_history(void) { - int here = currentpage; + fz_location here = currentpage; push_future(); - while (history_count > 0 && currentpage == here) + while (history_count > 0 && eqloc(currentpage, here)) restore_mark(history[--history_count]); } static void pop_future(void) { - int here = currentpage; + fz_location here = currentpage; push_history(); - while (future_count > 0 && currentpage == here) + while (future_count > 0 && eqloc(currentpage, here)) restore_mark(future[--future_count]); push_history(); } @@ -758,16 +842,15 @@ static int count_outline(fz_outline *node, int end) while (node) { p = node->page; - if (p >= 0) - { - count += 1; - n = end; - if (node->next && node->next->page >= 0) - n = node->next->page; - is_selected = (currentpage == p || (currentpage > p && currentpage < n)); - if (node->down && (node->is_open || is_selected)) - count += count_outline(node->down, end); - } + count += 1; + n = end; + if (node->next && node->next->page >= 0) + n = node->next->page; + is_selected = 0; + if (fz_count_chapters(ctx, doc) == 1) + is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); + if (node->down && (node->is_open || is_selected)) + count += count_outline(node->down, end); node = node->next; } return count; @@ -775,25 +858,34 @@ static int count_outline(fz_outline *node, int end) static void do_outline_imp(struct list *list, int end, fz_outline *node, int depth) { - int selected, was_open, n; + int is_selected, was_open, n; while (node) { int p = node->page; - if (p >= 0) + n = end; + if (node->next && node->next->page >= 0) + n = node->next->page; + + was_open = node->is_open; + is_selected = 0; + if (fz_count_chapters(ctx, doc) == 1) + is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); + if (ui_tree_item(list, node, node->title, is_selected, depth, !!node->down, &node->is_open)) { - n = end; - if (node->next && node->next->page >= 0) - n = node->next->page; - - was_open = node->is_open; - selected = (currentpage == p || (currentpage > p && currentpage < n)); - if (ui_tree_item(list, node, node->title, selected, depth, !!node->down, &node->is_open)) + if (p < 0) + { + currentpage = fz_resolve_link(ctx, doc, node->uri, &node->x, &node->y); + jump_to_location_xy(currentpage, node->x, node->y); + } + else + { jump_to_page_xy(p, node->x, node->y); - - if (node->down && (was_open || selected)) - do_outline_imp(list, n, node->down, depth + 1); + } } + + if (node->down && (was_open || is_selected)) + do_outline_imp(list, n, node->down, depth + 1); node = node->next; } } @@ -802,8 +894,8 @@ static void do_outline(fz_outline *node) { static struct list list; ui_layout(L, BOTH, NW, 0, 0); - ui_tree_begin(&list, count_outline(node, fz_count_pages(ctx, doc)), outline_w, 0, 1); - do_outline_imp(&list, fz_count_pages(ctx, doc), node, 0); + ui_tree_begin(&list, count_outline(node, 65535), outline_w, 0, 1); + do_outline_imp(&list, 65535, node, 0); ui_tree_end(&list); ui_splitter(&outline_w, 150, 500, R); } @@ -850,11 +942,8 @@ static void do_links(fz_link *link) open_browser(link->uri); else { - int p = fz_resolve_link(ctx, doc, link->uri, &link_x, &link_y); - if (p >= 0) - jump_to_page_xy(p, link_x, link_y); - else - fz_warn(ctx, "cannot find link destination '%s'", link->uri); + fz_location loc = fz_resolve_link(ctx, doc, link->uri, &link_x, &link_y); + jump_to_location_xy(loc, link_x, link_y); } } } @@ -1057,7 +1146,7 @@ static void load_document(void) } anchor = NULL; - currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); + currentpage = fz_clamp_location(ctx, doc, currentpage); } void reload(void) @@ -1128,11 +1217,12 @@ static void smart_move_backward(void) { if (scroll_x <= slop_x) { - if (currentpage - 1 >= 0) + fz_location prev = fz_previous_page(ctx, doc, currentpage); + if (!eqloc(currentpage, prev)) { - scroll_x = page_tex.w; - scroll_y = page_tex.h; - currentpage -= 1; + scroll_x = (page_tex.w <= canvas_w) ? 0 : page_tex.w - canvas_w; + scroll_y = (page_tex.h <= canvas_h) ? 0 : page_tex.h - canvas_h; + currentpage = prev; } } else @@ -1155,11 +1245,12 @@ static void smart_move_forward(void) { if (scroll_x + canvas_w >= page_tex.w - slop_x) { - if (currentpage + 1 < fz_count_pages(ctx, doc)) + fz_location next = fz_next_page(ctx, doc, currentpage); + if (!eqloc(currentpage, next)) { scroll_x = 0; scroll_y = 0; - currentpage += 1; + currentpage = next; } } else @@ -1177,7 +1268,8 @@ static void smart_move_forward(void) static void clear_search(void) { showsearch = 0; - search_hit_page = -1; + search_page = currentpage; + search_hit_page = fz_make_location(-1, -1); search_hit_count = 0; } @@ -1228,10 +1320,19 @@ static void do_app(void) case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break; case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break; - case ',': case KEY_PAGE_UP: currentpage -= fz_maxi(number, 1); break; - case '.': case KEY_PAGE_DOWN: currentpage += fz_maxi(number, 1); break; case 'g': jump_to_page(number - 1); break; - case 'G': jump_to_page(fz_count_pages(ctx, doc) - 1); break; + case 'G': jump_to_location(fz_last_page(ctx, doc)); break; + + case ',': case KEY_PAGE_UP: + number = fz_maxi(number, 1); + while (number--) + currentpage = fz_previous_page(ctx, doc, currentpage); + break; + case '.': case KEY_PAGE_DOWN: + number = fz_maxi(number, 1); + while (number--) + currentpage = fz_next_page(ctx, doc, currentpage); + break; case 'A': if (number == 0) @@ -1256,7 +1357,7 @@ static void do_app(void) { struct mark mark = marks[number]; restore_mark(mark); - jump_to_page(mark.page); + jump_to_location(mark.loc); } break; case 'T': @@ -1285,29 +1386,33 @@ static void do_app(void) break; case 'N': search_dir = -1; - if (search_hit_page == currentpage) - search_page = currentpage + search_dir; + search_active = !!search_needle; + if (eqloc(search_hit_page, currentpage)) + { + search_page = fz_previous_page(ctx, doc, currentpage); + if (is_first_page(search_page)) + search_active = 0; + } else - search_page = currentpage; - if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { - search_hit_page = -1; - if (search_needle) - search_active = 1; + search_page = currentpage; } + search_hit_page = fz_make_location(-1, -1); break; case 'n': search_dir = 1; - if (search_hit_page == currentpage) - search_page = currentpage + search_dir; + search_active = !!search_needle; + if (eqloc(search_hit_page, currentpage)) + { + search_page = fz_next_page(ctx, doc, currentpage); + if (is_last_page(search_page)) + search_active = 0; + } else - search_page = currentpage; - if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { - search_hit_page = -1; - if (search_needle) - search_active = 1; + search_page = currentpage; } + search_hit_page = fz_make_location(-1, -1); break; } @@ -1316,12 +1421,12 @@ static void do_app(void) else number = 0; - currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); + currentpage = fz_clamp_location(ctx, doc, currentpage); while (currentrotate < 0) currentrotate += 360; while (currentrotate >= 360) currentrotate -= 360; - if (search_hit_page != currentpage) - search_hit_page = -1; /* clear highlights when navigating */ + if (!eqloc(search_hit_page, currentpage)) + search_hit_page = fz_make_location(-1, -1); /* clear highlights when navigating */ ui.key = 0; /* we ate the key event, so zap it */ } @@ -1363,7 +1468,7 @@ static void do_info(void) fz_strlcat(buf, "none", sizeof buf); ui_label("Permissions: %s", buf); } - ui_label("Page: %d / %d", currentpage + 1, fz_count_pages(ctx, doc)); + ui_label("Page: %d / %d", fz_page_number_from_location(ctx, doc, currentpage)+1, fz_count_pages(ctx, doc)); { int w = (int)(page_bounds.x1 - page_bounds.x0 + 0.5f); int h = (int)(page_bounds.y1 - page_bounds.y0 + 0.5f); @@ -1473,7 +1578,7 @@ static void do_canvas(void) ui_layout(T, X, NW, 0, 0); ui_panel_begin(0, ui.gridsize+8, 4, 4, 1); ui_layout(L, NONE, W, 2, 0); - ui_label("Searching page %d of %d.", search_page + 1, fz_count_pages(ctx, doc)); + ui_label("Searching chapter %d page %d...", search_page.chapter, search_page.page); ui_panel_end(); } else @@ -1486,7 +1591,7 @@ static void do_canvas(void) do_links(links); do_page_selection(); - if (search_hit_page == currentpage && search_hit_count > 0) + if (eqloc(search_hit_page, currentpage) && search_hit_count > 0) do_search_hits(); } @@ -1500,7 +1605,7 @@ static void do_canvas(void) if (ui_input(&search_input, 0, 1) == UI_INPUT_ACCEPT) { showsearch = 0; - search_page = -1; + search_page = fz_make_location(-1, -1); if (search_needle) { fz_free(ctx, search_needle); @@ -1544,24 +1649,33 @@ void do_main(void) ui.key = ui.mod = ui.plain = 0; ui.down = ui.middle = ui.right = 0; - while (glutGet(GLUT_ELAPSED_TIME) < start_time + 200) + while (search_active && glutGet(GLUT_ELAPSED_TIME) < start_time + 200) { - search_hit_count = fz_search_page_number(ctx, doc, search_page, search_needle, - search_hit_quads, nelem(search_hit_quads)); + search_hit_count = fz_search_chapter_page_number(ctx, doc, + search_page.chapter, search_page.page, + search_needle, + search_hit_quads, nelem(search_hit_quads)); if (search_hit_count) { search_active = 0; search_hit_page = search_page; - jump_to_page(search_hit_page); - break; + jump_to_location(search_hit_page); } else { - search_page += search_dir; - if (search_page < 0 || search_page == fz_count_pages(ctx, doc)) + if (search_dir > 0) { - search_active = 0; - break; + if (is_last_page(search_page)) + search_active = 0; + else + search_page = fz_next_page(ctx, doc, search_page); + } + else + { + if (is_first_page(search_page)) + search_active = 0; + else + search_page = fz_previous_page(ctx, doc, search_page); } } } @@ -1576,7 +1690,7 @@ void do_main(void) if (showoutline) do_outline(outline); - if (oldpage != currentpage || oldseparations != currentseparations || oldicc != currenticc) + if (!eqloc(oldpage, currentpage) || oldseparations != currentseparations || oldicc != currenticc) { load_page(); update_title(); diff --git a/platform/java/example/Viewer.java b/platform/java/example/Viewer.java index a39ffd6..8f0ec2e 100644 --- a/platform/java/example/Viewer.java +++ b/platform/java/example/Viewer.java @@ -421,12 +421,12 @@ public class Viewer extends Frame implements WindowListener, ActionListener, Ite } if (layoutEm != oldLayoutEm) { - long mark = doc.makeBookmark(pageNumber); + long mark = doc.makeBookmark(doc.locationFromPageNumber(pageNumber)); doc.layout(layoutWidth, layoutHeight, layoutEm); updateOutline(); pageCount = doc.countPages(); pageLabel.setText("/ " + pageCount); - pageNumber = doc.findBookmark(mark); + pageNumber = doc.pageNumberFromLocation(doc.findBookmark(mark)); } if (zoomLevel != oldZoomLevel || pageNumber != oldPageNumber || layoutEm != oldLayoutEm || searchHits != oldSearchHits) @@ -444,9 +444,10 @@ public class Viewer extends Frame implements WindowListener, ActionListener, Ite if (source == outlineList) { int i = outlineList.getSelectedIndex(); Outline node = flatOutline.elementAt(i); - if (node.page >= 0) { - if (node.page != pageNumber) { - pageNumber = node.page; + int linkPage = doc.pageNumberFromLocation(doc.resolveLink(node)); + if (linkPage >= 0) { + if (linkPage != pageNumber) { + pageNumber = linkPage; updatePageCanvas(); } } diff --git a/platform/java/mupdf_native.c b/platform/java/mupdf_native.c index 2f199ee..9eaaab4 100644 --- a/platform/java/mupdf_native.c +++ b/platform/java/mupdf_native.c @@ -69,6 +69,7 @@ static jclass cls_Image; static jclass cls_IndexOutOfBoundsException; static jclass cls_IntegerArray; static jclass cls_Link; +static jclass cls_Location; static jclass cls_Matrix; static jclass cls_NativeDevice; static jclass cls_NullPointerException; @@ -114,9 +115,6 @@ static jfieldID fid_DocumentWriter_pointer; static jfieldID fid_Document_pointer; static jfieldID fid_Font_pointer; static jfieldID fid_Image_pointer; -static jfieldID fid_Link_bounds; -static jfieldID fid_Link_page; -static jfieldID fid_Link_uri; static jfieldID fid_Matrix_a; static jfieldID fid_Matrix_b; static jfieldID fid_Matrix_c; @@ -195,6 +193,7 @@ static jmethodID mid_Document_init; static jmethodID mid_Font_init; static jmethodID mid_Image_init; static jmethodID mid_Link_init; +static jmethodID mid_Location_init; static jmethodID mid_Matrix_init; static jmethodID mid_Object_toString; static jmethodID mid_Outline_init; @@ -579,10 +578,10 @@ static int find_fids(JNIEnv *env) mid_Image_init = get_method(&err, env, "<init>", "(J)V"); cls_Link = get_class(&err, env, PKG"Link"); - fid_Link_bounds = get_field(&err, env, "bounds", "L"PKG"Rect;"); - fid_Link_page = get_field(&err, env, "page", "I"); - fid_Link_uri = get_field(&err, env, "uri", "Ljava/lang/String;"); - mid_Link_init = get_method(&err, env, "<init>", "(L"PKG"Rect;ILjava/lang/String;)V"); + mid_Link_init = get_method(&err, env, "<init>", "(L"PKG"Rect;Ljava/lang/String;)V"); + + cls_Location = get_class(&err, env, PKG"Location"); + mid_Location_init = get_method(&err, env, "<init>", "(IIFF)V"); cls_Matrix = get_class(&err, env, PKG"Matrix"); fid_Matrix_a = get_field(&err, env, "a", "F"); @@ -598,7 +597,7 @@ static int find_fids(JNIEnv *env) fid_NativeDevice_nativeInfo = get_field(&err, env, "nativeInfo", "J"); cls_Outline = get_class(&err, env, PKG"Outline"); - mid_Outline_init = get_method(&err, env, "<init>", "(Ljava/lang/String;ILjava/lang/String;FF[L"PKG"Outline;)V"); + mid_Outline_init = get_method(&err, env, "<init>", "(Ljava/lang/String;Ljava/lang/String;[L"PKG"Outline;)V"); cls_Page = get_class(&err, env, PKG"Page"); fid_Page_pointer = get_field(&err, env, "pointer", "J"); @@ -806,6 +805,7 @@ static void lose_fids(JNIEnv *env) (*env)->DeleteGlobalRef(env, cls_IntegerArray); (*env)->DeleteGlobalRef(env, cls_IOException); (*env)->DeleteGlobalRef(env, cls_Link); + (*env)->DeleteGlobalRef(env, cls_Location); (*env)->DeleteGlobalRef(env, cls_Matrix); (*env)->DeleteGlobalRef(env, cls_NativeDevice); (*env)->DeleteGlobalRef(env, cls_NullPointerException); @@ -1472,11 +1472,8 @@ static inline jobject to_Outline_safe(fz_context *ctx, JNIEnv *env, fz_document while (outline) { jstring jtitle = NULL; - jint jpage = -1; jstring juri = NULL; jobject jdown = NULL; - float x = 0; - float y = 0; if (outline->title) { @@ -1486,13 +1483,8 @@ static inline jobject to_Outline_safe(fz_context *ctx, JNIEnv *env, fz_document if (outline->uri) { - if (fz_is_external_link(ctx, outline->uri)) - { - juri = (*env)->NewStringUTF(env, outline->uri); - if (!juri) return NULL; - } - else - jpage = fz_resolve_link(ctx, doc, outline->uri, &x, &y); + juri = (*env)->NewStringUTF(env, outline->uri); + if (!juri) return NULL; } if (outline->down) @@ -1501,7 +1493,7 @@ static inline jobject to_Outline_safe(fz_context *ctx, JNIEnv *env, fz_document if (!jdown) return NULL; } - joutline = (*env)->NewObject(env, cls_Outline, mid_Outline_init, jtitle, jpage, juri, x, y, jdown); + joutline = (*env)->NewObject(env, cls_Outline, mid_Outline_init, jtitle, juri, jdown); if (!joutline) return NULL; if (jdown) @@ -5252,7 +5244,27 @@ FUN(Document_authenticatePassword)(JNIEnv *env, jobject self, jstring jpassword) } JNIEXPORT jint JNICALL -FUN(Document_countPages)(JNIEnv *env, jobject self) +FUN(Document_countChapters)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + int count = 0; + + if (!ctx || !doc) return 0; + + fz_try(ctx) + count = fz_count_chapters(ctx, doc); + fz_catch(ctx) + { + jni_rethrow(env, ctx); + return 0; + } + + return count; +} + +JNIEXPORT jint JNICALL +FUN(Document_countPages)(JNIEnv *env, jobject self, jint chapter) { fz_context *ctx = get_context(env); fz_document *doc = from_Document(env, self); @@ -5261,7 +5273,7 @@ FUN(Document_countPages)(JNIEnv *env, jobject self) if (!ctx || !doc) return 0; fz_try(ctx) - count = fz_count_pages(ctx, doc); + count = fz_count_chapter_pages(ctx, doc, chapter); fz_catch(ctx) { jni_rethrow(env, ctx); @@ -5306,7 +5318,7 @@ FUN(Document_layout)(JNIEnv *env, jobject self, jfloat w, jfloat h, jfloat em) } JNIEXPORT jobject JNICALL -FUN(Document_loadPage)(JNIEnv *env, jobject self, jint number) +FUN(Document_loadPage)(JNIEnv *env, jobject self, jint chapter, jint number) { fz_context *ctx = get_context(env); fz_document *doc = from_Document(env, self); @@ -5315,7 +5327,7 @@ FUN(Document_loadPage)(JNIEnv *env, jobject self, jint number) if (!ctx || !doc) return NULL; fz_try(ctx) - page = fz_load_page(ctx, doc, number); + page = fz_load_chapter_page(ctx, doc, chapter, number); fz_catch(ctx) { jni_rethrow(env, ctx); @@ -5401,14 +5413,14 @@ FUN(Document_loadOutline)(JNIEnv *env, jobject self) } JNIEXPORT jlong JNICALL -FUN(Document_makeBookmark)(JNIEnv *env, jobject self, jint page) +FUN(Document_makeBookmark)(JNIEnv *env, jobject self, jint chapter, jint page) { fz_context *ctx = get_context(env); fz_document *doc = from_Document(env, self); fz_bookmark mark = 0; fz_try(ctx) - mark = fz_make_bookmark(ctx, doc, page); + mark = fz_make_bookmark(ctx, doc, fz_make_location(chapter, page)); fz_catch(ctx) { jni_rethrow(env, ctx); @@ -5418,22 +5430,52 @@ FUN(Document_makeBookmark)(JNIEnv *env, jobject self, jint page) return mark; } -JNIEXPORT jint JNICALL +JNIEXPORT jobject JNICALL FUN(Document_findBookmark)(JNIEnv *env, jobject self, jlong mark) { fz_context *ctx = get_context(env); fz_document *doc = from_Document(env, self); - int page = -1; + fz_location loc = { -1, -1 }; fz_try(ctx) - page = fz_lookup_bookmark(ctx, doc, mark); + loc = fz_lookup_bookmark(ctx, doc, mark); fz_catch(ctx) { jni_rethrow(env, ctx); - return -1; + return NULL; } - return page; + return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, 0, 0); +} + +JNIEXPORT jobject JNICALL +FUN(Document_resolveLink)(JNIEnv *env, jobject self, jstring juri) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_location loc = { -1, -1 }; + float x = 0, y = 0; + const char *uri = ""; + + if (juri) + { + uri = (*env)->GetStringUTFChars(env, juri, NULL); + if (!uri) + return NULL; + } + + fz_try(ctx) + loc = fz_resolve_link(ctx, doc, uri, &x, &y); + fz_always(ctx) + if (juri) + (*env)->ReleaseStringUTFChars(env, juri, uri); + fz_catch(ctx) + { + jni_rethrow(env, ctx); + return NULL; + } + + return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, x, y); } /* Page interface */ @@ -5758,7 +5800,6 @@ FUN(Page_getLinks)(JNIEnv *env, jobject self) jobject jbounds = NULL; jobject jlink = NULL; jobject juri = NULL; - int page = 0; jbounds = to_Rect_safe(ctx, env, link->rect); if (!jbounds) return NULL; @@ -5768,10 +5809,8 @@ FUN(Page_getLinks)(JNIEnv *env, jobject self) juri = (*env)->NewStringUTF(env, link->uri); if (!juri) return NULL; } - else - page = fz_resolve_link(ctx, link->doc, link->uri, NULL, NULL); - jlink = (*env)->NewObject(env, cls_Link, mid_Link_init, jbounds, page, juri); + jlink = (*env)->NewObject(env, cls_Link, mid_Link_init, jbounds, juri); (*env)->DeleteLocalRef(env, jbounds); if (!jlink) return NULL; if (juri) diff --git a/platform/java/mupdf_native.h b/platform/java/mupdf_native.h index 0ccba08..4b72e9d 100644 --- a/platform/java/mupdf_native.h +++ b/platform/java/mupdf_native.h @@ -484,19 +484,35 @@ JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_fitz_Document_authenticatePass /* * Class: com_artifex_mupdf_fitz_Document - * Method: countPages + * Method: countChapters * Signature: ()I */ -JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_Document_countPages +JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_Document_countChapters (JNIEnv *, jobject); /* * Class: com_artifex_mupdf_fitz_Document + * Method: countPages + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_Document_countPages + (JNIEnv *, jobject, jint); + +/* + * Class: com_artifex_mupdf_fitz_Document * Method: loadPage - * Signature: (I)Lcom/artifex/mupdf/fitz/Page; + * Signature: (II)Lcom/artifex/mupdf/fitz/Page; */ JNIEXPORT jobject JNICALL Java_com_artifex_mupdf_fitz_Document_loadPage - (JNIEnv *, jobject, jint); + (JNIEnv *, jobject, jint, jint); + +/* + * Class: com_artifex_mupdf_fitz_Document + * Method: resolveLink + * Signature: (Ljava/lang/String;)Lcom/artifex/mupdf/fitz/Location; + */ +JNIEXPORT jobject JNICALL Java_com_artifex_mupdf_fitz_Document_resolveLink + (JNIEnv *, jobject, jstring); /* * Class: com_artifex_mupdf_fitz_Document @@ -532,19 +548,19 @@ JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_Document_layout /* * Class: com_artifex_mupdf_fitz_Document - * Method: makeBookmark - * Signature: (I)J + * Method: findBookmark + * Signature: (J)Lcom/artifex/mupdf/fitz/Location; */ -JNIEXPORT jlong JNICALL Java_com_artifex_mupdf_fitz_Document_makeBookmark - (JNIEnv *, jobject, jint); +JNIEXPORT jobject JNICALL Java_com_artifex_mupdf_fitz_Document_findBookmark + (JNIEnv *, jobject, jlong); /* * Class: com_artifex_mupdf_fitz_Document - * Method: findBookmark - * Signature: (J)I + * Method: makeBookmark + * Signature: (II)J */ -JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_Document_findBookmark - (JNIEnv *, jobject, jlong); +JNIEXPORT jlong JNICALL Java_com_artifex_mupdf_fitz_Document_makeBookmark + (JNIEnv *, jobject, jint, jint); /* * Class: com_artifex_mupdf_fitz_Document @@ -2663,46 +2679,6 @@ JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_isEditing JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_setChoiceValue (JNIEnv *, jobject, jstring); -/* - * Class: com_artifex_mupdf_fitz_PDFWidget - * Method: isSigned - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_isSigned - (JNIEnv *, jobject); - -/* - * Class: com_artifex_mupdf_fitz_PDFWidget - * Method: clearSignature - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_clearSignature - (JNIEnv *, jobject); - -/* - * Class: com_artifex_mupdf_fitz_PDFWidget - * Method: signWithPFX - * Signature: ([B[B)V - */ -JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_signWithPFX - (JNIEnv *, jobject, jbyteArray, jbyteArray); - -/* - * Class: com_artifex_mupdf_fitz_PDFWidget - * Method: checkSignatureDigest - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_checkSignatureDigest - (JNIEnv *, jobject); - -/* - * Class: com_artifex_mupdf_fitz_PDFWidget - * Method: checkSignatureCertificate - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_com_artifex_mupdf_fitz_PDFWidget_checkSignatureCertificate - (JNIEnv *, jobject); - #ifdef __cplusplus } #endif diff --git a/platform/java/src/com/artifex/mupdf/fitz/Document.java b/platform/java/src/com/artifex/mupdf/fitz/Document.java index f957ab6..43517f1 100644 --- a/platform/java/src/com/artifex/mupdf/fitz/Document.java +++ b/platform/java/src/com/artifex/mupdf/fitz/Document.java @@ -45,15 +45,122 @@ public class Document public native boolean needsPassword(); public native boolean authenticatePassword(String password); - public native int countPages(); - public native Page loadPage(int number); + + public native int countChapters(); + public native int countPages(int chapter); + public native Page loadPage(int chapter, int number); + + public int countPages() { + int np = 0; + int nc = countChapters(); + for (int i = 0; i < nc; ++i) + np += countPages(i); + return np; + } + + public Page loadPage(Location loc) { + return loadPage(loc.chapter, loc.page); + } + + public Page loadPage(int number) { + int start = 0; + int nc = countChapters(); + for (int i = 0; i < nc; ++i) { + int np = countPages(i); + if (number < start + np) + return loadPage(i, number - start); + start += np; + } + throw new IllegalArgumentException("page number out of range"); + } + + public Location lastPage() { + int nc = countChapters(); + int np = countPages(nc-1); + return new Location(nc-1, np-1); + } + + public Location nextPage(Location loc) { + int np = countPages(loc.chapter); + if (loc.page + 1 == np) { + int nc = countChapters(); + if (loc.chapter + 1 < nc) + return new Location(loc.chapter + 1, 0); + } else { + return new Location(loc.chapter, loc.page + 1); + } + return loc; + } + + public Location previousPage(Location loc) { + if (loc.page == 0) { + if (loc.chapter > 0) { + int np = countPages(loc.chapter - 1); + return new Location(loc.chapter - 1, np - 1); + } + } else { + return new Location(loc.chapter, loc.page - 1); + } + return loc; + } + + public Location clampLocation(Location input) { + int c = input.chapter; + int p = input.page; + int nc = countChapters(); + if (c < 0) c = 0; + if (c >= nc) c = nc - 1; + int np = countPages(c); + if (p < 0) p = 0; + if (p >= np) p = np - 1; + if (input.chapter == c && input.page == p) + return input; + return new Location(c, p); + } + + public Location locationFromPageNumber(int number) { + int i, start = 0, np = 0, nc = countChapters(); + if (number < 0) + number = 0; + for (i = 0; i < nc; ++i) + { + np = countPages(i); + if (number < start + np) + return new Location(i, number - start); + start += np; + } + return new Location(start, np - 1); + } + + public int pageNumberFromLocation(Location loc) { + int nc = countChapters(); + int start = 0; + for (int i = 0; i < nc; ++i) { + if (i == loc.chapter) + return start + loc.page; + start += countPages(i); + } + return -1; + } + + public native Location resolveLink(String uri); + public Location resolveLink(Outline link) { + return resolveLink(link.uri); + } + public Location resolveLink(Link link) { + return resolveLink(link.uri); + } + public native Outline[] loadOutline(); public native String getMetaData(String key); public native boolean isReflowable(); public native void layout(float width, float height, float em); - public native long makeBookmark(int page); - public native int findBookmark(long mark); + public native Location findBookmark(long mark); + public native long makeBookmark(int chapter, int page); + public long makeBookmark(Location loc) { + return makeBookmark(loc.chapter, loc.page); + } public native boolean isUnencryptedPDF(); diff --git a/platform/java/src/com/artifex/mupdf/fitz/Link.java b/platform/java/src/com/artifex/mupdf/fitz/Link.java index 1f7eddb..5e30598 100644 --- a/platform/java/src/com/artifex/mupdf/fitz/Link.java +++ b/platform/java/src/com/artifex/mupdf/fitz/Link.java @@ -3,16 +3,14 @@ package com.artifex.mupdf.fitz; public class Link { public Rect bounds; - public int page; public String uri; - public Link(Rect bounds, int page, String uri) { + public Link(Rect bounds, String uri) { this.bounds = bounds; - this.page = page; this.uri = uri; } public String toString() { - return "Link(b="+bounds+",page="+page+",uri="+uri+")"; + return "Link(bounds="+bounds+",uri="+uri+")"; } } diff --git a/platform/java/src/com/artifex/mupdf/fitz/Location.java b/platform/java/src/com/artifex/mupdf/fitz/Location.java new file mode 100644 index 0000000..0476111 --- /dev/null +++ b/platform/java/src/com/artifex/mupdf/fitz/Location.java @@ -0,0 +1,19 @@ +package com.artifex.mupdf.fitz; + +public final class Location +{ + public final int chapter; + public final int page; + public final float x, y; + public Location(int chapter, int page) { + this.chapter = chapter; + this.page = page; + this.x = this.y = 0; + } + public Location(int chapter, int page, float x, float y) { + this.chapter = chapter; + this.page = page; + this.x = x; + this.y = y; + } +} diff --git a/platform/java/src/com/artifex/mupdf/fitz/Outline.java b/platform/java/src/com/artifex/mupdf/fitz/Outline.java index f230118..13e2a52 100644 --- a/platform/java/src/com/artifex/mupdf/fitz/Outline.java +++ b/platform/java/src/com/artifex/mupdf/fitz/Outline.java @@ -4,26 +4,18 @@ public class Outline { public String title; public String uri; - public int page; public Outline[] down; - public float x; - public float y; - public Outline(String title, int page, String uri, float x, float y, Outline[] down) { + public Outline(String title, String uri, Outline[] down) { this.title = title; - this.page = page; this.uri = uri; this.down = down; - this.x = x; - this.y = y; } public String toString() { StringBuffer s = new StringBuffer(); - s.append(page); - s.append(": "); s.append(title); s.append(' '); s.append(uri); diff --git a/platform/x11/pdfapp.c b/platform/x11/pdfapp.c index ef7d174..b3d6ef4 100644 --- a/platform/x11/pdfapp.c +++ b/platform/x11/pdfapp.c @@ -1230,22 +1230,22 @@ void pdfapp_onkey(pdfapp_t *app, int c, int modifiers) case '<': if (app->layout_em > 6) { - fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, app->pageno); + fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, fz_location_from_page_number(app->ctx, app->doc, app->pageno)); app->layout_em -= 1; fz_layout_document(app->ctx, app->doc, app->layout_w, app->layout_h, app->layout_em); app->pagecount = fz_count_pages(app->ctx, app->doc); - app->pageno = fz_lookup_bookmark(app->ctx, app->doc, mark); + app->pageno = fz_page_number_from_location(app->ctx, app->doc, fz_lookup_bookmark(app->ctx, app->doc, mark)); pdfapp_showpage(app, 1, 1, 1, 0, 0); } break; case '>': if (app->layout_em < 36) { - fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, app->pageno); + fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, fz_location_from_page_number(app->ctx, app->doc, app->pageno)); app->layout_em += 1; fz_layout_document(app->ctx, app->doc, app->layout_w, app->layout_h, app->layout_em); app->pagecount = fz_count_pages(app->ctx, app->doc); - app->pageno = fz_lookup_bookmark(app->ctx, app->doc, mark); + app->pageno = fz_page_number_from_location(app->ctx, app->doc, fz_lookup_bookmark(app->ctx, app->doc, mark)); pdfapp_showpage(app, 1, 1, 1, 0, 0); } break; @@ -1714,7 +1714,10 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta if (fz_is_external_link(ctx, link->uri)) pdfapp_gotouri(app, link->uri); else - pdfapp_gotopage(app, fz_resolve_link(ctx, app->doc, link->uri, NULL, NULL) + 1); + { + fz_location loc = fz_resolve_link(ctx, app->doc, link->uri, NULL, NULL); + pdfapp_gotopage(app, fz_page_number_from_location(ctx, app->doc, loc)+1); + } return; } } diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c index 7040f82..89c687f 100644 --- a/source/cbz/mucbz.c +++ b/source/cbz/mucbz.c @@ -130,7 +130,7 @@ cbz_drop_document(fz_context *ctx, fz_document *doc_) } static int -cbz_count_pages(fz_context *ctx, fz_document *doc_) +cbz_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { cbz_document *doc = (cbz_document*)doc_; return doc->page_count; @@ -175,7 +175,7 @@ cbz_drop_page(fz_context *ctx, fz_page *page_) } static fz_page * -cbz_load_page(fz_context *ctx, fz_document *doc_, int number) +cbz_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { cbz_document *doc = (cbz_document*)doc_; cbz_page *page = NULL; @@ -271,5 +271,7 @@ fz_document_handler cbz_document_handler = NULL, cbz_open_document_with_stream, cbz_extensions, - cbz_mimetypes + cbz_mimetypes, + NULL, + NULL }; diff --git a/source/cbz/muimg.c b/source/cbz/muimg.c index baaef53..600365f 100644 --- a/source/cbz/muimg.c +++ b/source/cbz/muimg.c @@ -30,7 +30,7 @@ img_drop_document(fz_context *ctx, fz_document *doc_) } static int -img_count_pages(fz_context *ctx, fz_document *doc_) +img_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { img_document *doc = (img_document*)doc_; return doc->page_count; @@ -74,7 +74,7 @@ img_drop_page(fz_context *ctx, fz_page *page_) } static fz_page * -img_load_page(fz_context *ctx, fz_document *doc_, int number) +img_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { img_document *doc = (img_document*)doc_; fz_pixmap *pixmap = NULL; @@ -247,5 +247,7 @@ fz_document_handler img_document_handler = NULL, img_open_document_with_stream, img_extensions, - img_mimetypes + img_mimetypes, + NULL, + NULL }; diff --git a/source/fitz/document.c b/source/fitz/document.c index a4c5f34..c627749 100644 --- a/source/fitz/document.c +++ b/source/fitz/document.c @@ -150,7 +150,7 @@ extern fz_document_handler pdf_document_handler; magic: a string used to detect document type; either a file name or mime-type. */ fz_document * -fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream) +fz_open_accelerated_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel) { const fz_document_handler *handler; @@ -164,13 +164,36 @@ fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stre #else fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find document handler for file type: %s", magic); #endif - + if (handler->open_accel_with_stream) + if (accel || handler->open_with_stream == NULL) + return handler->open_accel_with_stream(ctx, stream, accel); + if (accel) + { + /* We've had an accelerator passed to a format that doesn't + * handle it. This should never happen, as how did the + * accelerator get created? */ + fz_drop_stream(ctx, accel); + } return handler->open_with_stream(ctx, stream); } /* Open a PDF, XPS or CBZ document. + Open a document using the specified stream object rather than + opening a file on disk. + + magic: a string used to detect document type; either a file name or mime-type. +*/ +fz_document * +fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream) +{ + return fz_open_accelerated_document_with_stream(ctx, magic, stream, NULL); +} + +/* + Open a PDF, XPS or CBZ document. + Open a document file and read its basic structure so pages and objects can be located. MuPDF will try to repair broken documents (without actually changing the file contents). @@ -181,12 +204,15 @@ fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stre filename: a path to a file as it would be given to open(2). */ fz_document * -fz_open_document(fz_context *ctx, const char *filename) +fz_open_accelerated_document(fz_context *ctx, const char *filename, const char *accel) { const fz_document_handler *handler; fz_stream *file; + fz_stream *afile = NULL; fz_document *doc = NULL; + fz_var(afile); + if (filename == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "no document to open"); @@ -198,21 +224,91 @@ fz_open_document(fz_context *ctx, const char *filename) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find document handler for file: %s", filename); #endif - if (handler->open) + if (accel) { + if (handler->open_accel) + return handler->open_accel(ctx, filename, accel); + if (handler->open_accel_with_stream == NULL) + { + /* We're not going to be able to use the accelerator - this + * should never happen, as how can one have been created? */ + accel = NULL; + } + } + if (!accel && handler->open) return handler->open(ctx, filename); file = fz_open_file(ctx, filename); fz_try(ctx) - doc = handler->open_with_stream(ctx, file); + { + if (accel || handler->open_with_stream == NULL) + { + if (accel) + afile = fz_open_file(ctx, accel); + doc = handler->open_accel_with_stream(ctx, file, afile); + } + else + doc = handler->open_with_stream(ctx, file); + } fz_always(ctx) + { + fz_drop_stream(ctx, afile); fz_drop_stream(ctx, file); + } fz_catch(ctx) fz_rethrow(ctx); return doc; } +/* + Open a PDF, XPS or CBZ document. + + Open a document file and read its basic structure so pages and + objects can be located. MuPDF will try to repair broken + documents (without actually changing the file contents). + + The returned fz_document is used when calling most other + document related functions. + + filename: a path to a file as it would be given to open(2). +*/ +fz_document * +fz_open_document(fz_context *ctx, const char *filename) +{ + return fz_open_accelerated_document(ctx, filename, NULL); +} + +void fz_save_accelerator(fz_context *ctx, fz_document *doc, const char *accel) +{ + if (doc == NULL) + return; + if (doc->output_accelerator == NULL) + return; + + fz_output_accelerator(ctx, doc, fz_new_output_with_path(ctx, accel, 0)); +} + +void fz_output_accelerator(fz_context *ctx, fz_document *doc, fz_output *accel) +{ + if (doc == NULL || accel == NULL) + return; + if (doc->output_accelerator == NULL) + { + fz_drop_output(ctx, accel); + fz_throw(ctx, FZ_ERROR_GENERIC, "Document does not support writing an accelerator"); + } + + doc->output_accelerator(ctx, doc, accel); +} + +int fz_document_supports_accelerator(fz_context *ctx, fz_document *doc) +{ + if (doc == NULL) + return 0; + return (doc->output_accelerator) != NULL; +} + void * fz_new_document_of_size(fz_context *ctx, int size) { @@ -264,21 +360,21 @@ fz_is_document_reflowable(fz_context *ctx, fz_document *doc) same location after the document has been laid out with different parameters. */ -fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, int page) +fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, fz_location loc) { if (doc && doc->make_bookmark) - return doc->make_bookmark(ctx, doc, page); - return (fz_bookmark)page; + return doc->make_bookmark(ctx, doc, loc); + return (loc.chapter<<16) + loc.page; } /* Find a bookmark and return its page number. */ -int fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark) +fz_location fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark) { if (doc && doc->lookup_bookmark) return doc->lookup_bookmark(ctx, doc, mark); - return (int)mark; + return fz_make_location((mark>>16) & 0xffff, mark & 0xffff); } /* @@ -348,9 +444,9 @@ fz_load_outline(fz_context *ctx, fz_document *doc) xp, yp: Pointer to store coordinate of destination on the page. - Returns -1 if the URI cannot be resolved. + Returns (-1,-1) if the URI cannot be resolved. */ -int +fz_location fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp) { fz_ensure_layout(ctx, doc); @@ -358,7 +454,7 @@ fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, f if (yp) *yp = 0; if (doc && doc->resolve_link) return doc->resolve_link(ctx, doc, uri, xp, yp); - return -1; + return fz_make_location(-1, -1); } /* @@ -378,19 +474,145 @@ fz_layout_document(fz_context *ctx, fz_document *doc, float w, float h, float em } /* - Return the number of pages in document + Return the number of chapters in the document. + At least 1. +*/ +int +fz_count_chapters(fz_context *ctx, fz_document *doc) +{ + fz_ensure_layout(ctx, doc); + if (doc && doc->count_chapters) + return doc->count_chapters(ctx, doc); + return 1; +} - May return 0 for documents with no pages. +/* + Return the number of pages in a chapter. + May return 0. */ int -fz_count_pages(fz_context *ctx, fz_document *doc) +fz_count_chapter_pages(fz_context *ctx, fz_document *doc, int chapter) { fz_ensure_layout(ctx, doc); if (doc && doc->count_pages) - return doc->count_pages(ctx, doc); + return doc->count_pages(ctx, doc, chapter); return 0; } + +/* + Return the number of pages in document + + May return 0 for documents with no pages. +*/ +int +fz_count_pages(fz_context *ctx, fz_document *doc) +{ + int i, c, n = 0; + c = fz_count_chapters(ctx, doc); + for (i = 0; i < c; ++i) + n += fz_count_chapter_pages(ctx, doc, i); + return n; +} + +fz_page * +fz_load_page(fz_context *ctx, fz_document *doc, int number) +{ + int i, n = fz_count_chapters(ctx, doc); + int start = 0; + for (i = 0; i < n; ++i) + { + int m = fz_count_chapter_pages(ctx, doc, i); + if (number < start + m) + return fz_load_chapter_page(ctx, doc, i, number - start); + start += m; + } + fz_throw(ctx, FZ_ERROR_GENERIC, "Page not found: %d", number+1); +} + +fz_location fz_last_page(fz_context *ctx, fz_document *doc) +{ + int nc = fz_count_chapters(ctx, doc); + int np = fz_count_chapter_pages(ctx, doc, nc-1); + return fz_make_location(nc-1, np-1); +} + +fz_location fz_next_page(fz_context *ctx, fz_document *doc, fz_location loc) +{ + int nc = fz_count_chapters(ctx, doc); + int np = fz_count_chapter_pages(ctx, doc, loc.chapter); + if (loc.page + 1 == np) + { + if (loc.chapter + 1 < nc) + { + return fz_make_location(loc.chapter + 1, 0); + } + } + else + { + return fz_make_location(loc.chapter, loc.page + 1); + } + return loc; +} + +fz_location fz_previous_page(fz_context *ctx, fz_document *doc, fz_location loc) +{ + if (loc.page == 0) + { + if (loc.chapter > 0) + { + int np = fz_count_chapter_pages(ctx, doc, loc.chapter - 1); + return fz_make_location(loc.chapter - 1, np - 1); + } + } + else + { + return fz_make_location(loc.chapter, loc.page - 1); + } + return loc; +} + +fz_location fz_clamp_location(fz_context *ctx, fz_document *doc, fz_location loc) +{ + int nc = fz_count_chapters(ctx, doc); + int np; + if (loc.chapter < 0) loc.chapter = 0; + if (loc.chapter >= nc) loc.chapter = nc - 1; + np = fz_count_chapter_pages(ctx, doc, loc.chapter); + if (loc.page < 0) loc.page = 0; + if (loc.page >= np) loc.page = np - 1; + return loc; +} + +fz_location fz_location_from_page_number(fz_context *ctx, fz_document *doc, int number) +{ + int i, m = 0, n = fz_count_chapters(ctx, doc); + int start = 0; + if (number < 0) + number = 0; + for (i = 0; i < n; ++i) + { + m = fz_count_chapter_pages(ctx, doc, i); + if (number < start + m) + return fz_make_location(i, number - start); + start += m; + } + return fz_make_location(start, m-1); +} + +int fz_page_number_from_location(fz_context *ctx, fz_document *doc, fz_location loc) +{ + int i, n, start = 0; + n = fz_count_chapters(ctx, doc); + for (i = 0; i < n; ++i) + { + if (i == loc.chapter) + return start + loc.page; + start += fz_count_chapter_pages(ctx, doc, i); + } + return -1; +} + /* Retrieve document meta data strings. @@ -447,10 +669,11 @@ fz_document_output_intent(fz_context *ctx, fz_document *doc) page using fz_bound_page, or to render the page using fz_run_page_*. Free the page by calling fz_drop_page. - number: page number, 0 is the first page of the document. + chapter: chapter number, 0 is the first chapter of the document. + number: page number, 0 is the first page of the chapter. */ fz_page * -fz_load_page(fz_context *ctx, fz_document *doc, int number) +fz_load_chapter_page(fz_context *ctx, fz_document *doc, int chapter, int number) { fz_page *page; @@ -458,12 +681,13 @@ fz_load_page(fz_context *ctx, fz_document *doc, int number) if (doc) for (page = doc->open; page; page = page->next) - if (page->number == number) + if (page->chapter == chapter && page->number == number) return fz_keep_page(ctx, page); if (doc && doc->load_page) { - page = doc->load_page(ctx, doc, number); + page = doc->load_page(ctx, doc, chapter, number); + page->chapter = chapter; page->number = number; /* Insert new page at the head of the list of open pages. */ diff --git a/source/fitz/util.c b/source/fitz/util.c index e20da80..fb8ca5b 100644 --- a/source/fitz/util.c +++ b/source/fitz/util.c @@ -369,6 +369,22 @@ fz_search_page_number(fz_context *ctx, fz_document *doc, int number, const char return count; } +int +fz_search_chapter_page_number(fz_context *ctx, fz_document *doc, int chapter, int number, const char *needle, fz_quad *hit_bbox, int hit_max) +{ + fz_page *page; + int count = 0; + + page = fz_load_chapter_page(ctx, doc, chapter, number); + fz_try(ctx) + count = fz_search_page(ctx, page, needle, hit_bbox, hit_max); + fz_always(ctx) + fz_drop_page(ctx, page); + fz_catch(ctx) + fz_rethrow(ctx); + return count; +} + /* Convert structured text into plain text. */ diff --git a/source/fitz/xml.c b/source/fitz/xml.c index 9279267..d1a5ec8 100644 --- a/source/fitz/xml.c +++ b/source/fitz/xml.c @@ -472,7 +472,6 @@ static void xml_emit_close_tag(fz_context *ctx, struct parser *parser) static void xml_emit_text(fz_context *ctx, struct parser *parser, char *a, char *b) { - static char *empty = ""; fz_xml *head; char *s; int c; @@ -512,7 +511,6 @@ static void xml_emit_text(fz_context *ctx, struct parser *parser, char *a, char static void xml_emit_cdata(fz_context *ctx, struct parser *parser, char *a, char *b) { - static char *empty = ""; fz_xml *head; char *s; diff --git a/source/html/epub-doc.c b/source/html/epub-doc.c index 5c283df..2135aca 100644 --- a/source/html/epub-doc.c +++ b/source/html/epub-doc.c @@ -3,12 +3,16 @@ #include <string.h> #include <math.h> +#include <assert.h> + +#include <zlib.h> /* for crc32 */ enum { T, R, B, L }; typedef struct epub_document_s epub_document; typedef struct epub_chapter_s epub_chapter; typedef struct epub_page_s epub_page; +typedef struct epub_accelerator_s epub_accelerator; struct epub_document_s { @@ -19,13 +23,26 @@ struct epub_document_s epub_chapter *spine; fz_outline *outline; char *dc_title, *dc_creator; + float layout_w, layout_h, layout_em; + epub_accelerator *accel; + uint32_t css_sum; + + /* A common pattern of use is for us to open a document, + * load a page, draw it, drop it, load the next page, + * draw it, drop it etc. This means that the HTML for + * a chapter might get thrown away between the drop and + * the the next load (if the chapter is large, and the + * store size is low). Accordingly, we store a handle + * to the most recently used html block here, thus + * ensuring that the stored copy won't be evicted. */ + fz_html *most_recent_html; }; struct epub_chapter_s { + epub_document *doc; char *path; - int start; - fz_html *html; + int number; epub_chapter *next; }; @@ -33,140 +50,392 @@ struct epub_page_s { fz_page super; epub_document *doc; + epub_chapter *ch; int number; + fz_html *html; +}; + +struct epub_accelerator_s +{ + int max_chapters; + int num_chapters; + float layout_w; + float layout_h; + float layout_em; + uint32_t css_sum; + int use_doc_css; + int *pages_in_chapter; }; -static int count_chapter_pages(epub_chapter *ch) +static uint32_t +user_css_sum(fz_context *ctx) { - if (ch->html->root->b > 0) - return ceilf(ch->html->root->b / ch->html->page_h); + uint32_t sum = 0; + const char *css = fz_user_css(ctx); + sum = crc32(0, NULL, 0); + if (css) + sum = crc32(sum, (Byte*)css, strlen(css)); + return sum; +} + +static fz_html *epub_get_laid_out_html(fz_context *ctx, epub_document *doc, epub_chapter *ch); + +static int count_laid_out_pages(fz_html *html) +{ + if (html->root->b > 0) + return ceilf(html->root->b / html->page_h); return 1; } -static int +static void +invalidate_accelerator(fz_context *ctx, epub_accelerator *acc) +{ + int i; + + for (i = 0; i < acc->max_chapters; i++) + acc->pages_in_chapter[i] = -1; +} + +static int count_chapter_pages(fz_context *ctx, epub_document *doc, epub_chapter *ch) +{ + epub_accelerator *acc = doc->accel; + int use_doc_css = fz_use_document_css(ctx); + + if (use_doc_css != acc->use_doc_css || doc->css_sum != acc->css_sum) + { + acc->use_doc_css = use_doc_css; + acc->css_sum = doc->css_sum; + invalidate_accelerator(ctx, acc); + } + + if (ch->number < acc->num_chapters && acc->pages_in_chapter[ch->number] != -1) + return acc->pages_in_chapter[ch->number]; + + fz_drop_html(ctx, epub_get_laid_out_html(ctx, doc, ch)); + return acc->pages_in_chapter[ch->number]; +} + +static fz_location epub_resolve_link(fz_context *ctx, fz_document *doc_, const char *dest, float *xp, float *yp) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; + int i; const char *s = strchr(dest, '#'); size_t n = s ? (size_t)(s - dest) : strlen(dest); if (s && s[1] == 0) s = NULL; - for (ch = doc->spine; ch; ch = ch->next) + for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next) { if (!strncmp(ch->path, dest, n) && ch->path[n] == 0) { if (s) { + float y; + fz_html *html = epub_get_laid_out_html(ctx, doc, ch); + int ph = html->page_h; + /* Search for a matching fragment */ - float y = fz_find_html_target(ctx, ch->html, s+1); + y = fz_find_html_target(ctx, html, s+1); + fz_drop_html(ctx, html); if (y >= 0) { - int page = y / ch->html->page_h; - if (yp) *yp = y - page * ch->html->page_h; - return ch->start + page; + int page = y / ph; + if (yp) *yp = y - page * ph; + return fz_make_location(i, page); } - return -1; + return fz_make_location(-1, -1); } - return ch->start; + return fz_make_location(i, 0); } } - return -1; + return fz_make_location(-1, -1); } static void -epub_update_outline(fz_context *ctx, fz_document *doc, fz_outline *node) +epub_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em) { - while (node) - { - node->page = epub_resolve_link(ctx, doc, node->uri, &node->x, &node->y); - epub_update_outline(ctx, doc, node->down); - node = node->next; - } + epub_document *doc = (epub_document*)doc_; + uint32_t css_sum = user_css_sum(ctx); + int use_doc_css = fz_use_document_css(ctx); + + if (doc->layout_w == w && doc->layout_h == h && doc->layout_em == em && doc->css_sum == css_sum) + return; + doc->layout_w = w; + doc->layout_h = h; + doc->layout_em = em; + + if (doc->accel == NULL) + return; + + /* When we load the saved accelerator, doc->accel + * can be populated with different values than doc. + * This is really useful as doc starts out with the + * values being 0. If we've got the right values + * already, then don't bin the data! */ + if (doc->accel->layout_w == w && + doc->accel->layout_h == h && + doc->accel->layout_em == em && + doc->accel->use_doc_css == use_doc_css && + doc->accel->css_sum == css_sum) + return; + + doc->accel->layout_w = w; + doc->accel->layout_h = h; + doc->accel->layout_em = em; + doc->accel->use_doc_css = use_doc_css; + doc->accel->css_sum = css_sum; + invalidate_accelerator(ctx, doc->accel); } -static void -epub_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em) +static int +epub_count_chapters(fz_context *ctx, fz_document *doc_) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; int count = 0; - for (ch = doc->spine; ch; ch = ch->next) - { - ch->start = count; - fz_layout_html(ctx, ch->html, w, h, em); - count += count_chapter_pages(ch); - } - - epub_update_outline(ctx, doc_, doc->outline); + ++count; + return count; } static int -epub_count_pages(fz_context *ctx, fz_document *doc_) +epub_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; - int count = 0; - for (ch = doc->spine; ch; ch = ch->next) - count += count_chapter_pages(ch); - return count; + int i; + for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next) + { + if (i == chapter) + { + return count_chapter_pages(ctx, doc, ch); + } + } + return 0; +} + +#define MAGIC_ACCELERATOR 0xacce1e7a +#define MAGIC_ACCEL_EPUB 0x62755065 +#define ACCEL_VERSION 0x00010001 + +static void epub_load_accelerator(fz_context *ctx, epub_document *doc, fz_stream *accel) +{ + int v; + float w, h, em; + int num_chapters; + epub_accelerator *acc = NULL; + uint32_t css_sum; + int use_doc_css; + int make_new = (accel == NULL); + + fz_var(acc); + + if (accel) + { + /* Try to read the accelerator data. If we fail silently give up. */ + fz_try(ctx) + { + v = fz_read_int32_le(ctx, accel); + if (v != (int32_t)MAGIC_ACCELERATOR) + { + make_new = 1; + break; + } + + v = fz_read_int32_le(ctx, accel); + if (v != MAGIC_ACCEL_EPUB) + { + make_new = 1; + break; + } + + v = fz_read_int32_le(ctx, accel); + if (v != ACCEL_VERSION) + { + make_new = 1; + break; + } + + w = fz_read_float_le(ctx, accel); + h = fz_read_float_le(ctx, accel); + em = fz_read_float_le(ctx, accel); + css_sum = fz_read_uint32_le(ctx, accel); + use_doc_css = fz_read_int32_le(ctx, accel); + + num_chapters = fz_read_int32_le(ctx, accel); + if (num_chapters <= 0) + { + make_new = 1; + break; + } + + acc = fz_malloc_struct(ctx, epub_accelerator); + acc->pages_in_chapter = fz_malloc_array(ctx, num_chapters, int); + acc->max_chapters = acc->num_chapters = num_chapters; + acc->layout_w = w; + acc->layout_h = h; + acc->layout_em = em; + acc->css_sum = css_sum; + acc->use_doc_css = use_doc_css; + + for (v = 0; v < num_chapters; v++) + acc->pages_in_chapter[v] = fz_read_int32_le(ctx, accel); + } + fz_catch(ctx) + { + if (acc) + fz_free(ctx, acc->pages_in_chapter); + fz_free(ctx, acc); + /* Swallow the error and run unaccelerated */ + make_new = 1; + } + } + + /* If we aren't given an accelerator to load (or the one we're given + * is bad) create a blank stub and we can fill it out as we go. */ + if (make_new) + { + acc = fz_malloc_struct(ctx, epub_accelerator); + acc->css_sum = doc->css_sum; + acc->use_doc_css = fz_use_document_css(ctx); + } + + doc->accel = acc; +} + +static void +accelerate_chapter(fz_context *ctx, epub_document *doc, epub_chapter *ch, fz_html *html) +{ + epub_accelerator *acc = doc->accel; + int p = count_laid_out_pages(html); + + if (ch->number < acc->num_chapters) + { + assert(acc->pages_in_chapter[ch->number] == p || acc->pages_in_chapter[ch->number] == -1); + acc->pages_in_chapter[ch->number] = p; + return; + } + + if (ch->number >= acc->max_chapters) + { + int n = acc->max_chapters * 2; + int i; + if (n == 0) + n = 4; + while (n < ch->number) + n *= 2; + + acc->pages_in_chapter = fz_realloc_array(ctx, acc->pages_in_chapter, n, int); + for (i = acc->max_chapters; i < n; i++) + acc->pages_in_chapter[i] = -1; + acc->max_chapters = n; + } + acc->pages_in_chapter[ch->number] = p; + if (acc->num_chapters < ch->number+1) + acc->num_chapters = ch->number+1; } static void epub_drop_page(fz_context *ctx, fz_page *page_) { + epub_page *page = (epub_page *)page_; + fz_drop_document(ctx, &page->doc->super); + fz_drop_html(ctx, page->html); } -static fz_rect -epub_bound_page(fz_context *ctx, fz_page *page_) +static epub_chapter * +epub_load_chapter(fz_context *ctx, epub_document *doc, const char *path, int i) { - epub_page *page = (epub_page*)page_; - epub_document *doc = page->doc; epub_chapter *ch; - int n = page->number; - int count = 0; - fz_rect bbox; - for (ch = doc->spine; ch; ch = ch->next) + ch = fz_malloc_struct(ctx, epub_chapter); + fz_try(ctx) { - int cn = count_chapter_pages(ch); - if (n < count + cn) - { - bbox.x0 = 0; - bbox.y0 = 0; - bbox.x1 = ch->html->page_w + ch->html->page_margin[L] + ch->html->page_margin[R]; - bbox.y1 = ch->html->page_h + ch->html->page_margin[T] + ch->html->page_margin[B]; - return bbox; - } - count += cn; + ch->path = Memento_label(fz_strdup(ctx, path), "chapter_path"); + ch->number = i; + } + fz_catch(ctx) + { + fz_free(ctx, ch); + fz_rethrow(ctx); + } + + return ch; +} + +static fz_html * +epub_parse_chapter(fz_context *ctx, epub_document *doc, epub_chapter *ch) +{ + fz_archive *zip = doc->zip; + fz_buffer *buf; + char base_uri[2048]; + fz_html *html; + + /* Look for one we made earlier */ + html = fz_find_html(ctx, doc, ch->number); + if (html) + return html; + + fz_dirname(base_uri, ch->path, sizeof base_uri); + + buf = fz_read_archive_entry(ctx, zip, ch->path); + fz_try(ctx) + html = fz_parse_html(ctx, doc->set, zip, base_uri, buf, fz_user_css(ctx)); + fz_always(ctx) + fz_drop_buffer(ctx, buf); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_store_html(ctx, html, doc, ch->number); +} + +static fz_html * +epub_get_laid_out_html(fz_context *ctx, epub_document *doc, epub_chapter *ch) +{ + fz_html *html = epub_parse_chapter(ctx, doc, ch); + fz_try(ctx) + { + fz_layout_html(ctx, html, doc->layout_w, doc->layout_h, doc->layout_em); + accelerate_chapter(ctx, doc, ch, html); + } + fz_catch(ctx) + { + fz_drop_html(ctx, html); + fz_rethrow(ctx); } - return fz_unit_rect; + fz_drop_html(ctx, doc->most_recent_html); + doc->most_recent_html = fz_keep_html(ctx, html); + + return html; +} + +static fz_rect +epub_bound_page(fz_context *ctx, fz_page *page_) +{ + epub_page *page = (epub_page*)page_; + epub_chapter *ch = page->ch; + fz_rect bbox; + fz_html *html = epub_get_laid_out_html(ctx, page->doc, ch); + + bbox.x0 = 0; + bbox.y0 = 0; + bbox.x1 = html->page_w + html->page_margin[L] + html->page_margin[R]; + bbox.y1 = html->page_h + html->page_margin[T] + html->page_margin[B]; + fz_drop_html(ctx, html); + return bbox; } static void epub_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) { epub_page *page = (epub_page*)page_; - epub_document *doc = page->doc; - epub_chapter *ch; - int n = page->number; - int count = 0; - for (ch = doc->spine; ch; ch = ch->next) - { - int cn = count_chapter_pages(ch); - if (n < count + cn) - { - fz_draw_html(ctx, dev, ctm, ch->html, n-count); - break; - } - count += cn; - } + fz_draw_html(ctx, dev, ctm, page->html, page->number); } static fz_link * @@ -174,66 +443,83 @@ epub_load_links(fz_context *ctx, fz_page *page_) { epub_page *page = (epub_page*)page_; epub_document *doc = page->doc; - epub_chapter *ch; - int n = page->number; - int count = 0; + epub_chapter *ch = page->ch; - for (ch = doc->spine; ch; ch = ch->next) - { - int cn = count_chapter_pages(ch); - if (n < count + cn) - return fz_load_html_links(ctx, ch->html, n - count, ch->path, doc); - count += cn; - } - - return NULL; + return fz_load_html_links(ctx, page->html, page->number, ch->path, doc); } static fz_bookmark -epub_make_bookmark(fz_context *ctx, fz_document *doc_, int n) +epub_make_bookmark(fz_context *ctx, fz_document *doc_, fz_location loc) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; - int count = 0; + int i; - for (ch = doc->spine; ch; ch = ch->next) + for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next) { - int cn = count_chapter_pages(ch); - if (n < count + cn) - return fz_make_html_bookmark(ctx, ch->html, n - count); - count += cn; + if (i == loc.chapter) + { + fz_html *html = epub_get_laid_out_html(ctx, doc, ch); + fz_bookmark mark = fz_make_html_bookmark(ctx, html, loc.page); + fz_drop_html(ctx, html); + return mark; + } } return 0; } -static int +static fz_location epub_lookup_bookmark(fz_context *ctx, fz_document *doc_, fz_bookmark mark) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; + int i; - for (ch = doc->spine; ch; ch = ch->next) + for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next) { - int p = fz_lookup_html_bookmark(ctx, ch->html, mark); + fz_html *html = epub_get_laid_out_html(ctx, doc, ch); + int p = fz_lookup_html_bookmark(ctx, html, mark); + fz_drop_html(ctx, html); if (p != -1) - return ch->start + p; + return fz_make_location(i, p); } - return -1; + return fz_make_location(-1, -1); } static fz_page * -epub_load_page(fz_context *ctx, fz_document *doc_, int number) +epub_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { epub_document *doc = (epub_document*)doc_; - epub_page *page = fz_new_derived_page(ctx, epub_page); - page->super.bound_page = epub_bound_page; - page->super.run_page_contents = epub_run_page; - page->super.load_links = epub_load_links; - page->super.drop_page = epub_drop_page; - page->doc = doc; - page->number = number; - return (fz_page*)page; + epub_chapter *ch; + int i; + for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next) + { + if (i == chapter) + { + epub_page *page = fz_new_derived_page(ctx, epub_page); + page->super.bound_page = epub_bound_page; + page->super.run_page_contents = epub_run_page; + page->super.load_links = epub_load_links; + page->super.drop_page = epub_drop_page; + page->doc = (epub_document *)fz_keep_document(ctx, doc_); + page->ch = ch; + page->number = number; + page->html = epub_get_laid_out_html(ctx, doc, ch); + return (fz_page*)page; + } + } + return NULL; +} + +static void +epub_drop_accelerator(fz_context *ctx, epub_accelerator *acc) +{ + if (acc == NULL) + return; + + fz_free(ctx, acc->pages_in_chapter); + fz_free(ctx, acc); } static void @@ -245,16 +531,18 @@ epub_drop_document(fz_context *ctx, fz_document *doc_) while (ch) { next = ch->next; - fz_drop_html(ctx, ch->html); fz_free(ctx, ch->path); fz_free(ctx, ch); ch = next; } + epub_drop_accelerator(ctx, doc->accel); fz_drop_archive(ctx, doc->zip); fz_drop_html_font_set(ctx, doc->set); fz_drop_outline(ctx, doc->outline); fz_free(ctx, doc->dc_title); fz_free(ctx, doc->dc_creator); + fz_drop_html(ctx, doc->most_recent_html); + fz_purge_stored_html(ctx, doc); } static const char * @@ -289,42 +577,6 @@ path_from_idref(char *path, fz_xml *manifest, const char *base_uri, const char * return fz_cleanname(fz_urldecode(path)); } -static epub_chapter * -epub_parse_chapter(fz_context *ctx, epub_document *doc, const char *path) -{ - fz_archive *zip = doc->zip; - fz_buffer *buf = NULL; - epub_chapter *ch; - char base_uri[2048]; - - fz_dirname(base_uri, path, sizeof base_uri); - - ch = fz_malloc_struct(ctx, epub_chapter); - ch->path = NULL; - ch->html = NULL; - ch->next = NULL; - - fz_var(buf); - - fz_try(ctx) - { - buf = fz_read_archive_entry(ctx, zip, path); - ch->path = Memento_label(fz_strdup(ctx, path), "chapter_path"); - ch->html = fz_parse_html(ctx, doc->set, zip, base_uri, buf, fz_user_css(ctx)); - } - fz_always(ctx) - fz_drop_buffer(ctx, buf); - fz_catch(ctx) - { - fz_drop_html(ctx, ch->html); - fz_free(ctx, ch->path); - fz_free(ctx, ch); - fz_rethrow(ctx); - } - - return ch; -} - static fz_outline * epub_parse_ncx_imp(fz_context *ctx, epub_document *doc, fz_xml *node, char *base_uri) { @@ -419,6 +671,7 @@ epub_parse_header(fz_context *ctx, epub_document *doc) const char *version; char ncx[2048], s[2048]; epub_chapter **tailp; + int i; if (fz_has_archive_entry(ctx, zip, "META-INF/rights.xml")) fz_throw(ctx, FZ_ERROR_GENERIC, "EPUB is locked by DRM"); @@ -477,14 +730,16 @@ epub_parse_header(fz_context *ctx, epub_document *doc) doc->spine = NULL; tailp = &doc->spine; itemref = fz_xml_find_down(spine, "itemref"); + i = 0; while (itemref) { if (path_from_idref(s, manifest, base_uri, fz_xml_att(itemref, "idref"), sizeof s)) { fz_try(ctx) { - *tailp = epub_parse_chapter(ctx, doc, s); + *tailp = epub_load_chapter(ctx, doc, s, i); tailp = &(*tailp)->next; + i++; } fz_catch(ctx) { @@ -525,8 +780,39 @@ epub_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char * return -1; } +static void +epub_output_accelerator(fz_context *ctx, fz_document *doc_, fz_output *out) +{ + epub_document *doc = (epub_document*)doc_; + int i; + + fz_try(ctx) + { + if (doc->accel == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "No accelerator data to write"); + + fz_write_int32_le(ctx, out, MAGIC_ACCELERATOR); + fz_write_int32_le(ctx, out, MAGIC_ACCEL_EPUB); + fz_write_int32_le(ctx, out, ACCEL_VERSION); + fz_write_float_le(ctx, out, doc->accel->layout_w); + fz_write_float_le(ctx, out, doc->accel->layout_h); + fz_write_float_le(ctx, out, doc->accel->layout_em); + fz_write_uint32_le(ctx, out, doc->accel->css_sum); + fz_write_int32_le(ctx, out, doc->accel->use_doc_css); + fz_write_int32_le(ctx, out, doc->accel->num_chapters); + for (i = 0; i < doc->accel->num_chapters; i++) + fz_write_int32_le(ctx, out, doc->accel->pages_in_chapter[i]); + + fz_close_output(ctx, out); + } + fz_always(ctx) + fz_drop_output(ctx, out); + fz_catch(ctx) + fz_rethrow(ctx); +} + static fz_document * -epub_init(fz_context *ctx, fz_archive *zip) +epub_init(fz_context *ctx, fz_archive *zip, fz_stream *accel) { epub_document *doc; @@ -540,15 +826,22 @@ epub_init(fz_context *ctx, fz_archive *zip) doc->super.resolve_link = epub_resolve_link; doc->super.make_bookmark = epub_make_bookmark; doc->super.lookup_bookmark = epub_lookup_bookmark; + doc->super.count_chapters = epub_count_chapters; doc->super.count_pages = epub_count_pages; doc->super.load_page = epub_load_page; doc->super.lookup_metadata = epub_lookup_metadata; + doc->super.output_accelerator = epub_output_accelerator; doc->super.is_reflowable = 1; + doc->css_sum = user_css_sum(ctx); + fz_try(ctx) { + epub_load_accelerator(ctx, doc, accel); epub_parse_header(ctx, doc); } + fz_always(ctx) + fz_drop_stream(ctx, accel); fz_catch(ctx) { fz_drop_document(ctx, &doc->super); @@ -559,14 +852,18 @@ epub_init(fz_context *ctx, fz_archive *zip) } static fz_document * -epub_open_document_with_stream(fz_context *ctx, fz_stream *file) +epub_open_document_with_stream(fz_context *ctx, fz_stream *file, fz_stream *accel) { - return epub_init(ctx, fz_open_zip_archive_with_stream(ctx, file)); + return epub_init(ctx, fz_open_zip_archive_with_stream(ctx, file), accel); } static fz_document * -epub_open_document(fz_context *ctx, const char *filename) +epub_open_document(fz_context *ctx, const char *filename, const char *accel) { + fz_stream *afile = NULL; + + if (accel) + afile = fz_open_file(ctx, accel); if (strstr(filename, "META-INF/container.xml") || strstr(filename, "META-INF\\container.xml")) { char dirname[2048], *p; @@ -575,10 +872,10 @@ epub_open_document(fz_context *ctx, const char *filename) *p = 0; if (!dirname[0]) fz_strlcpy(dirname, ".", sizeof dirname); - return epub_init(ctx, fz_open_directory(ctx, dirname)); + return epub_init(ctx, fz_open_directory(ctx, dirname), afile); } - return epub_init(ctx, fz_open_zip_archive(ctx, filename)); + return epub_init(ctx, fz_open_zip_archive(ctx, filename), afile); } static int @@ -604,8 +901,10 @@ static const char *epub_mimetypes[] = fz_document_handler epub_document_handler = { epub_recognize, - epub_open_document, - epub_open_document_with_stream, + NULL, + NULL, epub_extensions, - epub_mimetypes + epub_mimetypes, + epub_open_document, + epub_open_document_with_stream }; diff --git a/source/html/html-doc.c b/source/html/html-doc.c index 94b2975..1a49cf8 100644 --- a/source/html/html-doc.c +++ b/source/html/html-doc.c @@ -35,7 +35,7 @@ htdoc_drop_document(fz_context *ctx, fz_document *doc_) fz_drop_outline(ctx, doc->outline); } -static int +static fz_location htdoc_resolve_link(fz_context *ctx, fz_document *doc_, const char *dest, float *xp, float *yp) { html_document *doc = (html_document*)doc_; @@ -47,15 +47,15 @@ htdoc_resolve_link(fz_context *ctx, fz_document *doc_, const char *dest, float * { int page = y / doc->html->page_h; if (yp) *yp = y - page * doc->html->page_h; - return page; + return fz_make_location(0, page); } } - return -1; + return fz_make_location(-1, -1); } static int -htdoc_count_pages(fz_context *ctx, fz_document *doc_) +htdoc_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { html_document *doc = (html_document*)doc_; if (doc->html->root->b > 0) @@ -68,7 +68,7 @@ htdoc_update_outline(fz_context *ctx, fz_document *doc, fz_outline *node) { while (node) { - node->page = htdoc_resolve_link(ctx, doc, node->uri, &node->x, &node->y); + node->page = htdoc_resolve_link(ctx, doc, node->uri, &node->x, &node->y).page; htdoc_update_outline(ctx, doc, node->down); node = node->next; } @@ -119,21 +119,21 @@ htdoc_load_links(fz_context *ctx, fz_page *page_) } static fz_bookmark -htdoc_make_bookmark(fz_context *ctx, fz_document *doc_, int page) +htdoc_make_bookmark(fz_context *ctx, fz_document *doc_, fz_location loc) { html_document *doc = (html_document*)doc_; - return fz_make_html_bookmark(ctx, doc->html, page); + return fz_make_html_bookmark(ctx, doc->html, loc.page); } -static int +static fz_location htdoc_lookup_bookmark(fz_context *ctx, fz_document *doc_, fz_bookmark mark) { html_document *doc = (html_document*)doc_; - return fz_lookup_html_bookmark(ctx, doc->html, mark); + return fz_make_location(0, fz_lookup_html_bookmark(ctx, doc->html, mark)); } static fz_page * -htdoc_load_page(fz_context *ctx, fz_document *doc_, int number) +htdoc_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { html_document *doc = (html_document*)doc_; html_page *page = fz_new_derived_page(ctx, html_page); diff --git a/source/html/html-imp.h b/source/html/html-imp.h index 64e1164..2b5a80d 100644 --- a/source/html/html-imp.h +++ b/source/html/html-imp.h @@ -205,8 +205,10 @@ enum struct fz_html_s { + fz_storable storable; fz_pool *pool; /* pool allocator for this html tree */ float page_w, page_h; + float layout_w, layout_h, layout_em; float page_margin[4]; fz_html_box *root; char *title; @@ -281,6 +283,7 @@ struct fz_html_flow_s } content; }; + fz_css *fz_new_css(fz_context *ctx); void fz_parse_css(fz_context *ctx, fz_css *css, const char *source, const char *file); fz_css_property *fz_parse_css_properties(fz_context *ctx, fz_pool *pool, const char *source); @@ -313,9 +316,14 @@ fz_outline *fz_load_html_outline(fz_context *ctx, fz_html *node); float fz_find_html_target(fz_context *ctx, fz_html *html, const char *id); fz_link *fz_load_html_links(fz_context *ctx, fz_html *html, int page, const char *base_uri, void *doc); +fz_html *fz_keep_html(fz_context *ctx, fz_html *html); void fz_drop_html(fz_context *ctx, fz_html *html); fz_bookmark fz_make_html_bookmark(fz_context *ctx, fz_html *html, int page); int fz_lookup_html_bookmark(fz_context *ctx, fz_html *html, fz_bookmark mark); void fz_debug_html(fz_context *ctx, fz_html_box *box); +fz_html *fz_store_html(fz_context *ctx, fz_html *html, void *doc, int chapter); +fz_html *fz_find_html(fz_context *ctx, void *doc, int chapter); +void fz_purge_stored_html(fz_context *ctx, void *doc); + #endif diff --git a/source/html/html-layout.c b/source/html/html-layout.c index 17da431..9dc55c9 100644 --- a/source/html/html-layout.c +++ b/source/html/html-layout.c @@ -809,6 +809,11 @@ fz_layout_html(fz_context *ctx, fz_html *html, float w, float h, float em) fz_var(hb_buf); fz_var(unlocked); + /* If we're already laid out to the specifications we need, + * nothing to do. */ + if (html->layout_w == w && html->layout_h == h && html->layout_em == em) + return; + html->page_margin[T] = fz_from_css_number(html->root->style->margin[T], em, em, 0); html->page_margin[B] = fz_from_css_number(html->root->style->margin[B], em, em, 0); html->page_margin[L] = fz_from_css_number(html->root->style->margin[L], em, em, 0); @@ -864,6 +869,12 @@ fz_layout_html(fz_context *ctx, fz_html *html, float w, float h, float em) if (h == 0) html->page_h = box->b; + /* Remember how we're laid out so we can avoid needless + * relayouts in future. */ + html->layout_w = w; + html->layout_h = h; + html->layout_em = em; + #ifndef NDEBUG if (fz_atoi(getenv("FZ_DEBUG_HTML"))) fz_debug_html(ctx, html->root); diff --git a/source/html/html-parse.c b/source/html/html-parse.c index f7af9fd..92b8db2 100644 --- a/source/html/html-parse.c +++ b/source/html/html-parse.c @@ -1,4 +1,4 @@ -#include "mupdf/fitz.h" +#include "../fitz/fitz-imp.h" #include "mupdf/ucdn.h" #include "html-imp.h" @@ -499,13 +499,23 @@ static void fz_drop_html_box(fz_context *ctx, fz_html_box *box) } } +static void fz_drop_html_imp(fz_context *ctx, fz_storable *stor) +{ + fz_html *html = (fz_html *)stor; + fz_drop_html_box(ctx, html->root); + fz_drop_pool(ctx, html->pool); +} + void fz_drop_html(fz_context *ctx, fz_html *html) { - if (html) - { - fz_drop_html_box(ctx, html->root); - fz_drop_pool(ctx, html->pool); - } + fz_defer_reap_start(ctx); + fz_drop_storable(ctx, &html->storable); + fz_defer_reap_end(ctx); +} + +fz_html *fz_keep_html(fz_context *ctx, fz_html *html) +{ + return fz_keep_storable(ctx, &html->storable); } static fz_html_box *new_box(fz_context *ctx, fz_pool *pool, fz_bidi_direction markup_dir) @@ -1332,8 +1342,12 @@ fz_parse_html(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const cha g.pool = fz_new_pool(ctx); html = fz_pool_alloc(ctx, g.pool, sizeof *html); + FZ_INIT_STORABLE(html, 1, fz_drop_html_imp); html->pool = g.pool; html->root = new_box(ctx, g.pool, DEFAULT_DIR); + html->layout_w = 0; + html->layout_h = 0; + html->layout_em = 0; fz_default_css_style(ctx, &style); match.up = NULL; @@ -1505,3 +1519,121 @@ fz_debug_html(fz_context *ctx, fz_html_box *box) { fz_debug_html_box(ctx, box, 0); } + +static size_t +fz_html_size(fz_context *ctx, fz_html *html) +{ + return html ? fz_pool_size(ctx, html->pool) : 0; +} + +/* Magic to make html storable. */ +typedef struct { + int refs; + void *doc; + int chapter_num; +} fz_html_key; + +static int +fz_make_hash_html_key(fz_context *ctx, fz_store_hash *hash, void *key_) +{ + fz_html_key *key = (fz_html_key *)key_; + hash->u.pi.ptr = key->doc; + hash->u.pi.i = key->chapter_num; + return 1; +} + +static void * +fz_keep_html_key(fz_context *ctx, void *key_) +{ + fz_html_key *key = (fz_html_key *)key_; + return fz_keep_imp(ctx, key, &key->refs); +} + +static void +fz_drop_html_key(fz_context *ctx, void *key_) +{ + fz_html_key *key = (fz_html_key *)key_; + if (fz_drop_imp(ctx, key, &key->refs)) + { + fz_free(ctx, key); + } +} + +static int +fz_cmp_html_key(fz_context *ctx, void *k0_, void *k1_) +{ + fz_html_key *k0 = (fz_html_key *)k0_; + fz_html_key *k1 = (fz_html_key *)k1_; + return k0->doc == k1->doc && k0->chapter_num == k1->chapter_num; +} + +static void +fz_format_html_key(fz_context *ctx, char *s, int n, void *key_) +{ + fz_html_key *key = (fz_html_key *)key_; + fz_snprintf(s, n, "(html doc=%p, ch=%d)", key->doc, key->chapter_num); +} + +static const fz_store_type fz_html_store_type = +{ + fz_make_hash_html_key, + fz_keep_html_key, + fz_drop_html_key, + fz_cmp_html_key, + fz_format_html_key, + NULL +}; + +fz_html *fz_store_html(fz_context *ctx, fz_html *html, void *doc, int chapter) +{ + fz_html_key *key = NULL; + fz_html *other_html; + + /* Stick the parsed html in the store */ + fz_var(key); + + fz_try(ctx) + { + key = fz_malloc_struct(ctx, fz_html_key); + key->refs = 1; + key->doc = doc; + key->chapter_num = chapter; + other_html = fz_store_item(ctx, key, html, fz_html_size(ctx, html), &fz_html_store_type); + if (other_html) + { + fz_drop_html(ctx, html); + html = other_html; + } + } + fz_always(ctx) + fz_drop_html_key(ctx, key); + fz_catch(ctx) + { + /* Do nothing */ + } + + return html; +} + +fz_html *fz_find_html(fz_context *ctx, void *doc, int chapter) +{ + fz_html_key key; + + key.refs = 1; + key.doc = doc; + key.chapter_num = chapter; + return fz_find_item(ctx, &fz_drop_html_imp, &key, &fz_html_store_type); +} + +static int +html_filter_store(fz_context *ctx, void *doc, void *key_) +{ + fz_html_key *key = (fz_html_key *)key_; + + return (doc == key->doc); +} + +void fz_purge_stored_html(fz_context *ctx, void *doc) +{ + fz_filter_store(ctx, html_filter_store, doc, &fz_html_store_type); +} diff --git a/source/pdf/pdf-link.c b/source/pdf/pdf-link.c index 347649a..3669759 100644 --- a/source/pdf/pdf-link.c +++ b/source/pdf/pdf-link.c @@ -348,3 +348,10 @@ pdf_resolve_link(fz_context *ctx, pdf_document *doc, const char *uri, float *xp, fz_warn(ctx, "unknown link uri '%s'", uri); return -1; } + +fz_location +pdf_resolve_link_imp(fz_context *ctx, fz_document *doc_, const char *uri, float *xp, float *yp) +{ + pdf_document *doc = (pdf_document*)doc_; + return fz_make_location(0, pdf_resolve_link(ctx, doc, uri, xp, yp)); +} diff --git a/source/pdf/pdf-page.c b/source/pdf/pdf-page.c index f8ec5d0..ec39f50 100644 --- a/source/pdf/pdf-page.c +++ b/source/pdf/pdf-page.c @@ -15,6 +15,11 @@ pdf_count_pages(fz_context *ctx, pdf_document *doc) return pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count")); } +int pdf_count_pages_imp(fz_context *ctx, fz_document *doc, int chapter) +{ + return pdf_count_pages(ctx, (pdf_document*)doc); +} + static int pdf_load_page_tree_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int idx) { @@ -1163,6 +1168,11 @@ pdf_load_page(fz_context *ctx, pdf_document *doc, int number) return page; } +fz_page *pdf_load_page_imp(fz_context *ctx, fz_document *doc, int chapter, int number) +{ + return (fz_page*)pdf_load_page(ctx, (pdf_document*)doc, number); +} + /* Delete a page from the page tree of a document. This does not remove the page contents diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index 3ce3baa..bcb31b2 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -2321,9 +2321,9 @@ pdf_new_document(fz_context *ctx, fz_stream *file) doc->super.authenticate_password = (fz_document_authenticate_password_fn*)pdf_authenticate_password; doc->super.has_permission = (fz_document_has_permission_fn*)pdf_has_permission; doc->super.load_outline = (fz_document_load_outline_fn*)pdf_load_outline; - doc->super.resolve_link = (fz_document_resolve_link_fn*)pdf_resolve_link; - doc->super.count_pages = (fz_document_count_pages_fn*)pdf_count_pages; - doc->super.load_page = (fz_document_load_page_fn*)pdf_load_page; + doc->super.resolve_link = pdf_resolve_link_imp; + doc->super.count_pages = pdf_count_pages_imp; + doc->super.load_page = pdf_load_page_imp; doc->super.lookup_metadata = (fz_document_lookup_metadata_fn*)pdf_lookup_metadata; pdf_lexbuf_init(ctx, &doc->lexbuf.base, PDF_LEXBUF_LARGE); @@ -2738,7 +2738,7 @@ pdf_obj *pdf_progressive_advance(fz_context *ctx, pdf_document *doc, int pagenum */ pdf_document *pdf_document_from_fz_document(fz_context *ctx, fz_document *ptr) { - return (pdf_document *)((ptr && ptr->count_pages == (fz_document_count_pages_fn*)pdf_count_pages) ? ptr : NULL); + return (pdf_document *)((ptr && ptr->count_pages == pdf_count_pages_imp) ? ptr : NULL); } pdf_page *pdf_page_from_fz_page(fz_context *ctx, fz_page *ptr) @@ -2877,7 +2877,9 @@ fz_document_handler pdf_document_handler = (fz_document_open_fn*)pdf_open_document, (fz_document_open_with_stream_fn*)pdf_open_document_with_stream, pdf_extensions, - pdf_mimetypes + pdf_mimetypes, + NULL, + NULL }; void pdf_mark_xref(fz_context *ctx, pdf_document *doc) diff --git a/source/svg/svg-doc.c b/source/svg/svg-doc.c index f434b2a..1d3162d 100644 --- a/source/svg/svg-doc.c +++ b/source/svg/svg-doc.c @@ -18,7 +18,7 @@ svg_drop_document(fz_context *ctx, fz_document *doc_) } static int -svg_count_pages(fz_context *ctx, fz_document *doc_) +svg_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { return 1; } @@ -49,7 +49,7 @@ svg_drop_page(fz_context *ctx, fz_page *page_) } static fz_page * -svg_load_page(fz_context *ctx, fz_document *doc_, int number) +svg_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { svg_document *doc = (svg_document*)doc_; svg_page *page; @@ -262,5 +262,7 @@ fz_document_handler svg_document_handler = NULL, svg_open_document_with_stream, svg_extensions, - svg_mimetypes + svg_mimetypes, + NULL, + NULL }; diff --git a/source/xps/xps-doc.c b/source/xps/xps-doc.c index a7969c8..b71182c 100644 --- a/source/xps/xps-doc.c +++ b/source/xps/xps-doc.c @@ -133,7 +133,7 @@ xps_add_link_target(fz_context *ctx, xps_document *doc, char *name) doc->target = target; } -int +fz_location xps_lookup_link_target(fz_context *ctx, fz_document *doc_, const char *target_uri, float *xp, float *yp) { xps_document *doc = (xps_document*)doc_; @@ -142,8 +142,8 @@ xps_lookup_link_target(fz_context *ctx, fz_document *doc_, const char *target_ur needle = needle ? needle + 1 : target_uri; for (target = doc->target; target; target = target->next) if (!strcmp(target->name, needle)) - return target->page; - return 0; + return fz_make_location(0, target->page); + return fz_make_location(-1, -1); } static void @@ -358,7 +358,7 @@ xps_read_page_list(fz_context *ctx, xps_document *doc) } int -xps_count_pages(fz_context *ctx, fz_document *doc_) +xps_count_pages(fz_context *ctx, fz_document *doc_, int chapter) { xps_document *doc = (xps_document*)doc_; return doc->page_count; @@ -436,7 +436,7 @@ xps_drop_page_imp(fz_context *ctx, fz_page *page_) } fz_page * -xps_load_page(fz_context *ctx, fz_document *doc_, int number) +xps_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) { xps_document *doc = (xps_document*)doc_; xps_page *page = NULL; @@ -505,5 +505,7 @@ fz_document_handler xps_document_handler = xps_open_document, xps_open_document_with_stream, xps_extensions, - xps_mimetypes + xps_mimetypes, + NULL, + NULL }; diff --git a/source/xps/xps-imp.h b/source/xps/xps-imp.h index 905e68b..e7eb78d 100644 --- a/source/xps/xps-imp.h +++ b/source/xps/xps-imp.h @@ -6,12 +6,12 @@ typedef struct xps_page_s xps_page; fz_document *xps_open_document(fz_context *ctx, const char *filename); fz_document *xps_open_document_with_stream(fz_context *ctx, fz_stream *file); -int xps_count_pages(fz_context *ctx, fz_document *doc); -fz_page *xps_load_page(fz_context *ctx, fz_document *doc, int number); +int xps_count_pages(fz_context *ctx, fz_document *doc, int chapter); +fz_page *xps_load_page(fz_context *ctx, fz_document *doc, int chapter, int number); fz_outline *xps_load_outline(fz_context *ctx, fz_document *doc); void xps_run_page(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie); fz_link *xps_load_links(fz_context *ctx, fz_page *page); -int xps_lookup_link_target(fz_context *ctx, fz_document *doc, const char *target_uri, float *xp, float *yp); +fz_location xps_lookup_link_target(fz_context *ctx, fz_document *doc, const char *target_uri, float *xp, float *yp); int xps_strcasecmp(char *a, char *b); void xps_resolve_url(fz_context *ctx, xps_document *doc, char *output, char *base_uri, char *path, int output_size); diff --git a/source/xps/xps-outline.c b/source/xps/xps-outline.c index dfa5143..dd44275 100644 --- a/source/xps/xps-outline.c +++ b/source/xps/xps-outline.c @@ -37,7 +37,7 @@ xps_parse_document_outline(fz_context *ctx, xps_document *doc, fz_xml *root) entry = fz_new_outline(ctx); entry->title = Memento_label(fz_strdup(ctx, description), "outline_title"); entry->uri = Memento_label(fz_strdup(ctx, target), "outline_uri"); - entry->page = xps_lookup_link_target(ctx, (fz_document*)doc, target, NULL, NULL); + entry->page = xps_lookup_link_target(ctx, (fz_document*)doc, target, NULL, NULL).page; entry->down = NULL; entry->next = NULL; http://git.ghostscript.com/?p=mupdf.git;a=commit;h=80606caf66c9bd1306ae264f972c5c859e154f17 -- MuPDF library Artifex Software, Inc.