Remove the dirty flag from pdf_document.
authorRobin Watts <[email protected]>
Mon, 19 Jul 2021 12:09:18 +0000 (13:09 +0100)
committerRobin Watts <[email protected]>
Mon, 19 Jul 2021 18:04:43 +0000 (19:04 +0100)
Previously, we have tracked whether a document contains changes
by using a 'dirty' flag, that we have had to keep up to date
during edits.

Here, we move to remove it entirely, and instead base our
choice of 'dirty or not' upon whether the incremental xref
contains any objects.

This means we can now do things like:

  1) Load a document, tick a checkbox (doc appears dirty),
     undo (doc appears clean), redo (doc appears dirty),
     save snapshot, undo (doc appears clean).

  2) Reload the snapshot (doc appears dirty), undo (doc appears clean).

12 files changed:
include/mupdf/pdf/document.h
include/mupdf/pdf/parse.h
include/mupdf/pdf/xref.h
platform/gl/gl-main.c
source/pdf/pdf-annot.c
source/pdf/pdf-form.c
source/pdf/pdf-lex.c
source/pdf/pdf-object.c
source/pdf/pdf-parse.c
source/pdf/pdf-repair.c
source/pdf/pdf-write.c
source/pdf/pdf-xref.c

index 9e061e49736e102767aecf59b65417cee5e1129e..869aa74d9ac886ed435de830354a68d080cb5752 100644 (file)
@@ -365,7 +365,6 @@ struct pdf_document
        pdf_js *js;
 
        int recalculate;
-       int dirty;
        int redacted;
 
        pdf_doc_event_cb *event_cb;
index 824d16e07fd4d5d9bfe1451340ff91d376b07e74..b20a17f145d0298dc691af5526ae7c27e6e9b2ec 100644 (file)
@@ -12,6 +12,7 @@ typedef enum
        PDF_TOK_OBJ, PDF_TOK_ENDOBJ,
        PDF_TOK_STREAM, PDF_TOK_ENDSTREAM,
        PDF_TOK_XREF, PDF_TOK_TRAILER, PDF_TOK_STARTXREF,
+       PDF_TOK_NEWOBJ,
        PDF_NUM_TOKENS
 } pdf_token;
 
@@ -26,7 +27,7 @@ pdf_obj *pdf_parse_array(fz_context *ctx, pdf_document *doc, fz_stream *f, pdf_l
 pdf_obj *pdf_parse_dict(fz_context *ctx, pdf_document *doc, fz_stream *f, pdf_lexbuf *buf);
 pdf_obj *pdf_parse_stm_obj(fz_context *ctx, pdf_document *doc, fz_stream *f, pdf_lexbuf *buf);
 pdf_obj *pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *f, int *num, int *gen, int64_t *stm_ofs, int *try_repair);
-pdf_obj *pdf_parse_journal_obj(fz_context *ctx, pdf_document *doc, fz_stream *stm, int *onum, fz_buffer **ostm);
+pdf_obj *pdf_parse_journal_obj(fz_context *ctx, pdf_document *doc, fz_stream *stm, int *onum, fz_buffer **ostm, int *newobj);
 
 /*
        print a lexed token to a buffer, growing if necessary
index 8b4ad30e92c7becd23d4216d24ab3c7306054aa8..8acce92d45e383a81287fb47ad31abcfd492b54e 100644 (file)
@@ -133,11 +133,12 @@ pdf_xref_entry *pdf_get_populating_xref_entry(fz_context *ctx, pdf_document *doc
 pdf_xref_entry *pdf_get_xref_entry(fz_context *ctx, pdf_document *doc, int i);
 void pdf_replace_xref(fz_context *ctx, pdf_document *doc, pdf_xref_entry *entries, int n);
 void pdf_forget_xref(fz_context *ctx, pdf_document *doc);
+pdf_xref_entry *pdf_get_incremental_xref_entry(fz_context *ctx, pdf_document *doc, int i);
 
 /*
        Ensure that an object has been cloned into the incremental xref section.
 */
