From 96139290da0f3d76d1bec930fd7c6fb2875028a1 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Wed, 19 Jan 2005 21:48:12 +0000 Subject: [PATCH] "fixed" block-containers implemented. PDFState was cleaned up, simplified and refactored to support a "break-out" mechanism that allows to temporarily break out of the current viewport context. Support for sending comments to the PDF stream for debugging purposes. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198290 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/pdf/PDFState.java | 250 ++++++++++-------- .../apache/fop/render/pdf/PDFRenderer.java | 73 ++++- 2 files changed, 212 insertions(+), 111 deletions(-) diff --git a/src/java/org/apache/fop/pdf/PDFState.java b/src/java/org/apache/fop/pdf/PDFState.java index 6c24f6693..5266d3a10 100644 --- a/src/java/org/apache/fop/pdf/PDFState.java +++ b/src/java/org/apache/fop/pdf/PDFState.java @@ -18,16 +18,16 @@ package org.apache.fop.pdf; -import java.awt.Shape; -import java.util.ArrayList; -import java.util.HashMap; +import java.io.Serializable; +import java.util.List; import java.util.Iterator; -import java.awt.geom.GeneralPath; -import java.awt.geom.Area; import java.awt.Color; import java.awt.Paint; +import java.awt.Shape; import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; /** * This keeps information about the current state when writing to pdf. @@ -47,41 +47,10 @@ import java.awt.geom.AffineTransform; * the possible combinations after completing. */ public class PDFState { - private static final String COLOR = "color"; - private static final String BACKCOLOR = "backcolor"; - private static final String PAINT = "paint"; - private static final String BACKPAINT = "backpaint"; - private static final String LINECAP = "lineCap"; - private static final String LINEJOIN = "lineJoin"; - private static final String LINEWIDTH = "lineWidth"; - private static final String MITERLIMIT = "miterLimit"; - private static final String TEXT = "text"; - private static final String DASHOFFSET = "dashOffset"; - private static final String DASHARRAY = "dashArray"; - private static final String TRANSFORM = "transform"; - private static final String FONTSIZE = "fontSize"; - private static final String FONTNAME = "fontName"; - private static final String CLIP = "clip"; - private static final String GSTATE = "gstate"; - - private Color color = Color.black; - private Color backcolor = Color.white; - private Paint paint = null; - private Paint backPaint = null; - private int lineCap = 0; - private int lineJoin = 0; - private float lineWidth = 1; - private float miterLimit = 0; - private boolean text = false; - private int dashOffset = 0; - private int[] dashArray = new int[0]; - private AffineTransform transform = new AffineTransform(); - private float fontSize = 0; - private String fontName = ""; - private Shape clip = null; - private PDFGState gstate = null; - private ArrayList stateStack = new ArrayList(); + private Data data = new Data(); + + private List stateStack = new java.util.ArrayList(); /** * PDF State for storing graphics state. @@ -96,54 +65,37 @@ public class PDFState { * so that the state is known when popped. */ public void push() { - HashMap saveMap = new HashMap(); - saveMap.put(COLOR, color); - saveMap.put(BACKCOLOR, backcolor); - saveMap.put(PAINT, paint); - saveMap.put(BACKPAINT, backPaint); - saveMap.put(LINECAP, new Integer(lineCap)); - saveMap.put(LINEJOIN, new Integer(lineJoin)); - saveMap.put(LINEWIDTH, new Float(lineWidth)); - saveMap.put(MITERLIMIT, new Float(miterLimit)); - saveMap.put(TEXT, new Boolean(text)); - saveMap.put(DASHOFFSET, new Integer(dashOffset)); - saveMap.put(DASHARRAY, dashArray); - saveMap.put(TRANSFORM, transform); - saveMap.put(FONTSIZE, new Float(fontSize)); - saveMap.put(FONTNAME, fontName); - saveMap.put(CLIP, clip); - saveMap.put(GSTATE, gstate); - - stateStack.add(saveMap); - - transform = new AffineTransform(); + Data copy; + try { + copy = (Data)getData().clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e.getMessage()); + } + stateStack.add(copy); + data.resetConcatenations(); } + /** + * @return the currently valid state + */ + public Data getData() { + return data; + } + /** * Pop the state from the stack and set current values to popped state. * This should be called when a Q operator is used so * the state is restored to the correct values. + * @return the restored state, null if the stack is empty */ - public void pop() { + public Data pop() { if (getStackLevel() > 0) { - HashMap saveMap = (HashMap)stateStack.get(stateStack.size() - 1); - stateStack.remove(stateStack.size() - 1); - color = (Color)saveMap.get(COLOR); - backcolor = (Color)saveMap.get(BACKCOLOR); - paint = (Paint)saveMap.get(PAINT); - backPaint = (Paint)saveMap.get(BACKPAINT); - lineCap = ((Integer)saveMap.get(LINECAP)).intValue(); - lineJoin = ((Integer)saveMap.get(LINEJOIN)).intValue(); - lineWidth = ((Float)saveMap.get(LINEWIDTH)).floatValue(); - miterLimit = ((Float)saveMap.get(MITERLIMIT)).floatValue(); - text = ((Boolean)saveMap.get(TEXT)).booleanValue(); - dashOffset = ((Integer)saveMap.get(DASHOFFSET)).intValue(); - dashArray = (int[])saveMap.get(DASHARRAY); - transform = (AffineTransform)saveMap.get(TRANSFORM); - fontSize = ((Float)saveMap.get(FONTSIZE)).floatValue(); - fontName = (String)saveMap.get(FONTNAME); - clip = (Shape)saveMap.get(CLIP); - gstate = (PDFGState)saveMap.get(GSTATE); + Data popped = (Data)stateStack.remove(stateStack.size() - 1); + + data = popped; + return popped; + } else { + return null; } } @@ -163,6 +115,7 @@ public class PDFState { * * @param stack the level to restore to */ + /* public void restoreLevel(int stack) { int pos = stack; while (stateStack.size() > pos + 1) { @@ -171,7 +124,7 @@ public class PDFState { if (stateStack.size() > pos) { pop(); } - } + }*/ /** * Set the current line dash. @@ -182,9 +135,10 @@ public class PDFState { * @param offset the line dash start offset * @return true if the line dash has changed */ + /* public boolean setLineDash(int[] array, int offset) { return false; - } + }*/ /** * Set the current line width. @@ -192,8 +146,8 @@ public class PDFState { * @return true if the line width has changed */ public boolean setLineWidth(float width) { - if (lineWidth != width) { - lineWidth = width; + if (getData().lineWidth != width) { + getData().lineWidth = width; return true; } else { return false; @@ -208,11 +162,13 @@ public class PDFState { * @return true if the color has changed */ public boolean setColor(Color col) { - if (!col.equals(color)) { - color = col; + if (!col.equals(getData().color)) { + //System.out.println("old: " + getData().color + ", new: " + col); + getData().color = col; return true; + } else { + return false; } - return false; } /** @@ -223,11 +179,12 @@ public class PDFState { * @return true if the background color has changed */ public boolean setBackColor(Color col) { - if (!col.equals(backcolor)) { - backcolor = col; + if (!col.equals(getData().backcolor)) { + getData().backcolor = col; return true; + } else { + return false; } - return false; } /** @@ -238,13 +195,13 @@ public class PDFState { * @return true if the new paint changes the current paint */ public boolean setPaint(Paint p) { - if (paint == null) { + if (getData().paint == null) { if (p != null) { - paint = p; + getData().paint = p; return true; } - } else if (!paint.equals(p)) { - paint = p; + } else if (!data.paint.equals(p)) { + getData().paint = p; return true; } return false; @@ -263,14 +220,14 @@ public class PDFState { * @return true if the clip will change the current clip. */ public boolean checkClip(Shape cl) { - if (clip == null) { + if (getData().clip == null) { if (cl != null) { return true; } - } else if (!new Area(clip).equals(new Area(cl))) { + } else if (!new Area(getData().clip).equals(new Area(cl))) { return true; } - // todo check for clips that are larger than the current + //TODO check for clips that are larger than the current return false; } @@ -282,12 +239,12 @@ public class PDFState { * @param cl the new clip in the current state */ public void setClip(Shape cl) { - if (clip != null) { - Area newClip = new Area(clip); + if (getData().clip != null) { + Area newClip = new Area(getData().clip); newClip.intersect(new Area(cl)); - clip = new GeneralPath(newClip); + getData().clip = new GeneralPath(newClip); } else { - clip = cl; + getData().clip = cl; } } @@ -301,7 +258,7 @@ public class PDFState { * @return true if the new transform is different then the current transform */ public boolean checkTransform(AffineTransform tf) { - return !tf.equals(transform); + return !tf.equals(getData().transform); } /** @@ -312,7 +269,7 @@ public class PDFState { * @param tf the transform to concatonate to the current level transform */ public void setTransform(AffineTransform tf) { - transform.concatenate(tf); + getData().concatenate(tf); } /** @@ -326,11 +283,11 @@ public class PDFState { AffineTransform tf; AffineTransform at = new AffineTransform(); for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - HashMap map = (HashMap)iter.next(); - tf = (AffineTransform)map.get(TRANSFORM); + Data d = (Data)iter.next(); + tf = d.transform; at.concatenate(tf); } - at.concatenate(transform); + at.concatenate(getData().transform); return at; } @@ -351,17 +308,92 @@ public class PDFState { PDFGState newstate = new PDFGState(); newstate.addValues(defaultState); for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - HashMap map = (HashMap)iter.next(); - state = (PDFGState)map.get(GSTATE); + Data d = (Data)iter.next(); + state = d.gstate; if (state != null) { newstate.addValues(state); } } - if (gstate != null) { - newstate.addValues(gstate); + if (getData().gstate != null) { + newstate.addValues(getData().gstate); } return newstate; } + + public class Data implements Cloneable, Serializable { + + public Color color = Color.black; + public Color backcolor = Color.white; + public Paint paint = null; + public Paint backPaint = null; + public int lineCap = 0; + public int lineJoin = 0; + public float lineWidth = 1; + public float miterLimit = 0; + public boolean text = false; + public int dashOffset = 0; + public int[] dashArray = new int[0]; + public AffineTransform transform = new AffineTransform(); + public float fontSize = 0; + public String fontName = ""; + public Shape clip = null; + public PDFGState gstate = null; + /** Log of all concatenation operations */ + public List concatenations = null; + + + /** @see java.lang.Object#clone() */ + public Object clone() throws CloneNotSupportedException { + Data obj = new Data(); + obj.color = this.color; + obj.backcolor = this.backcolor; + obj.paint = this.paint; + obj.backPaint = this.paint; + obj.lineCap = this.lineCap; + obj.lineJoin = this.lineJoin; + obj.lineWidth = this.lineWidth; + obj.miterLimit = this.miterLimit; + obj.text = this.text; + obj.dashOffset = this.dashOffset; + obj.dashArray = this.dashArray; + obj.transform = new AffineTransform(this.transform); + obj.fontSize = this.fontSize; + obj.fontName = this.fontName; + obj.clip = this.clip; + obj.gstate = this.gstate; + if (this.concatenations != null) { + obj.concatenations = new java.util.ArrayList(this.concatenations); + } + return obj; + } + + /** + * Forgets the previously made AffineTransform concatenations. + */ + public void resetConcatenations() { + this.concatenations = null; + } + + /** + * Concatenate the given AffineTransform with the current thus creating + * a new viewport. Note that all concatenation operations are logged + * so they can be replayed if necessary (ex. for block-containers with + * "fixed" positioning. + * @param at Transformation to perform + */ + public void concatenate(AffineTransform at) { + if (this.concatenations == null) { + this.concatenations = new java.util.ArrayList(); + } + concatenations.add(at); + transform.concatenate(at); + } + + /** @see java.lang.Object#toString() */ + public String toString() { + return super.toString() + ", " + this.transform + " | " + this.concatenations; + } + } } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index 8c9c9030f..eaae3bb2d 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import java.awt.Color; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; +import java.util.Iterator; import java.util.Map; import java.util.List; @@ -101,11 +102,15 @@ text decoration * */ public class PDFRenderer extends PrintRenderer { + /** * The mime type for pdf */ public static final String MIME_TYPE = "application/pdf"; + /** Controls whether comments are written to the PDF stream. */ + protected static final boolean WRITE_COMMENTS = true; + /** * the PDF Document being created */ @@ -330,6 +335,16 @@ public class PDFRenderer extends PrintRenderer { renderBookmarkItem(bookmarkItem.getSubData(i), pdfOutline); } } + + /** + * writes out a comment. + * @param text text for the comment + */ + protected void comment(String text) { + if (WRITE_COMMENTS) { + currentStream.add("% " + text + "\n"); + } + } /** Saves the graphics state of the rendering engine. */ protected void saveGraphicsState() { @@ -432,11 +447,17 @@ public class PDFRenderer extends PrintRenderer { .makeStream(PDFFilterList.CONTENT_FILTER, false); currentState = new PDFState(); + /* This transform shouldn't affect PDFState as it only sets the basic + * coordinate system for the rendering process. + * currentState.setTransform(new AffineTransform(1, 0, 0, -1, 0, (int) Math.round(pageHeight / 1000))); + */ // Transform origin at top left to origin at bottom left currentStream.add("1 0 0 -1 0 " + (int) Math.round(pageHeight / 1000) + " cm\n"); + + currentFontName = ""; Page p = page.getPage(); @@ -459,7 +480,7 @@ public class PDFRenderer extends PrintRenderer { // Set the given CTM in the graphics state currentState.push(); currentState.setTransform( - new AffineTransform(CTMHelper.toPDFArray(ctm))); + new AffineTransform(CTMHelper.toPDFArray(ctm))); saveGraphicsState(); // multiply with current CTM @@ -682,7 +703,27 @@ public class PDFRenderer extends PrintRenderer { if (bv.getPositioning() == Block.ABSOLUTE || bv.getPositioning() == Block.FIXED) { - //TODO Handle positioning=FIXED + //For FIXED, we need to break out of the current viewports to the + //one established by the page. We save the state stack for restoration + //after the block-container has been painted. See below. + List breakOutList = null; + if (bv.getPositioning() == Block.FIXED) { + //break out + breakOutList = new java.util.ArrayList(); + PDFState.Data data; + while (true) { + data = currentState.getData(); + if (currentState.pop() == null) { + break; + } + if (breakOutList.size() == 0) { + comment("------ break out!"); + } + breakOutList.add(0, data); //Insert because of stack-popping + //getLogger().debug("Adding to break out list: " + data); + restoreGraphicsState(); + } + } CTM tempctm = new CTM(containingIPPosition, containingBPPosition); ctm = tempctm.multiply(ctm); @@ -729,6 +770,34 @@ public class PDFRenderer extends PrintRenderer { // clip if necessary + if (breakOutList != null) { + comment("------ restoring context after break-out..."); + PDFState.Data data; + Iterator i = breakOutList.iterator(); + while (i.hasNext()) { + data = (PDFState.Data)i.next(); + //getLogger().debug("Restoring: " + data); + currentState.push(); + saveGraphicsState(); + if (data.concatenations != null) { + Iterator tr = data.concatenations.iterator(); + while (tr.hasNext()) { + AffineTransform at = (AffineTransform)tr.next(); + currentState.setTransform(at); + double[] matrix = new double[6]; + at.getMatrix(matrix); + tempctm = new CTM(matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4] * 1000, matrix[5] * 1000); + currentStream.add(CTMHelper.toPDFString(tempctm) + " cm\n"); + } + } + //TODO Break-out: Also restore items such as line width and color + //Left out for now because all this painting stuff is very + //inconsistent. Some values go over PDFState, some don't. + } + comment("------ done."); + } + currentIPPosition = saveIP; currentBPPosition = saveBP; } else { -- 2.39.5