diff options
author | Paul Gardiner <paulg.artifex@glidos.net> | 2012-10-16 15:13:39 +0100 |
---|---|---|
committer | Paul Gardiner <paulg.artifex@glidos.net> | 2012-10-16 15:13:39 +0100 |
commit | 96f335bc84756cddc094bb44df28223a546808a0 (patch) | |
tree | 89898c3aa0dee293629e1171a8ec64ee954cd501 | |
parent | 31791f3ebe049af8dccacd5871c0aeac7dc86b29 (diff) | |
download | mupdf-96f335bc84756cddc094bb44df28223a546808a0.tar.xz |
Forms: avoid the need to reload the page on every change
Add pdf_update_annot, which is called before rendering an annotation, and
checks that the annotation structure has correct information. There are
three reasons the information can be out of date.
Attributes of a field may have been changed such that its appearance
stream needs updating. In this case the field will have have "Dirty"
added to its dictionary
The mouse may have changed state over the field, and a different
appearance stream needs selecting. The annotation structure now records
the mouse states for which the current appearance stream is acceptable.
The field may have changed state as recorded by its "AS" value, and a
different appearance stream needs selecting.
-rw-r--r-- | android/jni/mupdf.c | 6 | ||||
-rw-r--r-- | android/src/com/artifex/mupdf/MuPDFCore.java | 10 | ||||
-rw-r--r-- | apps/pdfapp.c | 72 | ||||
-rw-r--r-- | pdf/mupdf-internal.h | 15 | ||||
-rw-r--r-- | pdf/pdf_annot.c | 128 | ||||
-rw-r--r-- | pdf/pdf_form.c | 72 | ||||
-rw-r--r-- | pdf/pdf_interpret.c | 1 |
7 files changed, 233 insertions, 71 deletions
diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c index 2ec02bf8..2adc3b7c 100644 --- a/android/jni/mupdf.c +++ b/android/jni/mupdf.c @@ -150,10 +150,10 @@ Java_com_artifex_mupdf_MuPDFCore_gotoPageInternal(JNIEnv *env, jobject thiz, int JNIEXPORT void JNICALL Java_com_artifex_mupdf_MuPDFCore_markDirtyInternal(JNIEnv *env, jobject thiz, int page) { - if (currentPage != NULL && page == pagenum) + if (currentPageList != NULL && page == pagenum) { - fz_free_page(doc, currentPage); - currentPage = NULL; + fz_free_display_list(ctx, currentPageList); + currentPageList = NULL; } } diff --git a/android/src/com/artifex/mupdf/MuPDFCore.java b/android/src/com/artifex/mupdf/MuPDFCore.java index f2017f77..2e0db110 100644 --- a/android/src/com/artifex/mupdf/MuPDFCore.java +++ b/android/src/com/artifex/mupdf/MuPDFCore.java @@ -114,12 +114,8 @@ public class MuPDFCore break; } - if (changed) { - if (page == pageNum) - pageNum = -1; - + if (changed) markDirtyInternal(page); - } return new PassClickResult(changed, wtype, text); } @@ -129,10 +125,8 @@ public class MuPDFCore gotoPage(page); success = setFocusedWidgetTextInternal(text) != 0 ? true : false; - if (success) { - pageNum = -1; + if (success) markDirtyInternal(page); - } return success; } diff --git a/apps/pdfapp.c b/apps/pdfapp.c index 2e7b6ffe..aa31ab0f 100644 --- a/apps/pdfapp.c +++ b/apps/pdfapp.c @@ -420,6 +420,45 @@ static void pdfapp_loadpage(pdfapp_t *app) app->errored = errored; } +static void pdfapp_recreate_displaylist(pdfapp_t *app) +{ + fz_device *mdev = NULL; + int errored = 0; + fz_cookie cookie = { 0 }; + + fz_var(mdev); + + if (app->page_list) + { + fz_free_display_list(app->ctx, app->page_list); + app->page_list = NULL; + } + + fz_try(app->ctx) + { + /* Create display list */ + app->page_list = fz_new_display_list(app->ctx); + mdev = fz_new_list_device(app->ctx, app->page_list); + fz_run_page(app->doc, app->page, mdev, fz_identity, &cookie); + if (cookie.errors) + { + pdfapp_warn(app, "Errors found on page"); + errored = 1; + } + } + fz_always(app->ctx) + { + fz_free_device(mdev); + } + fz_catch(app->ctx) + { + pdfapp_warn(app, "Cannot load page"); + errored = 1; + } + + app->errored = errored; +} + #define MAX_TITLE 256 static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint) @@ -437,20 +476,29 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai if (loadpage) { - pdfapp_loadpage(app); + if (loadpage == 1) + { + pdfapp_loadpage(app); - /* Zero search hit position */ - app->hit = -1; - app->hitlen = 0; + /* Zero search hit position */ + app->hit = -1; + app->hitlen = 0; - /* Extract text */ - app->page_sheet = fz_new_text_sheet(app->ctx); - app->page_text = fz_new_text_page(app->ctx, app->page_bbox); - if (app->page_list) + /* Extract text */ + app->page_sheet = fz_new_text_sheet(app->ctx); + app->page_text = fz_new_text_page(app->ctx, app->page_bbox); + + if (app->page_list) + { + tdev = fz_new_text_device(app->ctx, app->page_sheet, app->page_text); + fz_run_display_list(app->page_list, tdev, fz_identity, fz_infinite_bbox, &cookie); + fz_free_device(tdev); + } + } + else { - tdev = fz_new_text_device(app->ctx, app->page_sheet, app->page_text); - fz_run_display_list(app->page_list, tdev, fz_identity, fz_infinite_bbox, &cookie); - fz_free_device(tdev); + /* pdfapp_onmouse passes loadpage == 2, meaning only recreate the display list */ + pdfapp_recreate_displaylist(app); } } @@ -1217,7 +1265,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta } app->nowaitcursor = 1; - pdfapp_showpage(app, 1, 1, 1); + pdfapp_showpage(app, 2, 1, 1); app->nowaitcursor = 0; processed = 1; } diff --git a/pdf/mupdf-internal.h b/pdf/mupdf-internal.h index d1bd5b9b..e14a3daf 100644 --- a/pdf/mupdf-internal.h +++ b/pdf/mupdf-internal.h @@ -288,7 +288,7 @@ pdf_xobject *pdf_keep_xobject(fz_context *ctx, pdf_xobject *xobj); void pdf_drop_xobject(fz_context *ctx, pdf_xobject *xobj); void pdf_update_xobject_contents(pdf_document *xref, pdf_xobject *form, fz_buffer *buffer); -void pdf_update_appearance(pdf_document *doc, pdf_obj *obj); +int pdf_update_appearance(pdf_document *doc, pdf_obj *obj); /* * CMap @@ -490,11 +490,23 @@ float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize, * Interactive features */ +/* + * Flags as to the suitability of an annotation for the mouse states up and down + * Both flags set mean suitable for both states. + */ +enum +{ + MOUSE_DOWN_APPEARANCE = 1, + MOUSE_UP_APPEARANCE = 2, +}; + struct pdf_annot_s { pdf_obj *obj; fz_rect rect; fz_rect pagerect; + int mouse_states; + int has_states; pdf_xobject *ap; fz_matrix matrix; pdf_annot *next; @@ -510,6 +522,7 @@ pdf_obj *pdf_load_name_tree(pdf_document *doc, char *which); fz_link *pdf_load_link_annots(pdf_document *, pdf_obj *annots, fz_matrix page_ctm); pdf_annot *pdf_load_annots(pdf_document *, pdf_obj *annots, fz_matrix page_ctm); +void pdf_update_annot(pdf_document *, pdf_annot *annot); void pdf_free_annot(fz_context *ctx, pdf_annot *link); int pdf_field_type(pdf_document *doc, pdf_obj *field); diff --git a/pdf/pdf_annot.c b/pdf/pdf_annot.c index c27e97de..0b810500 100644 --- a/pdf/pdf_annot.c +++ b/pdf/pdf_annot.c @@ -356,8 +356,10 @@ pdf_annot * pdf_load_annots(pdf_document *xref, pdf_obj *annots, fz_matrix page_ctm) { pdf_annot *annot, *head, *tail; - pdf_obj *obj, *ap, *as, *n, *rect; + pdf_obj *obj, *ap, *as, *n, *d, *c, *rect; int i, len; + int mouse_states; + int has_states = 0; fz_context *ctx = xref->ctx; head = tail = NULL; @@ -378,21 +380,44 @@ pdf_load_annots(pdf_document *xref, pdf_obj *annots, fz_matrix page_ctm) { pdf_hotspot *hp = &xref->hotspot; - n = NULL; + n = pdf_dict_gets(ap, "N"); /* normal state */ + d = pdf_dict_gets(ap, "D"); /* down state */ - if (hp->num == pdf_to_num(obj) - && hp->gen == pdf_to_gen(obj) - && (hp->state & HOTSPOT_POINTER_DOWN)) + if (n && d) { - n = pdf_dict_gets(ap, "D"); /* down state */ + if (hp->num == pdf_to_num(obj) + && hp->gen == pdf_to_gen(obj) + && (hp->state & HOTSPOT_POINTER_DOWN)) + { + /* Use the down appearance, but as we also have + * a normal appearance, it is suitable only for mouse + * down */ + c = d; + mouse_states = MOUSE_DOWN_APPEARANCE; + } + else + { + /* Use the normal appearance, but as we also have + * a down appearance, it is suitable only for mouse + * up */ + c = n; + mouse_states = MOUSE_UP_APPEARANCE; + } + } + else + { + /* Use whichever appearance we have for both states */ + c = n?n:d; + mouse_states = MOUSE_UP_APPEARANCE|MOUSE_DOWN_APPEARANCE; } - if (n == NULL) - n = pdf_dict_gets(ap, "N"); /* normal state */ /* lookup current state in sub-dictionary */ - if (!pdf_is_stream(xref, pdf_to_num(n), pdf_to_gen(n))) - n = pdf_dict_get(n, as); + if (!pdf_is_stream(xref, pdf_to_num(c), pdf_to_gen(c))) + { + has_states = 1; + c = pdf_dict_get(c, as); + } annot = fz_malloc_struct(ctx, pdf_annot); @@ -401,12 +426,14 @@ pdf_load_annots(pdf_document *xref, pdf_obj *annots, fz_matrix page_ctm) annot->pagerect = fz_transform_rect(page_ctm, annot->rect); annot->ap = NULL; annot->type = pdf_field_type(xref, obj); + annot->mouse_states = mouse_states; + annot->has_states = has_states; - if (pdf_is_stream(xref, pdf_to_num(n), pdf_to_gen(n))) + if (pdf_is_stream(xref, pdf_to_num(c), pdf_to_gen(c))) { fz_try(ctx) { - annot->ap = pdf_load_xobject(xref, n); + annot->ap = pdf_load_xobject(xref, c); pdf_transform_annot(annot); } fz_catch(ctx) @@ -433,6 +460,83 @@ pdf_load_annots(pdf_document *xref, pdf_obj *annots, fz_matrix page_ctm) return head; } +void +pdf_update_annot(pdf_document *xref, pdf_annot *annot) +{ + pdf_obj *obj, *ap, *as, *n, *d, *c; + fz_context *ctx = xref->ctx; + int suitable; + int mouse_states; + pdf_hotspot *hp = &xref->hotspot; + + obj = annot->obj; + + if (hp->num == pdf_to_num(obj) + && hp->gen == pdf_to_gen(obj) + && (hp->state & HOTSPOT_POINTER_DOWN)) + { + mouse_states = MOUSE_DOWN_APPEARANCE; + } + else + { + mouse_states = MOUSE_UP_APPEARANCE; + } + + suitable = (annot->mouse_states & mouse_states); + + if (pdf_update_appearance(xref, obj) || !suitable || annot->has_states) + { + ap = pdf_dict_gets(obj, "AP"); + as = pdf_dict_gets(obj, "AS"); + + if (pdf_is_dict(ap)) + { + pdf_hotspot *hp = &xref->hotspot; + + n = pdf_dict_gets(ap, "N"); /* normal state */ + d = pdf_dict_gets(ap, "D"); /* down state */ + + if (mouse_states == MOUSE_DOWN_APPEARANCE) + c = d?d:n; + else + c = n?n:d; + + annot->has_states = 0; + + /* lookup current state in sub-dictionary */ + if (!pdf_is_stream(xref, pdf_to_num(c), pdf_to_gen(c))) + { + annot->has_states = 1; + c = pdf_dict_get(c, as); + } + + /* This test is important to avoid losing the knowledge + * that an appearance stream is for both mouse states */ + if (!suitable) + annot->mouse_states = mouse_states; + + pdf_drop_xobject(ctx, annot->ap); + annot->ap = NULL; + + if (pdf_is_stream(xref, pdf_to_num(c), pdf_to_gen(c))) + { + fz_try(ctx) + { + annot->ap = pdf_load_xobject(xref, c); + pdf_transform_annot(annot); + } + fz_catch(ctx) + { + fz_warn(ctx, "ignoring broken annotation"); + } + } + + if (obj == xref->focus_obj) + xref->focus = annot; + } + } +} + pdf_annot * pdf_first_annot(pdf_document *doc, pdf_page *page) { diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c index 22b552cf..0db8b231 100644 --- a/pdf/pdf_form.c +++ b/pdf/pdf_form.c @@ -1698,50 +1698,52 @@ static void execute_action(pdf_document *doc, pdf_obj *obj, pdf_obj *a) } } -void pdf_update_appearance(pdf_document *doc, pdf_obj *obj) +int pdf_update_appearance(pdf_document *doc, pdf_obj *obj) { - if (!pdf_dict_gets(obj, "AP") || pdf_dict_gets(obj, "Dirty")) + if (pdf_dict_gets(obj, "AP") && !pdf_dict_gets(obj, "Dirty")) + return 0; + + if (!strcmp(pdf_to_name(pdf_dict_gets(obj, "Subtype")), "Widget")) { - if (!strcmp(pdf_to_name(pdf_dict_gets(obj, "Subtype")), "Widget")) + switch(pdf_field_type(doc, obj)) { - switch(pdf_field_type(doc, obj)) + case FZ_WIDGET_TYPE_TEXT: { - case FZ_WIDGET_TYPE_TEXT: + pdf_obj *formatting = pdf_dict_getp(obj, "AA/F"); + if (formatting && doc->js) { - pdf_obj *formatting = pdf_dict_getp(obj, "AA/F"); - if (formatting && doc->js) - { - /* Apply formatting */ - pdf_js_event e; - - e.target = obj; - e.value = pdf_field_value(doc, obj); - pdf_js_setup_event(doc->js, &e); - execute_action(doc, obj, formatting); - /* Update appearance from JS event.value */ - update_text_appearance(doc, obj, pdf_js_get_event(doc->js)->value); - } - else - { - /* Update appearance from field value */ - update_text_appearance(doc, obj, NULL); - } + /* Apply formatting */ + pdf_js_event e; + + e.target = obj; + e.value = pdf_field_value(doc, obj); + pdf_js_setup_event(doc->js, &e); + execute_action(doc, obj, formatting); + /* Update appearance from JS event.value */ + update_text_appearance(doc, obj, pdf_js_get_event(doc->js)->value); + } + else + { + /* Update appearance from field value */ + update_text_appearance(doc, obj, NULL); } - break; - case FZ_WIDGET_TYPE_PUSHBUTTON: - update_pushbutton_appearance(doc, obj); - break; - case FZ_WIDGET_TYPE_LISTBOX: - case FZ_WIDGET_TYPE_COMBOBOX: - /* Treating listbox and combobox identically for now, - * and the behaviour is most appropriate for a combobox */ - update_combobox_appearance(doc, obj); - break; } + break; + case FZ_WIDGET_TYPE_PUSHBUTTON: + update_pushbutton_appearance(doc, obj); + break; + case FZ_WIDGET_TYPE_LISTBOX: + case FZ_WIDGET_TYPE_COMBOBOX: + /* Treating listbox and combobox identically for now, + * and the behaviour is most appropriate for a combobox */ + update_combobox_appearance(doc, obj); + break; } - - pdf_dict_dels(obj, "Dirty"); } + + pdf_dict_dels(obj, "Dirty"); + + return 1; } static void execute_action_chain(pdf_document *doc, pdf_obj *obj) diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c index 1fc4c58e..7f99f5c0 100644 --- a/pdf/pdf_interpret.c +++ b/pdf/pdf_interpret.c @@ -2850,6 +2850,7 @@ pdf_run_page_with_usage(pdf_document *xref, pdf_page *page, fz_device *dev, fz_m { fz_try(ctx) { + pdf_update_annot(xref, annot); pdf_run_xobject(csi, page->resources, annot->ap, annot->matrix); } fz_catch(ctx) |