-void pdf_xref_ensure_incremental_object(fz_context *ctx, pdf_document *doc, int num);
+int pdf_xref_ensure_incremental_object(fz_context *ctx, pdf_document *doc, int num);
 int pdf_xref_is_incremental(fz_context *ctx, pdf_document *doc, int num);
 void pdf_xref_store_unsaved_signature(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_pkcs7_signer *signer);
 void pdf_xref_remove_unsaved_signature(fz_context *ctx, pdf_document *doc, pdf_obj *field);
index 4a691f4b9baa32108b1d7744b13ecc2f658a5302..23e7735651aed79368a0aef0186ec0fad59086a9 100644 (file)
@@ -758,7 +758,7 @@ void update_title(void)
        else
                title = filename;
 
-       if (pdf && pdf->dirty)
+       if (pdf && pdf_has_unsaved_changes(ctx, pdf))
                extra = "*";
 
        n = strlen(title);
@@ -2441,7 +2441,7 @@ void do_main(void)
        int was_dirty = 0;
        if (pdf)
        {
-               was_dirty = pdf->dirty;
+               was_dirty = pdf_has_unsaved_changes(ctx, pdf);
        }
 
        do_app();
@@ -2477,7 +2477,7 @@ void do_main(void)
 
        if (pdf)
        {
-               if (was_dirty != pdf->dirty)
+               if (was_dirty != pdf_has_unsaved_changes(ctx, pdf))
                        update_title();
        }
 }
index ed5bfecaccfbbcaae3114b89897e8a05570e4c2a..227fa0d642fef1c4407ad89a5259c99c4f83b83e 100644 (file)
@@ -281,8 +281,6 @@ void
 pdf_dirty_annot(fz_context *ctx, pdf_annot *annot)
 {
        pdf_annot_request_resynthesis(ctx, annot);
-       if (annot->page && annot->page->doc)
-               annot->page->doc->dirty = 1;
 }
 
 const char *
@@ -464,8 +462,6 @@ pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
                        *page->annot_tailp = annot;
                        page->annot_tailp = &annot->next;
                }
-
-               doc->dirty = 1;
        }
        fz_always(ctx)
        {
@@ -549,8 +545,6 @@ pdf_create_link(fz_context *ctx, pdf_page *page, fz_rect bbox, const char *uri)
                        linkp = &(*linkp)->next;
 
                *linkp = link;
-
-               doc->dirty = 1;
        }
        fz_always(ctx)
        {
@@ -856,8 +850,6 @@ pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
 
                /* And free it. */
                pdf_drop_annot(ctx, annot);
-
-               doc->dirty = 1;
        }
        fz_always(ctx)
                pdf_end_operation(ctx, page->doc);
index 36281798ee5803b2ad61aa950b64a71b0b1ca2a1..a4e8cebbc6b8151c7a29c9e15edf5c0095d5b068 100644 (file)
@@ -244,9 +244,6 @@ static void reset_form_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
                        break;
                }
        }
-
-       if (pdf_field_dirties_document(ctx, doc, field))
-               doc->dirty = 1;
 }
 
 void pdf_field_reset(fz_context *ctx, pdf_document *doc, pdf_obj *field)
@@ -497,8 +494,6 @@ static void toggle_check_box(fz_context *ctx, pdf_annot *annot)
 
                pdf_dict_put(ctx, grp, PDF_NAME(V), val);
                set_check_grp(ctx, doc, grp, val);
-               if (pdf_field_dirties_document(ctx, doc, field))
-                       doc->dirty = 1;
                doc->recalculate = 1;
        }
        fz_always(ctx)
@@ -511,7 +506,15 @@ static void toggle_check_box(fz_context *ctx, pdf_annot *annot)
 
 int pdf_has_unsaved_changes(fz_context *ctx, pdf_document *doc)
 {
-       return doc->dirty;
+       int i;
+
+       if (doc->num_incremental_sections == 0)
+               return 0;
+
+       for (i = 0; i < doc->xref_sections->num_objects; i++)
+               if (doc->xref_sections->subsec->table[i].type != 0)
+                       break;
+       return i != doc->xref_sections->num_objects;
 }
 
 int pdf_was_repaired(fz_context *ctx, pdf_document *doc)
@@ -601,8 +604,6 @@ static int set_validated_field_value(fz_context *ctx, pdf_document *doc, pdf_obj
                        return 0;
        }
 
-       if (pdf_field_dirties_document(ctx, doc, field))
-               doc->dirty = 1;
        update_field_value(ctx, doc, field, text);
 
        return 1;
