Re: Fix: Bug 687560 "Invalid PDF if /BP pdfmarks withnon-unique /_objdef"
"SaGS" <[email protected]> Mon, 1 Nov 2004 19:33:22 +0200
| Newsgroups | gmane.comp.printing.ghostscript.patches |
|---|---|
| Message-ID | <[email protected]> |
Hello, Attached is a new version of the patch for bug 687560 "Invalid PDF if /BP pdfmarks with non-unique /_objdef". I have removed pdf_obj_ref_reused() and all changes to pdf_enter_substream(), pdf_open_aside() and pdf_alloc_aside(). Instead, the new code does quite some "surgery" on cos_object_ts. _______________________________________________ gs-code-review mailing list [email protected] http://www.ghostscript.com/mailman/listinfo/gs-code-review
Bug687560-v3.diff
(application/octet-stream, 17 KB)
Index: gdevpdfm.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gdevpdfm.c,v
retrieving revision 1.47
diff -u -r1.47 gdevpdfm.c
--- gdevpdfm.c 7 Oct 2004 05:18:34 -0000 1.47
+++ gdevpdfm.c 1 Nov 2004 05:24:06 -0000
@@ -341,6 +341,49 @@
return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames);
}
+/* Ensure object has a pdf_resource_t attached */
+private int
+pdf_obj_addresource (gx_device_pdf *pdev, pdf_resource_type_t rtype, cos_object_t *pco)
+{
+ int code;
+ if (pco->pres == 0) {
+ /* (let pdf_alloc_aside() be the only one to handle pdf_resource_t creation) */
+ code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, gs_no_id),
+ pdf_resource_type_structs[rtype], &pco->pres, -1);
+ if (code < 0)
+ return code;
+ COS_FREE (pco->pres->object,"pdf_obj_addresource()");
+ pco->pres->object = pco;
+ if (pco->id) pdf_reserve_object_id (pdev, pco->pres, pco->id);
+ }
+ return 0;
+}
+
+/* Create a unique distill-time objname for driver's internal use */
+private int
+pdf_unique_objname (char *objname)
+{
+ static long n_objname = 0; /* (no need to be context-specific) */
+ /* Notes:
+ * - Names generated by different pdf_unique_objname() calls are different because
+ * n_objname is incremented with each call.
+ * - All names generated by pdf_unique_objname() are different from all names coming
+ * from PostScript code because they are not enclosed in "{}".
+ */
+ return sprintf (objname,"?%ld",n_objname++);
+}
+
+/* Contants that define various methods to deal with duplicate distill-time names */
+#define PDF_NAMECOLLISION_NONAME 0x0001 /* not a named object (so no collision possible) */
+#define PDF_NAMECOLLISION_NONE 0x0002 /* no name collision */
+#define PDF_NAMECOLLISION_ERRCODE 0x0004 /* error, "return code" */
+#define PDF_NAMECOLLISION_ERRTYPE 0x0008 /* "return_error(gs_error_typecheck)" */
+#define PDF_NAMECOLLISION_ERRRANGE 0x0010 /* "return_error(gs_error_rangecheck)" */
+#define PDF_NAMECOLLISION_REBUILD 0x0020 /* "rebuid" the same object, discard old copy */
+#define PDF_NAMECOLLISION_KEEPOLD 0x0040 /* keep old copy, discard new one */
+#define PDF_NAMECOLLISION_BACKUP 0x0080 /* "backup" previous copy, then create a new obj */
+#define PDF_NAMECOLLISION_FWDOBJ 0x0100 /* false collision due to a forward reference */
+
/* ---------------- Miscellaneous pdfmarks ---------------- */
/*
@@ -1052,27 +1095,174 @@
cos_stream_t *pcs;
cos_value_t value;
int code;
+ int oper; /* one or more of the PDF_NAMECOLLISION_* values */
+ cos_object_t *pcoprev;
code = pdf_open_page(pdev, PDF_IN_STREAM);
if (code < 0)
return code;
- code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, true,
- pdev->CompressFonts /* Have no better switch*/);
- if (code < 0)
- return code;
- pcs = (cos_stream_t *)pres->object;
- pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject");
- if (!pdev->substream_Resources)
- return_error(gs_error_VMerror);
- code = cos_dict_put(pdev->local_named_objects, objname->data,
- objname->size, cos_object_value(&value, pres->object));
- if (code < 0)
- return code;
- pres->named = true;
- pres->where_used = 0; /* initially not used */
- pcs->pres = pres;
- *ppcs = pcs;
- return 0;
+
+ oper = 0;
+ if (objname) {
+ /* check for duplicate name and see which methods are suitable for resolving the conflict */
+ switch (code = pdf_find_named(pdev, objname, &pcoprev)) {
+ case gs_error_undefined: /* new name */
+ oper = PDF_NAMECOLLISION_NONE;
+ break;
+ case 0: /* duplicate name */
+ if (cos_type(pcoprev) == cos_type_generic)
+ /* (assumes named generic objects in pdev->local_named_objects are
+ created only as a result of finding forward references) */
+ oper = PDF_NAMECOLLISION_FWDOBJ | PDF_NAMECOLLISION_REBUILD;
+ else if (cos_type(pcoprev) != cos_type_stream)
+ /* do not accept changing the type of an object */
+ oper = PDF_NAMECOLLISION_ERRTYPE;
+ else if (pcoprev->is_graphics && pcoprev->is_open)
+ /* do not accept redefining a Form XObject while it is being built */
+ oper = PDF_NAMECOLLISION_ERRRANGE;
+ else
+ oper = PDF_NAMECOLLISION_KEEPOLD | PDF_NAMECOLLISION_REBUILD | PDF_NAMECOLLISION_BACKUP;
+ break;
+ default: /* misc errors */
+ oper = PDF_NAMECOLLISION_ERRCODE;
+ break;
+ }
+ } else
+ oper = PDF_NAMECOLLISION_NONAME;
+ /* cannot rebuild an object that is already written */
+ if ((oper &PDF_NAMECOLLISION_REBUILD) && pcoprev->written)
+ oper &= ~PDF_NAMECOLLISION_REBUILD;
+ /* favor PDF_NAMECOLLISION_FWDOBJ over PDF_NAMECOLLISION_REBUILD */
+ if (oper &PDF_NAMECOLLISION_FWDOBJ)
+ oper &= ~PDF_NAMECOLLISION_REBUILD;
+ /* favor PDF_NAMECOLLISION_REBUILD over PDF_NAMECOLLISION_KEEPOLD over PDF_NAMECOLLISION_BACKUP */
+ if (oper &PDF_NAMECOLLISION_REBUILD)
+ oper &= ~(PDF_NAMECOLLISION_BACKUP|PDF_NAMECOLLISION_KEEPOLD);
+ if (oper &PDF_NAMECOLLISION_BACKUP)
+ oper &= ~(PDF_NAMECOLLISION_KEEPOLD);
+ /* the various methods of resolving name conflicts */
+ switch (oper) {
+ case PDF_NAMECOLLISION_ERRCODE:
+ return code;
+ case PDF_NAMECOLLISION_ERRTYPE:
+ return_error(gs_error_typecheck);
+ case PDF_NAMECOLLISION_ERRRANGE:
+ return_error(gs_error_rangecheck);
+ default:
+ /* should never happen; if it does, the decision code above is incomplete */
+ return_error(gs_error_unknownerror);
+ case PDF_NAMECOLLISION_FWDOBJ:
+ /* false collision due to a forward reference, resolved by "continuing" with
+ * the old object from where pdf_replace_names() left off
+ * - convert the old object into a stream
+ * - allocate a pdf_resource_t if not already done
+ * - continue as for PDF_NAMECOLLISION_REBUILD
+ */
+ if ((code = cos_become (pcoprev, cos_type_stream)) < 0 ||
+ (code = pdf_obj_addresource (pdev, resourceXObject, pcoprev)) < 0 )
+ return code;
+ pcoprev->is_graphics = true;
+ pres = pcoprev->pres;
+ pres->named = true;
+ pres->where_used = 0; /* initially not used */
+ oper = PDF_NAMECOLLISION_REBUILD; /* continue as for "rebuilding" the object...*/
+ case PDF_NAMECOLLISION_REBUILD: {
+ /* "rebuid" the same object, replacing the old copy
+ * - create a new stream+resource, without an id
+ * - do some "surgery" to keep the contents from the newly allocated stream
+ * and everything else (id, resource) from the old one
+ * - "reopen" the old object
+ * - take care the contents to be written to the old stream object
+ * - free what's not needed any more (new resource, new emptied stream)
+ * - do not add the object to pdev->local_named_objects, it's already there
+ * - pcoprev->named/where_used already in place
+ */
+ code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
+ pdev->CompressFonts /* Have no better switch*/);
+ if (code < 0)
+ return code;
+ if ((code = cos_stream_move_all ((cos_stream_t *)pcoprev,(cos_stream_t *)pres->object)) < 0)
+ return code;
+ pcoprev->is_open = pres->object->is_open;
+ cos_write_stream_redir (pdev->strm, (cos_stream_t *)pcoprev);
+ if ((code = pdf_cancel_resource (pdev,pres,resourceXObject)) < 0)
+ return code;
+ pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject(REBUILD.substreamresources)");
+ if (!pdev->substream_Resources)
+ return_error(gs_error_VMerror);
+ *ppcs = (cos_stream_t *)pcoprev;
+ return 0;
+ }
+ case PDF_NAMECOLLISION_KEEPOLD:
+ /* keep old copy, discard new one
+ * - create a "temporary object" without an ID
+ * - do not add the object to pdev->local_named_objects, because:
+ * - adding it removes the old object
+ * - it does not have an ID, so it must not be accessible by name since
+ * we cannot create references ("n 0 R") to it
+ * - pdfmark_EP() will discard the object
+ */
+ code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
+ pdev->CompressFonts /* Have no better switch*/);
+ if (code < 0)
+ return code;
+ pcs = (cos_stream_t *)pres->object;
+ pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject(KEEPOLD)");
+ if (!pdev->substream_Resources)
+ return_error(gs_error_VMerror);
+ pres->named = true;
+ pres->where_used = 0; /* initially not used */
+ pcs->pres = pres;
+ *ppcs = pcs;
+ return 0;
+ case PDF_NAMECOLLISION_BACKUP:
+ /* "backup" previous copy, then create a new object
+ * (an object name is treated like a variable: pdfmarks that create objects
+ * "assign" a (new) value to it, and each reference to the object takes
+ * into account the most recent value (in execution order))
+ * - replace the key in pdev->local_named_objects with a unique one
+ * - continue as if no name conflict
+ */
+ { char unique[1+10+1]; /* '?' + long + '\0' */
+ code = cos_dict_rename(pdev->local_named_objects,
+ objname->data,objname->size,
+ (const byte *)unique,pdf_unique_objname(unique));
+ if (code < 0)
+ return code;
+ }
+ oper = PDF_NAMECOLLISION_NONE; /* continue as for PDF_NAMECOLLISION_NONE... */
+ case PDF_NAMECOLLISION_NONAME:
+ /* object does not have a name
+ * - same as PDF_NAMECOLLISION_NONE, except the object is not registered
+ * in pdev->local_named_objects.
+ */
+ case PDF_NAMECOLLISION_NONE:
+ /* no name conflict
+ * - create the object normally and initialize all its fields
+ * - do assign the object an id
+ * - register the object in pdev->local_named_objects
+ */
+ code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, true,
+ pdev->CompressFonts /* Have no better switch*/);
+ if (code < 0)
+ return code;
+ pcs = (cos_stream_t *)pres->object;
+ pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject(NONAMECOLLISION)");
+ if (!pdev->substream_Resources)
+ return_error(gs_error_VMerror);
+ if (!(oper &PDF_NAMECOLLISION_NONAME))
+ code = cos_dict_put(pdev->local_named_objects, objname->data,
+ objname->size, cos_object_value(&value, pres->object));
+ else
+ DO_NOTHING;
+ if (code < 0)
+ return code;
+ pres->named = !(oper &PDF_NAMECOLLISION_NONAME);
+ pres->where_used = 0; /* initially not used */
+ pcs->pres = pres;
+ *ppcs = pcs;
+ return 0;
+ }
}
/* PS pdfmark */
@@ -1463,13 +1653,23 @@
const gs_matrix * pctm, const gs_param_string * no_objname)
{
int code;
+ pdf_resource_t *pres_cancel;
- code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
- if (code < 0)
- return code;
+ pres_cancel = pdev->accumulating_substream_resource;
+ if (pres_cancel->object->id > 0) pres_cancel = NULL;
+ if (!pres_cancel) {
+ code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
+ if (code < 0)
+ return code;
+ }
code = pdf_exit_substream(pdev);
if (code < 0)
return code;
+ if (pres_cancel) {
+ code = pdf_cancel_resource(pdev, pres_cancel, resourceXObject);
+ if (code < 0)
+ return code;
+ }
return 0;
}
@@ -1483,10 +1683,28 @@
if (count != 1)
return_error(gs_error_rangecheck);
- if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
+ /* misc errors */
return code;
- if (pco->is_open || !pco->is_graphics)
- return_error(gs_error_rangecheck);
+ if (cos_type(pco) == cos_type_generic) {
+ /* forward reference
+ * - use the object without additional checks
+ * - be sure it has an associated pdf_resource_t structure, since we need to
+ * store some information in object->pres->where_used
+ * - don't turn it into a stream object (let it a generic one), since the object
+ * is not yet created (from the PostScript code point of view) and references
+ * to it are still "forward"
+ */
+ if ((code = pdf_obj_addresource (pdev, resourceXObject, pco)) < 0)
+ return code;
+ pco->pres->named = true;
+ pco->is_graphics = true;
+ } else if (cos_type(pco) == cos_type_stream) {
+ /* object exists as a stream */
+ if (!pco->is_graphics || pco->is_open)
+ return_error(gs_error_rangecheck);
+ } else
+ return_error(gs_error_typecheck);
code = pdf_open_contents(pdev, PDF_IN_STREAM);
if (code < 0)
return code;
Index: gdevpdfo.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gdevpdfo.c,v
retrieving revision 1.30
diff -u -r1.30 gdevpdfo.c
--- gdevpdfo.c 8 Jun 2004 11:43:03 -0000 1.30
+++ gdevpdfo.c 1 Nov 2004 05:24:12 -0000
@@ -986,6 +986,36 @@
return cos_dict_put_c_key(pcd, key, cos_c_string_value(&cvalue, value));
}
+/* "Rename" a dict entry by changing the key without affecting the value */
+int
+cos_dict_rename(cos_dict_t *pcd, const byte *oldkey_data, uint oldkey_size,
+ const byte *newkey_data, uint newkey_size)
+{
+ gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
+ cos_dict_element_t **ppcde = &pcd->elements;
+ cos_dict_element_t *next;
+ byte *copied_key_data;
+ int code;
+
+ while ((next = *ppcde) != 0)
+ if (bytes_compare(next->key.data, next->key.size, oldkey_data, oldkey_size))
+ ppcde = &next->next;
+ else {
+ copied_key_data = gs_alloc_string(mem, newkey_size,"cos_dict_rename(new key)");
+ if (copied_key_data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(copied_key_data, newkey_data, newkey_size);
+ if (next->owns_key)
+ gs_free_const_string(mem, next->key.data, next->key.size,
+ "cos_dict_rename(old key)");
+ next->key.data = copied_key_data;
+ next->key.size = newkey_size;
+ next->owns_key = true;
+ return 0;
+ }
+ return_error(gs_error_undefined);
+}
+
/* Move all the elements from one dict to another. */
int
cos_dict_move_all(cos_dict_t *pcdto, cos_dict_t *pcdfrom)
@@ -1399,6 +1429,27 @@
return code;
}
+/* move all [dict] elements and [stream proper] contents from one stream to another
+ * - existing key-value pairs in the destination object are removed (this is different
+ * from cos_dict_move_all()'s behaviour)
+ */
+int
+cos_stream_move_all(cos_stream_t *pcsto, cos_stream_t *pcsfrom)
+{
+ int code;
+ if (pcsto->input_strm)
+ sclose (pcsto->input_strm);
+ if ((code = cos_stream_release_pieces (pcsto)) < 0)
+ return code;
+ cos_stream_release (COS_OBJECT(pcsto), "cos_stream_move_all()");
+ if ((code = cos_dict_move_all (cos_stream_dict(pcsto),cos_stream_dict(pcsfrom))) < 0)
+ return code;
+ pcsto->pieces = pcsfrom->pieces, pcsfrom->pieces = 0;
+ pcsto->length = pcsfrom->length, pcsfrom->length = 0;
+ pcsto->input_strm = pcsfrom->input_strm, pcsfrom->input_strm = 0;
+ return 0;
+}
+
/* Release the last contents piece of a stream object. */
/* Warning : this function can't release pieces if another stream is written after them. */
int
@@ -1501,6 +1552,20 @@
gs_free_object(mem, s, cname);
return 0;
}
+/* redirect a cos_write_stream to a different cos_stream */
+cos_stream_t *
+cos_write_stream_redir (stream *s, cos_stream_t *pcs)
+{
+ cos_stream_t *pcsprev;
+ cos_write_stream_state_t *ss;
+
+ while(s->procs.process != cos_s_procs.process)
+ s = s->strm;
+ ss = (cos_write_stream_state_t *)s->state;
+ pcsprev = ss->pcs;
+ ss->pcs = pcs;
+ return pcsprev; /* return previous cos_stream */
+}
/* Get cos stream from pipeline. */
cos_stream_t *
Index: gdevpdfo.h
===================================================================
RCS file: /cvs/ghostscript/gs/src/gdevpdfo.h,v
retrieving revision 1.19
diff -u -r1.19 gdevpdfo.h
--- gdevpdfo.h 8 Jun 2004 11:43:04 -0000 1.19
+++ gdevpdfo.h 1 Nov 2004 05:24:14 -0000
@@ -251,12 +251,17 @@
int cos_dict_put_string(cos_dict_t *, const byte *, uint, const byte *, uint);
int cos_dict_put_string_copy(cos_dict_t *pcd, const char *key, const char *value);
int cos_dict_put_c_strings(cos_dict_t *, const char *, const char *);
+/* rename an entry by changing the key without affecting the value */
+int cos_dict_rename(cos_dict_t *pcd, const byte *oldkey_data, uint oldkey_size,
+ const byte *newkey_data, uint newkey_size);
/* move all the elements from one dict to another */
int cos_dict_move_all(cos_dict_t *, cos_dict_t *);
/* stream */
int cos_stream_add(cos_stream_t *, uint);
int cos_stream_add_bytes(cos_stream_t *, const byte *, uint);
int cos_stream_add_stream_contents(cos_stream_t *, stream *);
+/* move all [dict] elements and [stream proper] contents from one stream to another */
+int cos_stream_move_all(cos_stream_t *pcsto, cos_stream_t *pcsfrom);
int cos_stream_release_pieces(cos_stream_t *pcs);
cos_dict_t *cos_stream_dict(cos_stream_t *);
@@ -297,6 +302,8 @@
stream *cos_write_stream_alloc(cos_stream_t *pcs, gx_device_pdf *pdev,
client_name_t cname);
+/* redirect a cos_write_stream to a different cos_stream */
+cos_stream_t *cos_write_stream_redir (stream *s, cos_stream_t *pcs);
/* Get cos stream from pipeline. */
cos_stream_t * cos_stream_from_pipeline(stream *s);
/* Get cos write stream from pipeline. */