Order of processing ExtGState keys matters
Describe the bug
When a ExtGState resource is invoked into a graphic state, 32K does not give any guidance as to the order in which the keys of the dictionary are processed.
Normally, this type of thing isn't mandated in the spec, but it turns out that when it comes to the use of /SMask (soft mask) it can matter a lot! Attached is a PDF with a very simple ExtGState resource - with a LW (line width) key and an SMask key, where the SMask is a simple stroked rectangle. Depending on the order in which the processor reads the keys, the width of the mask's rect will be quite different
Recommendation Well known implementation, as well as most other popular implementations, all process the SMask first and only then do the rest of the dictionary. Recommend that we make that normative.
That's an interesting one. Completely agree with your result, of course, but I think the question would be why the soft mask is inheriting the graphics state from the page at all?
ISO32000:2 at the very end of 8.7.1 describes the process for the Do operator, which makes it clear then when an XObject is used this way it's going to inherit the graphics state of the page. As another example, when an XObject is used in a tiling pattern the initial state is also described (end of 8.7.3.1).
But I don't see anything that makes any statement about the initial state for XObjects used as masks - 11.6.5.1 seems to be the applicable section - and actually implementations seem to have a bit of variation on this.
I'm attaching a modified version of your test Leonard; if there are two hollow rectangles on the page the graphics state is reset for masks, if there is only one hollow rectangle then it's inherited from the page.
No doubt there are other places where XObjects are used where the same question needs answering, hopefully the Arlington model will point the way.
If it is about the initial state, then it is likely also covered or mentioned in Bert's previous work (but maybe not explicitly identified as a root issue)...
@faceless2 There are no XObjects here...This is purely "plain" page content. Yes, an SMask is a form of Form XObj, but it's not used in the same way so I am not convinced they should be treated the same way.
But your question:
why the soft mask is inheriting the graphics state from the page at all?
Is really the first of two questions.
1 - SHOULD the SMask inherit from the GState? 2 - If the answer to 1 is yes, then my question (about order of application of the keys in the ExtGState) is relevant. If the answer is no, then you are correct, this issue is moot.
@petervwyatt I don't see this as initial state at all, though maybe a case could be made.
No disagreement with any of that - I am also not convinced they should be treated the same way as a "regular" XObject, i.e. one referenced with the Do operator. That's what leads me to question why it would inherit from the page, so I agree splitting the question into two halves is the way to do it.
I do not see (in any tested application) that the GS LW statement has an impact on the content stream of a smask in the same graphic state.
In these samples the LW statement is set to 20 and 50 and the result is the same (default 1pt):
softmask_linewidth res GS 20LW.pdf
softmask_linewidth res GS 50LW.pdf
Just to double check: If the line width is set in the content stream of the smask (50 w) the line width is 50pt.
softmask_linewidth res GS SMask 50w.pdf
If the line width is set in the content stream of the page to 50 that value is in a well known application inherited to the smask, but that is not the case in some other applications that I have tested.
softmask_linewidth res Page 50w.pdf
As a result I also agree to what Leonard said: In a graphic state an smask entry is applied first, leaving all other statements in the same graphic state alone.
I also think that all active graphic state parameters are inherited to an smask in a graphic state in the same way as that would happen with a form XObject, but I do not think that that is stated somewhere in the spec?
Existing text in clause “11.6.5.1 Soft-mask dictionaries” does specify which CTM to start from for the coordinate system of the soft-mask content:
“The mask’s coordinate system shall be defined by concatenating the transformation matrix specified by the Matrix entry in the transparency group’s form dictionary (see [8.10.2, "Form dictionaries") with the current transformation matrix at the moment the soft mask is established in the graphics state with the gs operator.”
It seems logical to extend this to initialize the rest of the Soft-mask content's initial graphics state completely with "the current graphics state at the moment the gs operator appears that establishes the soft mask in the graphics state". (with the modifications for blend mode, alpha and soft mask values as required for transparency group XObjects (see “11.6.6 Transparency group XObjects”)).
This would fix any issue with processing order of key-value pairs in an ExtGState dictionary as the parameters in the initial graphic state will be determined before the operators in the ExtGState are processed. This also means that e.g. an AIS ExtGState operator needs to be set with a separate, earlier gs operator ExtGState dictionary, before it can influence the behaviour of a SoftMask that is established with a gs operator.