@@ -636,8 +637,6 @@ static int set_checkbox_value(fz_context *ctx, pdf_document *doc, pdf_obj *field
 {
        update_checkbox_selector(ctx, doc, field, val);
        update_field_value(ctx, doc, field, val);
-       if (pdf_field_dirties_document(ctx, doc, field))
-               doc->dirty = 1;
        return 1;
 }
 
@@ -1212,8 +1211,6 @@ void pdf_choice_widget_set_value(fz_context *ctx, pdf_widget *tw, int n, const c
                pdf_dict_del(ctx, annot->obj, PDF_NAME(I));
 
                pdf_field_mark_dirty(ctx, annot->obj);
-               if (pdf_field_dirties_document(ctx, annot->page->doc, annot->obj))
-                       annot->page->doc->dirty = 1;
        }
        fz_always(ctx)
                end_annot_op(ctx, annot);
index 0db70da39b2679266b1e73387999d9936fb628ec..a120099579f8ca99c58aaa1a93853177bc242e33 100644 (file)
@@ -487,6 +487,7 @@ pdf_token_from_keyword(char *key)
                break;
        case 'n':
                if (!strcmp(key, "null")) return PDF_TOK_NULL;
+               if (!strcmp(key, "newobj")) return PDF_TOK_NEWOBJ;
                break;
        case 'o':
                if (!strcmp(key, "obj")) return PDF_TOK_OBJ;
index 6a5b630aee9b764122c9d9380335d5d85dc5e873..b0c1605ad80f9af59944272456296d955fc738b6 100644 (file)
@@ -105,6 +105,7 @@ typedef struct pdf_journal_fragment
        struct pdf_journal_fragment *prev;
 
        int obj_num;
+       int newobj;
        pdf_obj *inactive;
        fz_buffer *stream;
 } pdf_journal_fragment;
@@ -980,10 +981,14 @@ swap_fragments(fz_context *ctx, pdf_document *doc, pdf_journal_entry *entry)
                pdf_xref_entry *xre;
                pdf_obj *old;
                fz_buffer *obuf;
-               xre = pdf_get_xref_entry(ctx, doc, frag->obj_num);
+               int type;
+               xre = pdf_get_incremental_xref_entry(ctx, doc, frag->obj_num);
                old = xre->obj;
                obuf = xre->stm_buf;
                xre->obj = frag->inactive;
+               type = xre->type;
+               xre->type = frag->newobj ? 0 : 'o';
+               frag->newobj = type == 0;
                xre->stm_buf = frag->stream;
                frag->inactive = old;
                frag->stream = obuf;
