Update pdf_update_page to cope with local xrefs.
authorRobin Watts <[email protected]>
Wed, 21 Jul 2021 17:14:56 +0000 (18:14 +0100)
committerRobin Watts <[email protected]>
Wed, 21 Jul 2021 17:42:11 +0000 (18:42 +0100)
pdf_update_page runs across all the annotations on a page
calling pdf_update_annot. This in turn calls pdf_update_appearance.

A call to pdf_update_appearance can either a) do nothing,
b) synthesise an appearance in a local_xref or c) synthesise
an appearance in the real document, discarding any local_xref.

Thus a call to update the appearance of an annotation that does c)
can 'undo' the update of earlier annotations.

The fix for this is to spot the change, and to rerun the updates
for earlier annotations. This requirement is now documented in
the header file.

We update the implementation of pdf_update_page to perform this
correctly.

We also take the opportunity to hide pdf_update_appearance from
the API, as people should just be calling pdf_update_annot.

docs/manual-mutool-run.html
include/mupdf/pdf/annot.h
platform/java/jni/pdfannotation.c
platform/java/mupdf_native.h
platform/java/src/com/artifex/mupdf/fitz/PDFAnnotation.java
source/pdf/pdf-appearance.c
source/pdf/pdf-form.c
source/tools/murun.c

index b6998962e73b7eb647705fe8582769732ba029c6..23c9f91b1c991f21a358ad8b861d6bdd5ee2fd43 100644 (file)
@@ -796,7 +796,7 @@ The type must be one of the annotation subtypes listed in the PDF reference.
 <dt>PDFAnnotation#getColor(), #setColor(color)
 <dt>PDFAnnotation#getQuadPoints(), #setQuadPoints(quadPoints)
 <dt>PDFAnnotation#getInkList(), #setInkList(inkList)
-<dt>PDFAnnotation#updateAppearance()
+<dt>PDFAnnotation#update()
 <dd>Update the appearance stream to account for changes in the annotation.
 </dl>
 
index 187f52d6c7c4fdf390bb86bef0aaa49f33c18cfa..6a82ed3a4cdfa498ffd5e4f23dc134f07c447869 100644 (file)
@@ -554,7 +554,6 @@ int pdf_set_annot_field_value(fz_context *ctx, pdf_document *doc, pdf_widget *wi
        Recreate the appearance stream for an annotation, if necessary.
 */
 fz_text *pdf_layout_fit_text(fz_context *ctx, fz_font *font, fz_text_language lang, const char *str, fz_rect bounds);
-void pdf_update_appearance(fz_context *ctx, pdf_annot *annot);
 
 /*
        Start/Stop using the annotation-local xref. This allows us to
@@ -581,6 +580,22 @@ void pdf_annot_pop_and_discard_local_xref(fz_context *ctx, pdf_annot *annot);
 
        Returns true if the annotation appearance has changed since the last time
        pdf_update_annot was called or the annotation was first loaded.
+
+       Note that if you update an annotation and it causes a change (due to the
+       appearance stream being updated), that may invalidate the appearance stream
+       for other (earlier) annotations (specifically for those that have a
+       'local' appearance stream that has not been written back to the document
+       proper - such as unsigned signature fields). In order to work properly
+       therefore, if you get informed of a change to an annotation, you should
+       "reupdate" the previous annotations in a list. In practice it's probably
+       simplest to completely run the loop over the annotations and reupdate a
+       second time.
+
+       This may seem like it might go into an infinite loop of needing to update
+       multiple times, but in practice at worst every annotation needs to be
+       updated twice. The second pass through calling pdf_update_annot may have
+       calls returning true, but a third pass through would never have any calls
+       return true.
 */
 int pdf_update_annot(fz_context *ctx, pdf_annot *annot);
 
index c79164d460927d55160e47d9fe55d3b00bb7f758..98fe443f1a223e0adc46aac400cdcdeaf0224a6f 100644 (file)
@@ -998,20 +998,6 @@ FUN(PDFAnnotation_setLine)(JNIEnv *env, jobject self, jobject ja, jobject jb)
                jni_rethrow_void(env, ctx);
 }
 
