Procházet zdrojové kódy

"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
pull/30/head
Jeremias Maerki před 19 roky
rodič
revize
251af6d1e9

+ 141
- 109
src/java/org/apache/fop/pdf/PDFState.java Zobrazit soubor

@@ -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;
}
}
}


+ 71
- 2
src/java/org/apache/fop/render/pdf/PDFRenderer.java Zobrazit soubor

@@ -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 {

Načítá se…
Zrušit
Uložit