pdf_js *js;
int recalculate;
- int dirty;
int redacted;
pdf_doc_event_cb *event_cb;
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;
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
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);
else
title = filename;
- if (pdf && pdf->dirty)
+ if (pdf && pdf_has_unsaved_changes(ctx, pdf))
extra = "*";
n = strlen(title);
int was_dirty = 0;
if (pdf)
{
- was_dirty = pdf->dirty;
+ was_dirty = pdf_has_unsaved_changes(ctx, pdf);
}
do_app();
if (pdf)
{
- if (was_dirty != pdf->dirty)
+ if (was_dirty != pdf_has_unsaved_changes(ctx, pdf))
update_title();
}
}
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 *
*page->annot_tailp = annot;
page->annot_tailp = &annot->next;
}
-
- doc->dirty = 1;
}
fz_always(ctx)
{
linkp = &(*linkp)->next;
*linkp = link;
-
- doc->dirty = 1;
}
fz_always(ctx)
{
/* And free it. */
pdf_drop_annot(ctx, annot);
-
- doc->dirty = 1;
}
fz_always(ctx)
pdf_end_operation(ctx, page->doc);
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)
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)
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)
return 0;
}
- if (pdf_field_dirties_document(ctx, doc, field))
- doc->dirty = 1;
update_field_value(ctx, doc, field, text);
return 1;
{
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;
}
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);
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;
struct pdf_journal_fragment *prev;
int obj_num;
+ int newobj;
pdf_obj *inactive;
fz_buffer *stream;
} pdf_journal_fragment;
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;
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;
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");
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)
{
}
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;
entry->tail->next = frag;
}
entry->tail = frag;
+ frag->newobj = newobj;
frag->inactive = copy;
frag->stream = copy_stream;
}
while (1)
{
+ int newobj;
fz_skip_space(ctx, stm);
if (fz_skip_string(ctx, stm, "entry\n") == 0)
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);
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
}
}
- /*
- 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);
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)
{
}
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;
}
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)
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;
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);
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;
if (!in_opts->do_snapshot)
{
complete_signatures(ctx, doc, opts);
- doc->dirty = 0;
}
}
fz_always(ctx)
}
/* 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;
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;
/* 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)