-JNIEXPORT void JNICALL
-FUN(PDFAnnotation_updateAppearance)(JNIEnv *env, jobject self)
-{
-       fz_context *ctx = get_context(env);
-       pdf_annot *annot = from_PDFAnnotation(env, self);
-
-       if (!ctx || !annot) return;
-
-       fz_try(ctx)
-               pdf_update_appearance(ctx, annot);
-       fz_catch(ctx)
-               jni_rethrow_void(env, ctx);
-}
-
 JNIEXPORT jobject JNICALL
 FUN(PDFAnnotation_getDefaultAppearance)(JNIEnv *env, jobject self)
 {
index bcb9cfe682e6390b4a5239479be06ec8ce76595b..33c3fa5fdcc138984512eabe4c10c7ac468dbb5f 100644 (file)
@@ -1843,14 +1843,6 @@ JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_eventFocus
 JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_eventBlur
   (JNIEnv *, jobject);
 
-/*
- * Class:     com_artifex_mupdf_fitz_PDFAnnotation
- * Method:    updateAppearance
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_updateAppearance
-  (JNIEnv *, jobject);
-
 /*
  * Class:     com_artifex_mupdf_fitz_PDFAnnotation
  * Method:    update
index 926b320f959f84dc2e3e5e5e0aa36322c389e4e7..ff4194ca093ddfcda0ac3aeeeb1bc12ee4016c36 100644 (file)
@@ -219,7 +219,6 @@ public class PDFAnnotation
        public native void eventFocus();
        public native void eventBlur();
 
-       public native void updateAppearance();
        public native boolean update();
 
        public native PDFObject getObject();
index 651a9503eccdbad1ce82add7329ea259939fb4a0..9c91a51d26ba2e49d47dd91a4f745895c175fe38 100644 (file)
@@ -2621,7 +2621,7 @@ void pdf_annot_pop_and_discard_local_xref(fz_context *ctx, pdf_annot *annot)
        doc->local_xref = NULL;
 }
 
-void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
+static void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
 {
        pdf_obj *subtype;
        pdf_obj *ft = NULL;
index 3458137da81c80689971a5f727f0164a9954189f..e809894873dbd97a9f4ab23fa770c40a164683db 100644 (file)
@@ -585,6 +585,16 @@ pdf_update_page(fz_context *ctx, pdf_page *page)
                for (widget = page->widgets; widget; widget = widget->next)
                        if (pdf_update_annot(ctx, widget))
                                changed = 1;
+               if (changed)
+               {
+                       for (annot = page->annots; annot; annot = annot->next)
+                               pdf_update_annot(ctx, annot);
+                       for (widget = page->widgets; widget; widget = widget->next)
+                               pdf_update_annot(ctx, widget);
+                       /* These pdf_update_annots may return true (but only if they
+                        * did the first time, and a third run through will never
+                        * get any changes. */
+               }
        }
        fz_always(ctx)
        {
index 78b5467e1b34dd2664374c6521e9c21da6b11908..c80595484a0103d5ff218a8fe40a9d0d155e4530 100644 (file)
@@ -6087,16 +6087,6 @@ static void ffi_PDFAnnotation_setAppearance(js_State *J)
                js_throw(J);
 }
 
-static void ffi_PDFAnnotation_updateAppearance(js_State *J)
-{
-       fz_context *ctx = js_getcontext(J);
-       pdf_annot *annot = js_touserdata(J, 0, "pdf_annot");
-       fz_try(ctx)
-               pdf_update_appearance(ctx, annot);
-       fz_catch(ctx)
-               rethrow(J);
-}
-
 static void ffi_PDFAnnotation_update(js_State *J)
 {
        fz_context *ctx = js_getcontext(J);
@@ -7007,7 +6997,6 @@ int murun_main(int argc, char **argv)
                jsB_propfun(J, "PDFAnnotation.clearVertices", ffi_PDFAnnotation_clearVertices, 0);
                jsB_propfun(J, "PDFAnnotation.addVertex", ffi_PDFAnnotation_addVertex, 2);
 
-               jsB_propfun(J, "PDFAnnotation.updateAppearance", ffi_PDFAnnotation_updateAppearance, 0);
                jsB_propfun(J, "PDFAnnotation.update", ffi_PDFAnnotation_update, 0);
 
                jsB_propfun(J, "PDFAnnotation.getHot", ffi_PDFAnnotation_getHot, 0);