Description
In the PDF format, the clipping path operators can only make a clipping path smaller. The only way to make the clipping area wider again is to pop graphics states in the graphics state stack until previously set clipping paths are removed. However, in Java2D there is a method Graphics.setClip
which replaces the current clip with another one without relying on popping states from a stack.
PDFGraphics2D.setClip
currently tries to mimic the Java2D behaviour in this way:
It caches some of the attributes of the current graphics state.
It pops the current graphics state in order to get rid of the clip.
It pushes a new graphics state and applies the cached values and the new clip.
This procedure works only if the PDF graphics state stack has been kept so shallow that a single pop completely clears the clip. But there is at least one situation where this requirement gets broken: When a new Graphics
object is created from another one, e.g.:
Graphics graphics2 = graphics1.create();
This call is basically a push(). The equivalent for pop() is:
graphics2.dispose();
Since those are valid mechanisms in Java2D to realize a kind of state stack, they have to be supported. Currently however, they break PDFGraphics2D.setClip
.
Possible fix: Allow arbitrary depths in the graphics state stack and deal with it.
FreeHEP needs a mechanism to keep track of the graphics state stack. Since more than one PDFGraphics2D
object can be used, this stack handling needs to be performed in a single object shared by all those PDFGraphics2D
instances.
setClip
then requires a complex procedure:
- Pop all graphics states up to the initial base state. This needs to be some base state where the clip is the largest that shall be allowed, e.g. in
openPage
, the line with:clipRect(0, 0, size.width, size.height);
- Recreate the result state except for the clip, leave the clip like it was in the base state. This doesn't necessarily mean that several graphics states are pushed again - a single cumulated state could be enough. We have to throw that one away later anyway and restore the true original stack contents.
- Perform
setClip
. - Continue doing other stuff until the state is popped beyond the
setClip
state. - When the graphics state stack is popped beyond the level when the
setClip
procedure was executed, in the PDF the state will be back in the initial base state. This is because we had popped all states and only pushed a single cumulated state forsetClip
.PDFGraphics2D
now needs to recreate all original state changes and push them again to the PDF graphics state stack.
Idea for an alternative fix:
Keep the graphics state stack shallow in the PDF and hold a stack in Java instead. Perform all state accumulations (transformation matrix concatenation, clip intersection) in Java. Whenever a state is popped in Java (Graphics.dispose
), pop the one and only state in PDF and push a new one with the result matrices / clips / colors / etc. calculated from the stack structure in Java.