Re: Re: Bug 687560 "Invalid PDF if /BP pdfmarks withnon-unique /_objdef"

"SaGS" <[email protected]> Thu, 25 Nov 2004 22:40:38 +0200
Newsgroups gmane.comp.printing.ghostscript.patches
Message-ID <[email protected]>
----- Original Message -----
From: "Igor V. Melichev" <[email protected]>
To: "SaGS" <[email protected]>; "gs-code-review"
<[email protected]>
Sent: Thursday, 25 November 2004 09:32
Subject: [gs-code-review] Re: Bug 687560 "Invalid PDF if /BP pdfmarks
withnon-unique /_objdef"


> ...
> > I chose to resolve each {OBJRef} to the MOST RECENT definition in
execution order; "forward references" (= {OBJRef}
> > with no preceding definition of that object) are resolved to the FIRST
occurrence.
>
> 1. Please prove that this behavior is same as Adobe Distiller. Thank you.

This requirement does not make any sense. The above phrase refers to
PDF_NAMECOLLISION_REBUILD and if you will search the bug report for the 3
occurrences of "Compatibility:" you will see I wrote that Distiller
implements PDF_NAMECOLLISION_KEEPOLD. (Note: Those conclusions should be
considered guesses based on feeding files to Distiller or Jaws PDF Creator
and looking at what comes out. All this stuff is not documented at all and I
don't have privileged access to Adobe Systems or Global Graphics.)

A word about "I chose to": actually I don't see other reasonable choice. I
like the idea of "dynamic scoping", particularly because it maximizes
independence among objects.

>
> 2. Now I see the reason for the "surgery". You need to put "x 0 R" into
the output
> PDF when the object ID=x is not yet created. However the function
pdf_enter_substream
> has the argument "bool reserve_object_id", which works exactly for this
case with the value "false".
> In this case the existing code calls pdf_reserve_object_id later.
>
> To resolve the "surgery", I suggest to do the folowing :
>
> 1. When we need to write out a reference to an undefined object,
> create a fake object, and assign a new object id to it.
>
> 2. When the object is being defined later,
> create another cos object with pdf_enter_substream,
> drop the fake object with pdf_forget_resource,
> and set the object id to the new object equal to the one from the fake
object.
>

Nope. The "surgery" is needed by PDF_NAMECOLLISION_REBUILD, which kicks in
when THERE ALREADY ARE TOO MANY objects with ID=x (well, more exactly: one
already exists and a second one is coming).

Let me explain again:

    [{OBJECT_A} ... /BP pdfmark
    ... marking operators ...
    [/EP pdfmark
    ... some extra PostScript code ...
    [{OBJECT_A} ... /BP pdfmark <- here kicks in one of
PDF_NAMECOLLISION_REBUILD,
                                   PDF_NAMECOLLISION_KEEPOLD, or
PDF_NAMECOLLISION_BACKUP
    ... (possibly other) marking operators ...
    [/EP pdfmark

At the point I marked:
- we already have a complete Form XObject (created by the first /BP)
- this existing XObject must be registered in pdev->local_named_objects,
otherwise it cannot be found during "... some extra PostScript code ..."
- objects in pdev->local_named_objects MUST have IDs already assigned; this
is required by pdf_replace_names()/ pdfmark_next_object() (in gdevpdfr.c)
that process {OBJRefs} converting them to "n 0 Refs"; if objects have no
number, we get "0 0 R" or "-1 0 R", which are invalid and fixing them later
is impossible (there's no information about which object these are supposed
to point to).
- when the 2nd /BP appears, we must
   (a) create a dictionary because we need to store the /Matrix etc.
somewhere,
   (b) create a stream for the very next PostScript (marking) operators to
write into,
so in fact we have to prepare a complete Form XObject.

The intent of PDF_NAMECOLLISION_REBUILD is to make all existing references
(all kinds) to point to this new XObject, and "forget" the first one. Other
way to express this: get in the same state we would be if the first XObject
did not exist at all.

How do you find "reference to an undefined object" and "object is being
defined later" fit here?

-------

If you can guarantee there are no "C-ptrs" pointing to the old cos_object_t
structure (Note *), this old copy can be freed (logically and/or physically)
after moving whatever needs to be preserved to the new copy, and we don't
need cos_write_stream_redir()/ etc. (steps 2.1 .. 2.4 in your message dated
November 4th). If there are "C-ptrs" to the old cos_stream_t, we must
preserve the structure itself, so we need the "surgery" in order to link the
old structure in the new cos_object_t's place and move the new contents to
the old structure.

Another way to get rid of cos_dict_objects_write() is to move the stream's
contents (with cos_stream_move_all()) inside /EP, not inside /BP. But this
requires some place to store the information needed to locate the "old"
cos_stream_t, including a stack for /NamespacePush and /NamespacePop. I
consider this to be too involved.


(Note *) excluding those that start_XObject() and functions it calls are
supposed to handle: pointers from cos_object_t to its pdf_resource_t and
vice-versa, and those used to link the pdf_resource_t on the resource chain
and the cos_object_t into pdev->local_named_objects.

-------

I find there is another way to do as you say, but it is very involved and
it's not worth the effort:

- In pdev->local_named_objects keep the "fake objects" exclusively (to be
found by /PUT and {OBJRefs}), one fake object per distill-time name. These
are never deleted/ replaced (but they can be "emptied" if necessary);
- Create a structure parallel with pdev->local_named_objects to store the
"real" objects; these are not stored in pdev->local_named_objects to be sure
there won't be any "C-ptrs" to them so they can be deleted and replaced at
any time without the need for "surgery";
- At end-of-job/ NamespacePop time, cos_dict_objects_write() merges the
"fake objects" (content added by /PUT) with the "real" objects (content
coming mostly from marking operators) and writes the result to the output
PDF.


SaGS