summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gardiner <paulg.artifex@glidos.net>2012-10-16 15:13:39 +0100
committerPaul Gardiner <paulg.artifex@glidos.net>2012-10-16 15:13:39 +0100
commit96f335bc84756cddc094bb44df28223a546808a0 (patch)
tree89898c3aa0dee293629e1171a8ec64ee954cd501
parent31791f3ebe049af8dccacd5871c0aeac7dc86b29 (diff)
downloadmupdf-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.c6
-rw-r--r--android/src/com/artifex/mupdf/MuPDFCore.java10
-rw-r--r--apps/pdfapp.c72
-rw-r--r--pdf/mupdf-internal.h15
-rw-r--r--pdf/pdf_annot.c128
-rw-r--r--pdf/pdf_form.c72
-rw-r--r--pdf/pdf_interpret.c1
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)