@@ -1073,7 +1078,7 @@ pdf_fingerprint_file(fz_context *ctx, pdf_document *doc, unsigned char digest[16
        fz_md5 state;
 
        fz_md5_init(&state);
-       fz_md5_update_int64(&state, doc->num_xref_sections);
+       fz_md5_update_int64(&state, doc->num_xref_sections-i);
        for (; i < doc->num_xref_sections; i++)
        {
                pdf_xref_subsec *subsec = doc->xref_sections[i].subsec;
@@ -1095,24 +1100,29 @@ pdf_serialise_journal(fz_context *ctx, pdf_document *doc, fz_output *out)
        int currentpos = 0;
        unsigned char digest[16];
        int i;
+       int nis = doc->num_incremental_sections;
 
-       pdf_fingerprint_file(ctx, doc, digest, doc->num_incremental_sections);
+       pdf_fingerprint_file(ctx, doc, digest, nis);
+
+       if (!pdf_has_unsaved_changes(ctx, doc))
+               nis = 0;
 
        fz_write_printf(ctx, out, "%!MuPDF-Journal-100\n");
        fz_write_string(ctx, out, "\njournal\n<<\n");
-       fz_write_printf(ctx, out, "/NumSections %d\n", doc->num_incremental_sections);
+       fz_write_printf(ctx, out, "/NumSections %d\n", nis);
        fz_write_printf(ctx, out, "/FileSize %ld\n", doc->file_size);
        fz_write_printf(ctx, out, "/Fingerprint <");
        for (i = 0; i < 16; i++)
                fz_write_printf(ctx, out, "%02x", digest[i]);
        fz_write_printf(ctx, out, ">\n");
 
-       for (entry = doc->journal->head; entry != NULL; entry = entry->next)
-       {
-               currentpos++;
-               if (entry == doc->journal->current)
-                       break;
-       }
+       if (doc->journal->current != NULL)
+               for (entry = doc->journal->head; entry != NULL; entry = entry->next)
+               {
+                       currentpos++;
+                       if (entry == doc->journal->current)
+                               break;
+               }
        fz_write_printf(ctx, out, "/HistoryPos %d\n", currentpos);
        fz_write_string(ctx, out, ">>\n");
 
@@ -1122,7 +1132,12 @@ pdf_serialise_journal(fz_context *ctx, pdf_document *doc, fz_output *out)
                fz_write_printf(ctx, out, "entry\n%(\n", entry->title);
                for (frag = entry->head; frag != NULL; frag = frag->next)
                {
-                       fz_write_printf(ctx, out, "%d 0 obj\n", frag->obj_num, 0);
+                       if (frag->newobj)
+                       {
+                               fz_write_printf(ctx, out, "%d 0 newobj\n", frag->obj_num);
+                               continue;
+                       }
+                       fz_write_printf(ctx, out, "%d 0 obj\n", frag->obj_num);
                        pdf_print_encrypted_obj(ctx, out, frag->inactive, 1, 0, NULL, frag->obj_num, 0);
                        if (frag->stream)
                        {
@@ -1137,7 +1152,7 @@ pdf_serialise_journal(fz_context *ctx, pdf_document *doc, fz_output *out)
 }
 
 static void
-add_fragment(fz_context *ctx, pdf_document *doc, int parent, pdf_obj *copy, fz_buffer *copy_stream)
+add_fragment(fz_context *ctx, pdf_document *doc, int parent, pdf_obj *copy, fz_buffer *copy_stream, int newobj)
 {
        pdf_journal_entry *entry = doc->journal->current;
        pdf_journal_fragment *frag;
@@ -1168,6 +1183,7 @@ add_fragment(fz_context *ctx, pdf_document *doc, int parent, pdf_obj *copy, fz_b
                        entry->tail->next = frag;
                }
                entry->tail = frag;
+               frag->newobj = newobj;
                frag->inactive = copy;
                frag->stream = copy_stream;
        }
@@ -1252,6 +1268,7 @@ void pdf_deserialise_journal(fz_context *ctx, pdf_document *doc, fz_stream *stm)
 
        while (1)
        {
+               int newobj;
                fz_skip_space(ctx, stm);
 
                if (fz_skip_string(ctx, stm, "entry\n") == 0)
@@ -1276,9 +1293,9 @@ void pdf_deserialise_journal(fz_context *ctx, pdf_document *doc, fz_stream *stm)
                        fz_throw(ctx, FZ_ERROR_GENERIC, "Badly formed journal");
 
                /* Read the object/stream for the next fragment. */
-               obj = pdf_parse_journal_obj(ctx, doc, stm, &num, &buffer);
+               obj = pdf_parse_journal_obj(ctx, doc, stm, &num, &buffer, &newobj);
 
-               add_fragment(ctx, doc, num, obj, buffer);
+               add_fragment(ctx, doc, num, obj, buffer, newobj);
        }
 
        fz_skip_space(ctx, stm);
@@ -1316,6 +1333,7 @@ static void prepare_object_for_alteration(fz_context *ctx, pdf_obj *obj, pdf_obj
        pdf_obj *copy = NULL;
        pdf_obj *orig;
        fz_buffer *copy_stream = NULL;
+       int was_empty;
 
        /*
                obj should be a dict or an array. We don't care about
@@ -1381,31 +1399,29 @@ static void prepare_object_for_alteration(fz_context *ctx, pdf_obj *obj, pdf_obj
                }
        }
 
-       /*
-               Otherwise we need to ensure that the containing hierarchy of objects
-               has been moved to the incremental xref section.
-       */
-       pdf_xref_ensure_incremental_object(ctx, doc, parent);
-
-       if (doc->journal == NULL)
-               return;
-
-       entry = doc->journal->current;
-       if (entry == NULL)
-       {
-               /* We are adding to an implicit entry being the first
-                * one on the list. i.e. we just bin anything, it's not
-                * undoable. */
-               return;
-       }
+       /* Find the journal entry to which we will add this object, if there is one. */
+       entry = doc->journal ? doc->journal->current : NULL;
 
        /* We are about to add a fragment. Everything after this in the
         * history must be thrown away. */
-       discard_journal_entries(ctx, &entry->next);
+       if (entry)
+               discard_journal_entries(ctx, &entry->next);
 
        for (frag = entry->head; frag != NULL; frag = frag->next)
                if (frag->obj_num == parent)
-                       return; /* Already stashed this one! */
+               {
+                       entry = NULL;
+                       break; /* Already stashed this one! */
+               }
+
+       /*
+               We need to ensure that the containing hierarchy of objects
+               has been moved to the incremental xref section.
+       */
+       was_empty = pdf_xref_ensure_incremental_object(ctx, doc, parent);
+
+       if (entry == NULL)
+               return;
 
        orig = pdf_load_object(ctx, doc, parent);
 
@@ -1414,11 +1430,19 @@ static void prepare_object_for_alteration(fz_context *ctx, pdf_obj *obj, pdf_obj
 
        fz_try(ctx)
        {
-               copy = pdf_deep_copy_obj(ctx, orig);
-               pdf_set_obj_parent(ctx, copy, parent);
-               if (pdf_obj_num_is_stream(ctx, doc, parent))
-                       copy_stream = pdf_load_raw_stream_number(ctx, doc, parent);
-               add_fragment(ctx, doc, parent, copy, copy_stream);
+               if (was_empty)
+               {
+                       copy = NULL;
+                       copy_stream = NULL;
+               }
+               else
+               {
+                       copy = pdf_deep_copy_obj(ctx, orig);
+                       pdf_set_obj_parent(ctx, copy, parent);
+                       if (pdf_obj_num_is_stream(ctx, doc, parent))
+                               copy_stream = pdf_load_raw_stream_number(ctx, doc, parent);
+               }
+               add_fragment(ctx, doc, parent, copy, copy_stream, was_empty);
        }
        fz_always(ctx)
        {
index 46fc6f402bbb0f8e0f716a4fb4cabce06f4bb29f..4e9ee7fedf7b26184ccf5c2b9f8d6589179ee957 100644 (file)
@@ -717,8 +717,8 @@ pdf_parse_stm_obj(fz_context *ctx, pdf_document *doc, fz_stream *file, pdf_lexbu
 }
 
 pdf_obj *
-pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *file,
-       int *onum, int *ogen, int64_t *ostmofs, int *try_repair)
+pdf_parse_ind_obj_or_newobj(fz_context *ctx, pdf_document *doc, fz_stream *file,
+       int *onum, int *ogen, int64_t *ostmofs, int *try_repair, int *newobj)
 {
        pdf_obj *obj = NULL;
        int num = 0, gen = 0;
@@ -757,6 +757,14 @@ pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *file,
        }
 
        tok = pdf_lex(ctx, file, buf);
+       if (tok == PDF_TOK_NEWOBJ && newobj)
+       {
+               *newobj = 1;
+               if (onum) *onum = num;
+               if (ogen) *ogen = gen;
+               if (ostmofs) *ostmofs = 0;
+               return NULL;
+       }
        if (tok != PDF_TOK_OBJ)
        {
                if (try_repair)
@@ -857,16 +865,24 @@ pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *file,
        return obj;
 }
 
+pdf_obj *
+pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *file,
+       int *onum, int *ogen, int64_t *ostmofs, int *try_repair)
+{
+       return pdf_parse_ind_obj_or_newobj(ctx, doc, file, onum, ogen, ostmofs, try_repair, NULL);
+}
+
 pdf_obj *
 pdf_parse_journal_obj(fz_context *ctx, pdf_document *doc, fz_stream *stm,
-       int *onum, fz_buffer **ostm)
+       int *onum, fz_buffer **ostm, int *newobj)
 {
        pdf_obj *obj = NULL;
        pdf_token tok;
        pdf_lexbuf *buf = &doc->lexbuf.base;
        int64_t stmofs;
 
-       obj = pdf_parse_ind_obj(ctx, doc, stm, onum, NULL, &stmofs, NULL);
+       *newobj = 0;
+       obj = pdf_parse_ind_obj_or_newobj(ctx, doc, stm, onum, NULL, &stmofs, NULL, newobj);
        /* This will have consumed either the stream or the endobj keywords. */
 
        *ostm = NULL;
index c4d52c7fb680b6780b2a8f825ed836f285552443..b1f2d8c0aacc2866335127eafa5e830065f1e375 100644 (file)
@@ -332,8 +332,6 @@ pdf_repair_xref(fz_context *ctx, pdf_document *doc)
                fz_throw(ctx, FZ_ERROR_GENERIC, "Repair failed already - not trying again");
        doc->repair_attempted = 1;
 
-       doc->dirty = 1;
-
        pdf_forget_xref(ctx, doc);
 
        fz_seek(ctx, doc->file, 0, 0);
index 86d4c9e6c54e4c4ff72abc51f86f2f7672de6ff0..c5f8e46695b1b4d9d246f4719073c84013eb59c6 100644 (file)
@@ -3454,7 +3454,7 @@ do_pdf_save_document(fz_context *ctx, pdf_document *doc, pdf_write_state *opts,
                ensure_initial_incremental_contents(ctx, doc->file, opts->out, doc->file_size);
 
                /* If no changes, nothing more to write */
-               if (doc->num_incremental_sections == 0)
+               if (!pdf_has_unsaved_changes(ctx, doc))
                {
                        doc->save_in_progress = 0;
                        return;
@@ -3655,7 +3655,6 @@ do_pdf_save_document(fz_context *ctx, pdf_document *doc, pdf_write_state *opts,
                if (!in_opts->do_snapshot)
                {
                        complete_signatures(ctx, doc, opts);
-                       doc->dirty = 0;
                }
        }
        fz_always(ctx)
index 22d24957353cb10d4b1da5e78fb295e50f67316b..e073c31e56b5260516d42af1e6806a8c75dd2fb9 100644 (file)
@@ -463,7 +463,7 @@ static void ensure_incremental_xref(fz_context *ctx, pdf_document *doc)
 }
 
 /* Used when altering a document */
-static pdf_xref_entry *pdf_get_incremental_xref_entry(fz_context *ctx, pdf_document *doc, int i)
+pdf_xref_entry *pdf_get_incremental_xref_entry(fz_context *ctx, pdf_document *doc, int i)
 {
        pdf_xref *xref;
        pdf_xref_subsec *sub;
@@ -577,7 +577,7 @@ void pdf_ensure_solid_xref(fz_context *ctx, pdf_document *doc, int num)
        ensure_solid_xref(ctx, doc, num, doc->num_xref_sections-1);
 }
 
-void pdf_xref_ensure_incremental_object(fz_context *ctx, pdf_document *doc, int num)
+int pdf_xref_ensure_incremental_object(fz_context *ctx, pdf_document *doc, int num)
 {
        pdf_xref_entry *new_entry, *old_entry;
        pdf_xref_subsec *sub = NULL;
@@ -605,26 +605,20 @@ void pdf_xref_ensure_incremental_object(fz_context *ctx, pdf_document *doc, int
 
        /* If we don't find it, or it's already in the incremental section, return */
        if (i == 0 || sub == NULL)
-               return;
+               return 0;
 
        /* Move the object to the incremental section */
        doc->xref_index[num] = 0;
        old_entry = &sub->table[num - sub->start];
        new_entry = pdf_get_incremental_xref_entry(ctx, doc, num);
        *new_entry = *old_entry;
-       if (i < doc->num_incremental_sections)
-       {
-               /* old entry is incremental and may have changes.
-                * Better keep a copy. We must override the old entry with
-                * the copy because the caller may be holding a reference to
-                * the original and expect it to end up in the new entry */
-               old_entry->obj = pdf_deep_copy_obj(ctx, old_entry->obj);
-       }
-       else
-       {
-               old_entry->obj = NULL;
-       }
+       /* Better keep a copy. We must override the old entry with
+        * the copy because the caller may be holding a reference to
+        * the original and expect it to end up in the new entry */
+       old_entry->obj = pdf_deep_copy_obj(ctx, old_entry->obj);
        old_entry->stm_buf = NULL;
+
+       return 1;
 }
 
 void pdf_xref_ensure_local_object(fz_context *ctx, pdf_document *doc, int num)