From: Adrian Cumiskey Date: Thu, 10 Jul 2008 10:59:39 +0000 (+0000) Subject: * Created new AbstractState class that shares common properties and stack popping... X-Git-Tag: fop-1_0~376^2~141 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c0b3bda3b8ec533d8595c75a810df9ac19ab23d4;p=xmlgraphics-fop.git * Created new AbstractState class that shares common properties and stack popping/pushing behaviour of and is now extended by AFPState and PDFState. * color, resolution and bitsPerPixel moved from AFPInfo to AFPState/AbstractState resulting in a more simplified AFPInfo and AFPSVGHandler. * Changed AFPRenderer, AFPDataStream, PresentationTextObject and PresentationTextData to use encapsulated AFPTextDataInfo instead of a chain of long parameter passing between objects. Also provided some formatting cleanup in PresentationTextData. * Use of X and Y offsets no longer used by AFPDataStream, setOffsets() is now deprecated and added setOrientation() accessor (which still remains relevant). * AFPRenderer no longer uses its own bespoke ViewPortPos mechanism and now uses the more standard state stack and AffineTransform mechanism enjoyed by the postscript and PDF Renderers. This means that there is now a proper implementation for startVParea(), endVParea(), saveGraphicsState(), restoreGraphicsState(), breakOutOfStateStack() and restoreStateStackAfterBreakOut() and it is no longer necessary to have a copy/pasted/tweaked renderBlockViewport() or renderRegionViewport(). As a result of this AFPRenderer is now much more compact (by some 500 lines) - similar work needs to be done in the PCL Renderer, any volunteers? * Added lots of helpful toString() methods for those mere mortals wishing to step through with a debugger :). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@675507 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/build.xml b/build.xml index ff76686b6..b55cabb2f 100644 --- a/build.xml +++ b/build.xml @@ -593,6 +593,7 @@ RetroWeaver will be added here --> + diff --git a/src/java/org/apache/fop/pdf/PDFState.java b/src/java/org/apache/fop/pdf/PDFState.java index e453d7b51..4f7b23a80 100644 --- a/src/java/org/apache/fop/pdf/PDFState.java +++ b/src/java/org/apache/fop/pdf/PDFState.java @@ -19,14 +19,9 @@ package org.apache.fop.pdf; -import java.io.Serializable; -import java.util.List; import java.util.Iterator; - -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; @@ -47,144 +42,12 @@ import java.awt.geom.GeneralPath; * It is impossible to optimise the result without analysing the all * the possible combinations after completing. */ -public class PDFState { - - private Data data = new Data(); - - private List stateStack = new java.util.ArrayList(); +public class PDFState extends org.apache.fop.render.AbstractState { /** * PDF State for storing graphics state. */ public PDFState() { - - } - - /** - * Push the current state onto the stack. - * This call should be used when the q operator is used - * so that the state is known when popped. - */ - public void push() { - Data copy; - try { - copy = (Data)getData().clone(); - getData().resetTransform(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e.getMessage()); - } - stateStack.add(copy); - } - - /** - * @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 Data pop() { - if (getStackLevel() > 0) { - Data popped = (Data)stateStack.remove(stateStack.size() - 1); - - data = popped; - return popped; - } else { - return null; - } - } - - /** - * Get the current stack level. - * - * @return the current stack level - */ - public int getStackLevel() { - return stateStack.size(); - } - - /** - * Restore the state to a particular level. - * this can be used to restore to a known level without making - * multiple pop calls. - * - * @param stack the level to restore to - */ - /* - public void restoreLevel(int stack) { - int pos = stack; - while (stateStack.size() > pos + 1) { - stateStack.remove(stateStack.size() - 1); - } - if (stateStack.size() > pos) { - pop(); - } - }*/ - - /** - * Set the current line dash. - * Check if setting the line dash to the given values - * will make a change and then set the state to the new values. - * - * @param array the line dash array - * @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. - * @param width the line width in points - * @return true if the line width has changed - */ - public boolean setLineWidth(float width) { - if (getData().lineWidth != width) { - getData().lineWidth = width; - return true; - } else { - return false; - } - } - - /** - * Set the current color. - * Check if the new color is a change and then set the current color. - * - * @param col the color to set - * @return true if the color has changed - */ - public boolean setColor(Color col) { - if (!col.equals(getData().color)) { - getData().color = col; - return true; - } else { - return false; - } - } - - /** - * Set the current background color. - * Check if the background color will change and then set the new color. - * - * @param col the new background color - * @return true if the background color has changed - */ - public boolean setBackColor(Color col) { - if (!col.equals(getData().backcolor)) { - getData().backcolor = col; - return true; - } else { - return false; - } } /** @@ -195,13 +58,14 @@ public class PDFState { * @return true if the new paint changes the current paint */ public boolean setPaint(Paint p) { - if (getData().paint == null) { + Paint paint = ((PDFData)getData()).paint; + if (paint == null) { if (p != null) { - getData().paint = p; + ((PDFData)getData()).paint = p; return true; } - } else if (!data.paint.equals(p)) { - getData().paint = p; + } else if (!paint.equals(p)) { + ((PDFData)getData()).paint = p; return true; } return false; @@ -220,11 +84,12 @@ public class PDFState { * @return true if the clip will change the current clip. */ public boolean checkClip(Shape cl) { - if (getData().clip == null) { + Shape clip = ((PDFData)getData()).clip; + if (clip == null) { if (cl != null) { return true; } - } else if (!new Area(getData().clip).equals(new Area(cl))) { + } else if (!new Area(clip).equals(new Area(cl))) { return true; } //TODO check for clips that are larger than the current @@ -239,80 +104,23 @@ public class PDFState { * @param cl the new clip in the current state */ public void setClip(Shape cl) { - if (getData().clip != null) { - Area newClip = new Area(getData().clip); + Shape clip = ((PDFData)getData()).clip; + if (clip != null) { + Area newClip = new Area(clip); newClip.intersect(new Area(cl)); - getData().clip = new GeneralPath(newClip); + ((PDFData)getData()).clip = new GeneralPath(newClip); } else { - getData().clip = cl; + ((PDFData)getData()).clip = cl; } } /** - * Check the current transform. - * The transform for the current state is the combination of all - * transforms in the current state. The parameter is compared - * against this current transform. - * - * @param tf the transform the check against - * @return true if the new transform is different then the current transform - */ - public boolean checkTransform(AffineTransform tf) { - return !tf.equals(getData().transform); - } - - /** - * Set a new transform. - * This transform is appended to the transform of - * the current graphic state. - * - * @param tf the transform to concatonate to the current level transform - * @deprecated This method name is misleading. Use concatenate(AffineTransform) instead! - */ - public void setTransform(AffineTransform tf) { - concatenate(tf); - } - - /** - * Concatenates the given AffineTransform to the current one. - * @param tf the transform to concatenate to the current level transform - */ - public void concatenate(AffineTransform tf) { - getData().concatenate(tf); - } - - /** - * Get the current transform. - * This gets the combination of all transforms in the - * current state. - * - * @return the calculate combined transform for the current state - */ - public AffineTransform getTransform() { - AffineTransform tf; - AffineTransform at = new AffineTransform(); - for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - Data d = (Data)iter.next(); - tf = d.transform; - at.concatenate(tf); - } - at.concatenate(getData().transform); - return at; - } - - /** - * Get a copy of the base transform for the page. Used to translate - * IPP/BPP values into X,Y positions when positioning is "fixed". + * Get the current stack level. * - * @return the base transform, or null if the state stack is empty + * @return the current stack level */ - public AffineTransform getBaseTransform() { - if (stateStack.size() == 0) { - return null; - } else { - Data baseData = (Data) stateStack.get(0); - return (AffineTransform) baseData.transform.clone(); - } + public int getStackLevel() { + return getStateStack().size(); } /** @@ -328,90 +136,67 @@ public class PDFState { PDFGState defaultState = PDFGState.DEFAULT; PDFGState state; - PDFGState newstate = new PDFGState(); - newstate.addValues(defaultState); - for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - Data d = (Data)iter.next(); + PDFGState newState = new PDFGState(); + newState.addValues(defaultState); + for (Iterator iter = getStateStack().iterator(); iter.hasNext();) { + PDFData d = (PDFData)iter.next(); state = d.gstate; if (state != null) { - newstate.addValues(state); + newState.addValues(state); } } - if (getData().gstate != null) { - newstate.addValues(getData().gstate); + if (((PDFData)getData()).gstate != null) { + newState.addValues(((PDFData)getData()).gstate); } - - return newstate; + return newState; } - public class Data implements Cloneable, Serializable { + private class PDFData extends org.apache.fop.render.AbstractState.AbstractData { - public Color color = Color.black; - public Color backcolor = Color.black; - 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; - + private static final long serialVersionUID = 3527950647293177764L; + + private Paint paint = null; + private Paint backPaint = null; + private int lineCap = 0; + private int lineJoin = 0; + private float miterLimit = 0; + private boolean text = false; + private int dashOffset = 0; + private Shape clip = null; + private PDFGState gstate = null; /** {@inheritDoc} */ public Object clone() throws CloneNotSupportedException { - Data obj = new Data(); - obj.color = this.color; - obj.backcolor = this.backcolor; + PDFData obj = (PDFData)super.clone(); 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; return obj; } - /** - * Get the current Transform. - */ - public AffineTransform getTransform() { - return transform; - } - - public void resetTransform() { - transform = new AffineTransform(); - } - - /** - * 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) { - transform.concatenate(at); - } - /** {@inheritDoc} */ public String toString() { - return super.toString() + ", " + this.transform; + return super.toString() + + ", paint=" + paint + + ", backPaint=" + backPaint + + ", lineCap=" + lineCap + + ", miterLimit=" + miterLimit + + ", text=" + text + + ", dashOffset=" + dashOffset + + ", clip=" + clip + + ", gstate=" + gstate; } } + + /** {@inheritDoc} */ + protected AbstractData instantiateData() { + return new PDFData(); + } } diff --git a/src/java/org/apache/fop/render/AbstractState.java b/src/java/org/apache/fop/render/AbstractState.java new file mode 100644 index 000000000..07595be7f --- /dev/null +++ b/src/java/org/apache/fop/render/AbstractState.java @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render; + +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Stack; + +/** + * A base class which holds information about the current rendering state. + */ +public abstract class AbstractState { + private AbstractData currentData = null; + private Stack/**/ stateStack = null; + + /** + * Instantiates a new state data object + * @return a new state data object + */ + protected abstract AbstractData instantiateData(); + + /** + * @return the currently valid state + */ + public AbstractData getData() { + if (currentData == null) { + currentData = instantiateData(); + } + return currentData; + } + + /** + * Set the current color. + * Check if the new color is a change and then set the current color. + * + * @param col the color to set + * @return true if the color has changed + */ + public boolean setColor(Color col) { + if (!col.equals(getData().color)) { + getData().color = col; + return true; + } + return false; + } + + /** + * Get the color. + * @return the color + */ + public Color getColor() { + if (getData().color == null) { + getData().color = Color.black; + } + return getData().color; + } + + /** + * Get the background color. + * @return the background color + */ + public Color getBackColor() { + if (getData().backColor == null) { + getData().backColor = Color.white; + } + return getData().backColor; + } + + /** + * Set the current background color. + * Check if the new background color is a change and then set the current background color. + * + * @param col the background color to set + * @return true if the color has changed + */ + public boolean setBackColor(Color col) { + if (!col.equals(getData().backColor)) { + getData().backColor = col; + return true; + } + return false; + } + + /** + * Set the current font name + * @param internalFontName the internal font name + * @return true if the font name has changed + */ + public boolean setFontName(String internalFontName) { + if (!internalFontName.equals(getData().fontName)) { + getData().fontName = internalFontName; + return true; + } + return false; + } + + /** + * Gets the current font name + * @return the current font name + */ + public String getFontName() { + return getData().fontName; + } + + /** + * Gets the current font size + * @return the current font size + */ + public int getFontSize() { + return getData().fontSize; + } + + /** + * Set the current font size. + * Check if the font size is a change and then set the current font size. + * + * @param size the font size to set + * @return true if the font size has changed + */ + public boolean setFontSize(int size) { + if (size != getData().fontSize) { + getData().fontSize = size; + return true; + } + return false; + } + + /** + * Set the current line width. + * @param width the line width in points + * @return true if the line width has changed + */ + public boolean setLineWidth(float width) { + if (getData().lineWidth != width) { + getData().lineWidth = width; + return true; + } + return false; + } + + /** + * Gets the current line width + * @return the current line width + */ + public float getLineWidth() { + return getData().lineWidth; + } + + /** + * Sets the dash array (line type) for the current basic stroke + * @param dash the line dash array + * @return true if the dash array has changed + */ + public boolean setDashArray(float[] dash) { + if (!Arrays.equals(dash, getData().dashArray)) { + getData().dashArray = dash; + return true; + } + return false; + } + + /** + * Get the current transform. + * This gets the combination of all transforms in the + * current state. + * + * @return the calculate combined transform for the current state + */ + public AffineTransform getTransform() { + AffineTransform at = new AffineTransform(); + for (Iterator iter = getStateStack().iterator(); iter.hasNext();) { + AbstractData data = (AbstractData)iter.next(); + AffineTransform stackTrans = data.getTransform(); + at.concatenate(stackTrans); + } + AffineTransform currentTrans = getData().getTransform(); + at.concatenate(currentTrans); + return at; + } + + /** + * Check the current transform. + * The transform for the current state is the combination of all + * transforms in the current state. The parameter is compared + * against this current transform. + * + * @param tf the transform the check against + * @return true if the new transform is different then the current transform + */ + public boolean checkTransform(AffineTransform tf) { + return !tf.equals(getData().getTransform()); + } + + /** + * Get a copy of the base transform for the page. Used to translate + * IPP/BPP values into X,Y positions when positioning is "fixed". + * + * @return the base transform, or null if the state stack is empty + */ + public AffineTransform getBaseTransform() { + if (getStateStack().isEmpty()) { + return null; + } else { + AbstractData baseData = (AbstractData)getStateStack().get(0); + return (AffineTransform) baseData.getTransform().clone(); + } + } + + /** + * Concatenates the given AffineTransform to the current one. + * @param tf the transform to concatenate to the current level transform + */ + public void concatenate(AffineTransform tf) { + getData().concatenate(tf); + } + + /** + * Resets the current AffineTransform. + */ + public void resetTransform() { + getData().resetTransform(); + } + + /** + * Push the current state onto the stack. + * This call should be used when the Q operator is used + * so that the state is known when popped. + */ + public void push() { + AbstractData copy; + try { + copy = (AbstractData)getData().clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e.getMessage()); + } + getStateStack().push(copy); + } + + /** + * 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 AbstractData pop() { + if (!getStateStack().isEmpty()) { + this.currentData = (AbstractData)getStateStack().pop(); + return this.currentData; + } else { + return null; + } + } + + /** + * Clears the state stack + */ + public void clear() { + getStateStack().clear(); + currentData = null; + } + + /** + * @return the state stack + */ + protected Stack/**/ getStateStack() { + if (stateStack == null) { + stateStack = new java.util.Stack/**/(); + } + return stateStack; + } + + /** {@inheritDoc} */ + public String toString() { + return "stateStack=" + stateStack + + ", currentData=" + currentData; + } + + /** + * A base state data holding object + */ + public abstract class AbstractData implements Cloneable, Serializable { + /** The current color */ + private Color color = null; + + /** The current background color */ + private Color backColor = null; + + /** The current font name */ + private String fontName = null; + + /** The current font size */ + private int fontSize = 0; + + /** The current line width */ + private float lineWidth = 0; + + /** The dash array for the current basic stroke (line type) */ + private float[] dashArray = null; + + /** The current transform */ + private AffineTransform transform = 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) { + getTransform().concatenate(at); + } + + /** + * Get the current AffineTransform. + * @return the current transform + */ + public AffineTransform getTransform() { + if (transform == null) { + transform = new AffineTransform(); + } + return transform; + } + + /** + * Resets the current AffineTransform. + */ + public void resetTransform() { + transform = new AffineTransform(); + } + + /** {@inheritDoc} */ + public Object clone() throws CloneNotSupportedException { + AbstractData obj = instantiateData(); + obj.color = this.color; + obj.backColor = this.backColor; + obj.fontName = this.fontName; + obj.fontSize = this.fontSize; + obj.lineWidth = this.lineWidth; + obj.dashArray = this.dashArray; + obj.transform = new AffineTransform(this.transform); + return obj; + } + + /** {@inheritDoc} */ + public String toString() { + return "color=" + color + + ", backColor=" + backColor + + ", fontName=" + fontName + + ", fontSize=" + fontSize + + ", lineWidth=" + lineWidth + + ", dashArray=" + dashArray + + ", transform=" + transform; + } + } +} diff --git a/src/java/org/apache/fop/render/afp/AFPInfo.java b/src/java/org/apache/fop/render/afp/AFPInfo.java index 19e58d30f..b12a46cb2 100644 --- a/src/java/org/apache/fop/render/afp/AFPInfo.java +++ b/src/java/org/apache/fop/render/afp/AFPInfo.java @@ -29,27 +29,27 @@ import org.apache.fop.render.afp.modca.AFPDataStream; public final class AFPInfo { /** see WIDTH */ private int width; + /** see HEIGHT */ private int height; + /** see XPOS */ private int x; + /** see YPOS */ private int y; + /** see HANDLER_CONFIGURATION */ private Configuration cfg; /** see AFP_FONT_INFO */ private FontInfo fontInfo; + /** See AFP_DATASTREAM */ private AFPDataStream afpDataStream; + /** See AFP_STATE */ private AFPState afpState; - /** see AFP_GRAYSCALE */ - private boolean color; - /** see AFP_RESOLUTION */ - private int resolution; - /** see AFP_BITS_PER_PIXEL */ - private int bitsPerPixel; /** * Returns the width. @@ -123,7 +123,7 @@ public final class AFPInfo { * @return true if supports color */ public boolean isColorSupported() { - return this.color; + return getState().isColorImages(); } /** @@ -144,14 +144,14 @@ public final class AFPInfo { * @return the resolution */ protected int getResolution() { - return resolution; + return getState().getResolution(); } /** * @return the number of bits per pixel to use */ protected int getBitsPerPixel() { - return bitsPerPixel; + return getState().getBitsPerPixel(); } /** @@ -170,14 +170,6 @@ public final class AFPInfo { this.y = y; } - /** - * Sets the current resolution - * @param resolution the current resolution - */ - protected void setResolution(int resolution) { - this.resolution = resolution; - } - /** * Sets the current font info * @param fontInfo the current font info @@ -196,25 +188,21 @@ public final class AFPInfo { /** * Sets the AFP datastream - * @param afpDataStream the AFP datastream - */ - public void setAFPDataStream(AFPDataStream afpDataStream) { - this.afpDataStream = afpDataStream; - } - - /** - * Sets if we are supporing color - * @param color true if color is supported - */ - public void setColor(boolean color) { - this.color = color; - } - - /** - * Sets the number of bits per pixel - * @param bitsPerPixel the number of bits per pixel - */ - public void setBitsPerPixel(int bitsPerPixel) { - this.bitsPerPixel = bitsPerPixel; + * @param dataStream the AFP datastream + */ + public void setAFPDataStream(AFPDataStream dataStream) { + this.afpDataStream = dataStream; + } + + /** {@inheritDoc} */ + public String toString() { + return "width=" + width + + ",height=" + height + + ",x=" + x + + ",y=" + y + + ",cfg=" + cfg + + ",fontInfo=" + fontInfo + + ",afpDatastream=" + afpDataStream + + ",afpState=" + afpState; } } \ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java index 790732c12..c08da6ad9 100644 --- a/src/java/org/apache/fop/render/afp/AFPRenderer.java +++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java @@ -23,7 +23,6 @@ import java.awt.Color; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.io.FileNotFoundException; @@ -50,18 +49,14 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.xmlgraphics.ps.ImageEncodingHelper; +import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.Block; -import org.apache.fop.area.BlockViewport; -import org.apache.fop.area.BodyRegion; import org.apache.fop.area.CTM; import org.apache.fop.area.LineArea; -import org.apache.fop.area.NormalFlow; import org.apache.fop.area.OffDocumentItem; import org.apache.fop.area.PageViewport; -import org.apache.fop.area.RegionReference; -import org.apache.fop.area.RegionViewport; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Image; import org.apache.fop.area.inline.Leader; @@ -76,6 +71,7 @@ import org.apache.fop.fonts.base14.Courier; import org.apache.fop.fonts.base14.Helvetica; import org.apache.fop.fonts.base14.TimesRoman; import org.apache.fop.render.AbstractPathOrientedRenderer; +import org.apache.fop.render.AbstractState; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.RendererContext; import org.apache.fop.render.afp.extensions.AFPElementMapping; @@ -87,7 +83,6 @@ import org.apache.fop.render.afp.fonts.FopCharacterSet; import org.apache.fop.render.afp.fonts.OutlineFont; import org.apache.fop.render.afp.modca.AFPConstants; import org.apache.fop.render.afp.modca.AFPDataStream; -import org.apache.fop.render.afp.modca.ImageObject; import org.apache.fop.render.afp.modca.PageObject; /** @@ -146,123 +141,37 @@ import org.apache.fop.render.afp.modca.PageObject; */ public class AFPRenderer extends AbstractPathOrientedRenderer { - /** - * The default afp renderer output resolution - */ - public static final int DPI_240_RESOLUTION = 240; + private static final int X = 0; + + private static final int Y = 1; + + private static final int X1 = 0; - /** - * The afp factor for calculating resolutions (e.g. 72000/240 = 300) - */ - private static final int DPI_CONVERSION_FACTOR = 72000; + private static final int Y1 = 1; + + private static final int X2 = 2; + + private static final int Y2 = 3; /** * The afp data stream object responsible for generating afp data */ private AFPDataStream afpDataStream = null; - /** - * The map of afp root extensions - */ - // UNUSED - // private HashMap rootExtensionMap = null; /** * The map of page segments */ private Map/**/pageSegmentsMap = null; - /** - * The fonts on the current page - */ - private Map/**/getCurrentPageFonts() { - if (currentPageFontMap == null) { - this.currentPageFontMap = new java.util.HashMap/**/(); - } - return this.currentPageFontMap; - } - - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void startRenderer(OutputStream outputStream) throws IOException { - this.currentColor = new Color(255, 255, 255); - getAFPDataStream().setPortraitRotation(portraitRotation); - getAFPDataStream().setLandscapeRotation(landscapeRotation); - getAFPDataStream().setOutputStream(outputStream); + currentState.setColor(new Color(255, 255, 255)); + getAFPDataStream().setPortraitRotation(currentState.getPortraitRotation()); + afpDataStream.setLandscapeRotation(currentState.getLandscapeRotation()); + afpDataStream.setOutputStream(outputStream); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void stopRenderer() throws IOException { getAFPDataStream().write(); afpDataStream = null; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void startPageSequence(LineArea seqTitle) { getAFPDataStream().endPageGroup(); - getAFPDataStream().startPageGroup(); + afpDataStream.startPageGroup(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean supportsOutOfOrder() { // return false; return true; } - /** - * Prepare a page for rendering. This is called if the renderer supports out - * of order rendering. The renderer should prepare the page so that a page - * further on in the set of pages can be rendered. The body of the page - * should not be rendered. The page will be rendered at a later time by the - * call to render page. - * - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void preparePage(PageViewport page) { - // initializeRootExtensions(page); - - // this.currentFontFamily = ""; - this.currentFontSize = 0; - this.pageFontCounter = 0; - this.currentPageFontMap = null; - // this.lineCache = new HashSet(); - - Rectangle2D bounds = page.getViewArea(); - - this.pageWidth = mpts2units(bounds.getWidth()); - this.pageHeight = mpts2units(bounds.getHeight()); - - // renderPageGroupExtensions(page); - final int pageRotation = 0; + int pageWidth = currentState.getPageWidth(); + int pageHeight = currentState.getPageHeight(); getAFPDataStream().startPage(pageWidth, pageHeight, pageRotation, getResolution(), getResolution()); renderPageObjectExtensions(page); + getPages().put(page, getAFPDataStream().savePage()); + } + + private Map/**/ getPages() { if (this.pages == null) { - this.pages = new java.util.HashMap(); + this.pages = new java.util.HashMap/**/(); } - this.pages.put(page, getAFPDataStream().savePage()); - + return this.pages; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void processOffDocumentItem(OffDocumentItem odi) { // TODO + log.debug("NYI processOffDocumentItem(" + odi + ")"); } /** {@inheritDoc} */ public Graphics2DAdapter getGraphics2DAdapter() { return new AFPGraphics2DAdapter(); } - - /** - * {@inheritDoc} - */ + + /** {@inheritDoc} */ public void startVParea(CTM ctm, Rectangle2D clippingRect) { - // dummy not used - } - - /** - * {@inheritDoc} - */ - public void endVParea() { - // dummy not used - } - - /** - * Renders a region viewport. - *

- * - * The region may clip the area and it establishes a position from where the - * region is placed. - *

- * - * @param port - * The region viewport to be rendered - */ - public void renderRegionViewport(RegionViewport port) { - if (port != null) { - Rectangle2D view = port.getViewArea(); - // The CTM will transform coordinates relative to - // this region-reference area into page coords, so - // set origin for the region to 0,0. - currentBPPosition = 0; - currentIPPosition = 0; - - RegionReference regionReference = port.getRegionReference(); - handleRegionTraits(port); - - /* - * _afpDataStream.startOverlay(mpts2units(view.getX()) , - * mpts2units(view.getY()) , mpts2units(view.getWidth()) , - * mpts2units(view.getHeight()) - * , rotation); - */ - - pushViewPortPos(new ViewPortPos(view, regionReference.getCTM())); - - if (regionReference.getRegionClass() == FO_REGION_BODY) { - renderBodyRegion((BodyRegion) regionReference); - } else { - renderRegion(regionReference); - } - /* - * _afpDataStream.endOverlay(); - */ - popViewPortPos(); + saveGraphicsState(); + if (ctm != null) { + AffineTransform at = ctm.toAffineTransform(); + concatenateTransformationMatrix(at); + } + if (clippingRect != null) { + clipRect((float)clippingRect.getX() / 1000f, + (float)clippingRect.getY() / 1000f, + (float)clippingRect.getWidth() / 1000f, + (float)clippingRect.getHeight() / 1000f); } } /** {@inheritDoc} */ - protected void renderBlockViewport(BlockViewport bv, List children) { - // clip and position viewport if necessary - - // save positions - int saveIP = currentIPPosition; - int saveBP = currentBPPosition; - - CTM ctm = bv.getCTM(); - int borderPaddingStart = bv.getBorderAndPaddingWidthStart(); - int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore(); - // This is the content-rect - float width = (float) bv.getIPD() / 1000f; - float height = (float) bv.getBPD() / 1000f; - - if (bv.getPositioning() == Block.ABSOLUTE - || bv.getPositioning() == Block.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) { - breakOutList = breakOutOfStateStack(); - } - - AffineTransform positionTransform = new AffineTransform(); - positionTransform.translate(bv.getXOffset(), bv.getYOffset()); - - // "left/"top" (bv.getX/YOffset()) specify the position of the - // content rectangle - positionTransform.translate(-borderPaddingStart, - -borderPaddingBefore); - - // skipping fox:transform here - - // saveGraphicsState(); - // Viewport position - // concatenateTransformationMatrix(mptToPt(positionTransform)); - - // Background and borders - float bpwidth = (borderPaddingStart + bv - .getBorderAndPaddingWidthEnd()) / 1000f; - float bpheight = (borderPaddingBefore + bv - .getBorderAndPaddingWidthAfter()) / 1000f; - Point2D ptSrc = new Point(0, 0); - Point2D ptDst = positionTransform.transform(ptSrc, null); - Rectangle2D borderRect = new Rectangle2D.Double(ptDst.getX(), ptDst - .getY(), 1000 * (width + bpwidth), - 1000 * (height + bpheight)); - pushViewPortPos(new ViewPortPos(borderRect, new CTM( - positionTransform))); - drawBackAndBorders(bv, 0, 0, width + bpwidth, height + bpheight); - - // Shift to content rectangle after border painting - AffineTransform contentRectTransform = new AffineTransform(); - contentRectTransform.translate(borderPaddingStart, - borderPaddingBefore); - // concatenateTransformationMatrix(mptToPt(contentRectTransform)); - ptSrc = new Point(0, 0); - ptDst = contentRectTransform.transform(ptSrc, null); - Rectangle2D contentRect = new Rectangle2D.Double(ptDst.getX(), - ptDst.getY(), 1000 * width, 1000 * height); - pushViewPortPos(new ViewPortPos(contentRect, new CTM( - contentRectTransform))); - - // Clipping is not supported, yet - // Rectangle2D clippingRect = null; - // clippingRect = new Rectangle(0, 0, bv.getIPD(), bv.getBPD()); - - // saveGraphicsState(); - // Set up coordinate system for content rectangle - AffineTransform contentTransform = ctm.toAffineTransform(); - // concatenateTransformationMatrix(mptToPt(contentTransform)); - contentRect = new Rectangle2D.Double(0, 0, 1000 * width, - 1000 * height); - pushViewPortPos(new ViewPortPos(contentRect, new CTM( - contentTransform))); - - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - - popViewPortPos(); - popViewPortPos(); - // restoreGraphicsState(); - popViewPortPos(); - // restoreGraphicsState(); - - if (breakOutList != null) { - restoreStateStackAfterBreakOut(breakOutList); - } - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - } else { - - currentBPPosition += bv.getSpaceBefore(); - - // borders and background in the old coordinate system - handleBlockTraits(bv); - - // Advance to start of content area - currentIPPosition += bv.getStartIndent(); - - CTM tempctm = new CTM(containingIPPosition, currentBPPosition); - ctm = tempctm.multiply(ctm); - - // Now adjust for border/padding - currentBPPosition += borderPaddingBefore; - - Rectangle2D clippingRect = null; - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - - // startVParea(ctm, clippingRect); - pushViewPortPos(new ViewPortPos(clippingRect, ctm)); - - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - // endVParea(); - popViewPortPos(); - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - - currentBPPosition += (int) (bv.getAllocBPD()); - } + public void endVParea() { + restoreGraphicsState(); } /** {@inheritDoc} */ - protected void renderReferenceArea(Block block) { - //TODO Remove this method once concatenateTransformationMatrix() is implemented - - // save position and offset - int saveIP = currentIPPosition; - int saveBP = currentBPPosition; - - //Establish a new coordinate system - AffineTransform at = new AffineTransform(); - at.translate(currentIPPosition, currentBPPosition); - at.translate(block.getXOffset(), block.getYOffset()); - at.translate(0, block.getSpaceBefore()); - - if (!at.isIdentity()) { - Rectangle2D contentRect - = new Rectangle2D.Double(at.getTranslateX(), at.getTranslateY(), - block.getAllocIPD(), block.getAllocBPD()); - pushViewPortPos(new ViewPortPos(contentRect, new CTM(at))); - } - - currentIPPosition = 0; - currentBPPosition = 0; - handleBlockTraits(block); - - List children = block.getChildAreas(); - if (children != null) { - renderBlocks(block, children); - } - + protected void concatenateTransformationMatrix(AffineTransform at) { if (!at.isIdentity()) { - popViewPortPos(); + currentState.concatenate(at); } - - // stacked and relative blocks effect stacking - currentIPPosition = saveIP; - currentBPPosition = saveBP; } - - /** {@inheritDoc} */ - protected void renderFlow(NormalFlow flow) { - // save position and offset - int saveIP = currentIPPosition; - int saveBP = currentBPPosition; - - //Establish a new coordinate system - AffineTransform at = new AffineTransform(); - at.translate(currentIPPosition, currentBPPosition); - - if (!at.isIdentity()) { - Rectangle2D contentRect - = new Rectangle2D.Double(at.getTranslateX(), at.getTranslateY(), - flow.getAllocIPD(), flow.getAllocBPD()); - pushViewPortPos(new ViewPortPos(contentRect, new CTM(at))); - } - currentIPPosition = 0; - currentBPPosition = 0; - super.renderFlow(flow); - - if (!at.isIdentity()) { - popViewPortPos(); - } - - // stacked and relative blocks effect stacking - currentIPPosition = saveIP; - currentBPPosition = saveBP; - } - - /** {@inheritDoc} */ - protected void concatenateTransformationMatrix(AffineTransform at) { - // Not used here since AFPRenderer defines its own renderBlockViewport() - // method. - throw new UnsupportedOperationException("NYI"); - } - - /** - * {@inheritDoc} - */ - public void renderPage(PageViewport pageViewport) { - - // initializeRootExtensions(page); - - // this.currentFontFamily = ""; - this.currentFontSize = 0; - this.pageFontCounter = 0; - this.currentPageFontMap = null; - // this.lineCache = new HashSet(); + public void renderPage(PageViewport pageViewport) throws IOException, FOPException { + currentState.clear(); Rectangle2D bounds = pageViewport.getViewArea(); + + AffineTransform basicPageTransform = new AffineTransform(); + int resolution = currentState.getResolution(); + double scale = (double)1 / (AFPConstants.DPI_72_MPTS / resolution); + basicPageTransform.scale(scale, scale); - this.pageWidth = mpts2units(bounds.getWidth()); - this.pageHeight = mpts2units(bounds.getHeight()); - - if (pages != null && pages.containsKey(pageViewport)) { + currentState.concatenate(basicPageTransform); + if (getPages().containsKey(pageViewport)) { getAFPDataStream().restorePage( - (PageObject) pages.remove(pageViewport)); - + (PageObject)getPages().remove(pageViewport)); } else { - // renderPageGroupExtensions(page); + int pageWidth + = (int)Math.round(bounds.getWidth() / (AFPConstants.DPI_72_MPTS / resolution)); + currentState.setPageWidth(pageWidth); + int pageHeight + = (int)Math.round(bounds.getHeight() / (AFPConstants.DPI_72_MPTS / resolution)); + currentState.setPageHeight(pageHeight); final int pageRotation = 0; getAFPDataStream().startPage(pageWidth, pageHeight, pageRotation, - getResolution(), getResolution()); + resolution, resolution); renderPageObjectExtensions(pageViewport); - } + + super.renderPage(pageViewport); - pushViewPortPos(new ViewPortPos()); - - renderPageAreas(pageViewport.getPage()); - - if (currentPageFontMap != null) { - Iterator iter = currentPageFontMap.values().iterator(); - while (iter.hasNext()) { - AFPFontAttributes afpFontAttributes = (AFPFontAttributes) iter - .next(); - - getAFPDataStream().createFont( - (byte) afpFontAttributes.getFontReference(), - afpFontAttributes.getFont(), - afpFontAttributes.getPointSize()); - } + AFPPageFonts pageFonts = currentState.getPageFonts(); + if (pageFonts != null && !pageFonts.isEmpty()) { + getAFPDataStream().addFontsToCurrentPage(pageFonts); } getAFPDataStream().endPage(); - - popViewPortPos(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void clip() { // TODO + log.debug("NYI clip()"); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void clipRect(float x, float y, float width, float height) { // TODO + log.debug("NYI clipRect(x=" + x + ",y=" + y + ",width=" + width + ", height=" + height + ")"); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void moveTo(float x, float y) { // TODO + log.debug("NYI moveTo(x=" + x + ",y=" + y + ")"); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void lineTo(float x, float y) { // TODO + log.debug("NYI lineTo(x=" + x + ",y=" + y + ")"); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void closePath() { // TODO + log.debug("NYI closePath()"); } - /** - * {@inheritDoc} - */ + private int[] mpts2units(float[] srcPts, float[] dstPts) { + return transformPoints(srcPts, dstPts, true); + } + + private int[] pts2units(float[] srcPts, float[] dstPts) { + return transformPoints(srcPts, dstPts, false); + } + + private int[] mpts2units(float[] srcPts) { + return transformPoints(srcPts, null, true); + } + + private int[] pts2units(float[] srcPts) { + return transformPoints(srcPts, null, false); + } + + /** {@inheritDoc} */ public void fillRect(float x, float y, float width, float height) { -// getAFPDataStream().createShading( -// pts2units(x), pts2units(y), pts2units(width), pts2units(height), -// currentColor); - getAFPDataStream().createLine(pts2units(x), pts2units(y), - pts2units(x + width), pts2units(y), pts2units(height), - currentColor); + float[] srcPts = new float[] {x * 1000, y * 1000}; + float[] dstPts = new float[srcPts.length]; + int[] coords = mpts2units(srcPts, dstPts); + int resolution = currentState.getResolution(); + int x2 = Math.round(dstPts[X] + ((width * 1000) / (AFPConstants.DPI_72_MPTS / resolution))); + int thickness = Math.round((height * 1000) / (AFPConstants.DPI_72_MPTS / resolution)); + getAFPDataStream().createLine( + coords[X], + coords[Y], + x2, + coords[Y], + thickness, + currentState.getColor()); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void drawBorderLine(float x1, float y1, float x2, float y2, boolean horz, boolean startOrBefore, int style, Color col) { - float w = x2 - x1; - float h = y2 - y1; - if ((w < 0) || (h < 0)) { + float[] srcPts = new float[] {x1 * 1000, y1 * 1000, x2 * 1000, y2 * 1000}; + float[] dstPts = new float[srcPts.length]; + int[] coords = mpts2units(srcPts, dstPts); + + float width = dstPts[X2] - dstPts[X1]; + float height = dstPts[Y2] - dstPts[Y1]; + if ((width < 0) || (height < 0)) { log.error("Negative extent received. Border won't be painted."); return; } @@ -816,58 +446,91 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { switch (style) { case Constants.EN_DOUBLE: if (horz) { - float h3 = h / 3; - float ym1 = y1; - float ym2 = ym1 + h3 + h3; - getAFPDataStream().createLine(pts2units(x1), pts2units(ym1), - pts2units(x2), pts2units(ym1), pts2units(h3), col); - getAFPDataStream().createLine(pts2units(x1), pts2units(ym2), - pts2units(x2), pts2units(ym2), pts2units(h3), col); + float h3 = height / 3; + float ym2 = dstPts[Y1] + h3 + h3; + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X2], + coords[Y1], + Math.round(h3), + col); + afpDataStream.createLine( + coords[X1], + Math.round(ym2), + coords[X2], + Math.round(ym2), + Math.round(h3), + col); } else { - float w3 = w / 3; - float xm1 = x1; - float xm2 = xm1 + w3 + w3; - getAFPDataStream().createLine(pts2units(xm1), pts2units(y1), - pts2units(xm1), pts2units(y2), pts2units(w3), col); - getAFPDataStream().createLine(pts2units(xm2), pts2units(y1), - pts2units(xm2), pts2units(y2), pts2units(w3), col); + float w3 = width / 3; + float xm2 = dstPts[X1] + w3 + w3; + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X1], + coords[Y2], + Math.round(w3), + col); + afpDataStream.createLine( + Math.round(xm2), + coords[Y1], + Math.round(xm2), + coords[Y2], + Math.round(w3), + col); } break; case Constants.EN_DASHED: if (horz) { - float w2 = 2 * h; - while (x1 + w2 < x2) { - getAFPDataStream().createLine(pts2units(x1), pts2units(y1), - pts2units(x1 + w2), pts2units(y1), pts2units(h), + float w2 = 2 * height; + while (coords[X1] + w2 < coords[X2]) { + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X1] + Math.round(w2), + coords[Y1], + Math.round(height), col); - x1 += 2 * w2; + coords[X1] += 2 * w2; } } else { - float h2 = 2 * w; - while (y1 + h2 < y2) { - getAFPDataStream().createLine(pts2units(x1), pts2units(y1), - pts2units(x1), pts2units(y1 + h2), pts2units(w), + float h2 = 2 * width; + while (coords[Y1] + h2 < coords[Y2]) { + afpDataStream.createLine( + coords[X1], + coords[Y2], + coords[X1], + coords[Y1] + Math.round(h2), + Math.round(width), col); - y1 += 2 * h2; + coords[Y1] += 2 * h2; } } break; case Constants.EN_DOTTED: if (horz) { - while (x1 + h < x2) { - getAFPDataStream() - .createLine(pts2units(x1), pts2units(y1), - pts2units(x1 + h), pts2units(y1), - pts2units(h), col); - x1 += 2 * h; + while (coords[X1] + height < coords[X2]) { + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X1] + Math.round(height), + coords[Y1], + Math.round(height), + col + ); + coords[X1] += 2 * height; } } else { - while (y1 + w < y2) { - getAFPDataStream() - .createLine(pts2units(x1), pts2units(y1), - pts2units(x1), pts2units(y1 + w), - pts2units(w), col); - y1 += 2 * w; + while (y1 + width < y2) { + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X1], + coords[Y1] + Math.round(width), + Math.round(width), + col); + coords[Y1] += 2 * width; } } break; @@ -877,29 +540,54 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { if (horz) { Color uppercol = lightenColor(col, -colFactor); Color lowercol = lightenColor(col, colFactor); - float h3 = h / 3; - float ym1 = y1; - getAFPDataStream().createLine(pts2units(x1), pts2units(ym1), - pts2units(x2), pts2units(ym1), pts2units(h3), uppercol); - getAFPDataStream().createLine(pts2units(x1), - pts2units(ym1 + h3), pts2units(x2), - pts2units(ym1 + h3), pts2units(h3), col); - getAFPDataStream().createLine(pts2units(x1), - pts2units(ym1 + h3 + h3), pts2units(x2), - pts2units(ym1 + h3 + h3), pts2units(h3), lowercol); + float h3 = height / 3; + afpDataStream.createLine( + coords[X1], + coords[Y1], + coords[X2], + coords[Y1], + Math.round(h3), + uppercol); + afpDataStream.createLine( + coords[X1], + Math.round(dstPts[Y1] + h3), + coords[X2], + Math.round(dstPts[Y1] + h3), + Math.round(h3), + col); + afpDataStream.createLine( + coords[X1], + Math.round(dstPts[Y1] + h3 + h3), + coords[X2], + Math.round(dstPts[Y1] + h3 + h3), + Math.round(h3), + lowercol); } else { Color leftcol = lightenColor(col, -colFactor); Color rightcol = lightenColor(col, colFactor); - float w3 = w / 3; - float xm1 = x1 + (w3 / 2); - getAFPDataStream().createLine(pts2units(xm1), pts2units(y1), - pts2units(xm1), pts2units(y2), pts2units(w3), leftcol); - getAFPDataStream().createLine(pts2units(xm1 + w3), - pts2units(y1), pts2units(xm1 + w3), pts2units(y2), - pts2units(w3), col); - getAFPDataStream().createLine(pts2units(xm1 + w3 + w3), - pts2units(y1), pts2units(xm1 + w3 + w3), pts2units(y2), - pts2units(w3), rightcol); + float w3 = width / 3; + float xm1 = dstPts[X1] + (w3 / 2); + afpDataStream.createLine( + Math.round(xm1), + coords[Y1], + Math.round(xm1), + coords[Y2], + Math.round(w3), + leftcol); + afpDataStream.createLine( + Math.round(xm1 + w3), + coords[Y1], + Math.round(xm1 + w3), + coords[Y2], + Math.round(w3), + col); + afpDataStream.createLine( + Math.round(xm1 + w3 + w3), + coords[Y1], + Math.round(xm1 + w3 + w3), + coords[Y2], + Math.round(w3), + rightcol); } break; } @@ -908,28 +596,24 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { case Constants.EN_INSET: case Constants.EN_OUTSET: default: - getAFPDataStream().createLine(pts2units(x1), pts2units(y1), - pts2units(horz ? x2 : x1), pts2units(horz ? y1 : y2), - pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))), col); + afpDataStream.createLine( + coords[X1], + coords[Y1], + (horz ? coords[X2] : coords[X1]), + (horz ? coords[Y1] : coords[Y2]), + Math.abs(Math.round(horz ? height : width)), + col); } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected RendererContext createRendererContext(int x, int y, int width, int height, Map foreignAttributes) { RendererContext context; context = super.createRendererContext(x, y, width, height, foreignAttributes); - context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE, Boolean - .valueOf(!this.colorImages)); context.setProperty(AFPRendererContextConstants.AFP_FONT_INFO, this.fontInfo); - context.setProperty(AFPRendererContextConstants.AFP_RESOLUTION, - new Integer(this.resolution)); - context.setProperty(AFPRendererContextConstants.AFP_BITS_PER_PIXEL, - new Integer(this.bitsPerPixel)); context.setProperty(AFPRendererContextConstants.AFP_DATASTREAM, getAFPDataStream()); context.setProperty(AFPRendererContextConstants.AFP_STATE, getState()); @@ -944,17 +628,18 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { /** {@inheritDoc} */ public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) { uri = URISpecification.getURL(uri); - getState().setImageUri(uri); + currentState.setImageUri(uri); Rectangle posInt = new Rectangle((int) pos.getX(), (int) pos.getY(), (int) pos.getWidth(), (int) pos.getHeight()); Point origin = new Point(currentIPPosition, currentBPPosition); int x = origin.x + posInt.x; int y = origin.y + posInt.y; - + String name = (String)getPageSegments().get(uri); if (name != null) { - getAFPDataStream().createIncludePageSegment(name, mpts2units(x), - mpts2units(y)); + float[] srcPts = {x, y}; + int[] coords = mpts2units(srcPts); + getAFPDataStream().createIncludePageSegment(name, coords[X], coords[Y]); } else { ImageManager manager = getUserAgent().getFactory().getImageManager(); ImageInfo info = null; @@ -989,31 +674,41 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { ImageRawCCITTFax ccitt = (ImageRawCCITTFax) img; in = ccitt.createInputStream(); byte[] buf = IOUtils.toByteArray(in); - int afpx = mpts2units(posInt.x + currentIPPosition); - int afpy = mpts2units(posInt.y + currentBPPosition); - int afpw = mpts2units(posInt.getWidth()); - int afph = mpts2units(posInt.getHeight()); - int afpres = getResolution(); + float[] srcPts = new float[] { + posInt.x + currentIPPosition, + posInt.y + currentBPPosition, + (float)posInt.getWidth(), + (float)posInt.getHeight() + }; + int[] coords = mpts2units(srcPts); String mimeType = info.getMimeType(); // create image object parameters ImageObjectInfo imageObjectInfo = new ImageObjectInfo(); + imageObjectInfo.setBuffered(false); imageObjectInfo.setUri(uri); imageObjectInfo.setMimeType(mimeType); ObjectAreaInfo objectAreaInfo = new ObjectAreaInfo(); - objectAreaInfo.setX(afpx); - objectAreaInfo.setY(afpy); - objectAreaInfo.setWidth(afpw); - objectAreaInfo.setHeight(afph); - objectAreaInfo.setWidthRes(afpres); - objectAreaInfo.setHeightRes(afpres); + objectAreaInfo.setX(coords[X]); + objectAreaInfo.setY(coords[Y]); + int resolution = currentState.getResolution(); + int w = Math.round( + ((float)posInt.getWidth() * 1000) + / (AFPConstants.DPI_72_MPTS / resolution)); + int h = Math.round( + ((float)posInt.getHeight() * 1000) + / (AFPConstants.DPI_72_MPTS / resolution)); + objectAreaInfo.setWidth(w); + objectAreaInfo.setHeight(h); + objectAreaInfo.setWidthRes(resolution); + objectAreaInfo.setHeightRes(resolution); imageObjectInfo.setObjectAreaInfo(objectAreaInfo); imageObjectInfo.setData(buf); imageObjectInfo.setDataHeight(ccitt.getSize().getHeightPx()); imageObjectInfo.setDataWidth(ccitt.getSize().getWidthPx()); - imageObjectInfo.setColor(colorImages); - imageObjectInfo.setBitsPerPixel(bitsPerPixel); + imageObjectInfo.setColor(currentState.isColorImages()); + imageObjectInfo.setBitsPerPixel(currentState.getBitsPerPixel()); imageObjectInfo.setCompression(ccitt.getCompression()); imageObjectInfo.setResourceInfoFromForeignAttributes(foreignAttributes); getAFPDataStream().createObject(imageObjectInfo); @@ -1079,15 +774,15 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * the x coordinate (in mpt) * @param y * the y coordinate (in mpt) - * @param w + * @param width * the width of the viewport (in mpt) - * @param h + * @param height * the height of the viewport (in mpt) * @param foreignAttributes * a mapping of foreign attributes */ public void drawBufferedImage(ImageInfo imageInfo, RenderedImage image, - int imageRes, int x, int y, int w, int h, Map foreignAttributes) { + int imageRes, int x, int y, int width, int height, Map foreignAttributes) { ByteArrayOutputStream baout = new ByteArrayOutputStream(); try { // Serialize image @@ -1111,129 +806,127 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { } ObjectAreaInfo objectAreaInfo = new ObjectAreaInfo(); - objectAreaInfo.setX(mpts2units(x)); - objectAreaInfo.setY(mpts2units(y)); - objectAreaInfo.setWidth(mpts2units(w)); - objectAreaInfo.setHeight(mpts2units(h)); + + float[] srcPts = new float[] {x, y}; + int[] coords = mpts2units(srcPts); + objectAreaInfo.setX(coords[X]); + objectAreaInfo.setY(coords[Y]); + int resolution = currentState.getResolution(); + int w = Math.round( + (width * 1000) + / (AFPConstants.DPI_72_MPTS / resolution)); + int h = Math.round( + (height * 1000) + / (AFPConstants.DPI_72_MPTS / resolution)); + objectAreaInfo.setWidth(w); + objectAreaInfo.setHeight(h); + objectAreaInfo.setWidthRes(imageRes); objectAreaInfo.setHeightRes(imageRes); imageObjectInfo.setObjectAreaInfo(objectAreaInfo); - + imageObjectInfo.setData(baout.toByteArray()); imageObjectInfo.setDataHeight(image.getHeight()); imageObjectInfo.setDataWidth(image.getWidth()); - imageObjectInfo.setColor(colorImages); - imageObjectInfo.setBitsPerPixel(bitsPerPixel); + imageObjectInfo.setColor(currentState.isColorImages()); + imageObjectInfo.setBitsPerPixel(currentState.getBitsPerPixel()); imageObjectInfo.setResourceInfoFromForeignAttributes(foreignAttributes); getAFPDataStream().createObject(imageObjectInfo); } - /** - * Establishes a new foreground or fill color. {@inheritDoc} - */ + /** {@inheritDoc} */ public void updateColor(Color col, boolean fill) { if (fill) { - currentColor = col; + currentState.setColor(col); } } /** {@inheritDoc} */ - public List breakOutOfStateStack() { - log.debug("Block.FIXED --> break out"); - List breakOutList = new java.util.ArrayList(); - //Don't pop the last ViewPortPos (created by renderPage()) - while (this.viewPortPositions.size() > 1) { - breakOutList.add(0, popViewPortPos()); + public void restoreStateStackAfterBreakOut(List breakOutList) { + log.debug("Block.FIXED --> restoring context after break-out"); + AbstractState.AbstractData data; + Iterator it = breakOutList.iterator(); + while (it.hasNext()) { + data = (AbstractState.AbstractData)it.next(); + saveGraphicsState(); + concatenateTransformationMatrix(data.getTransform()); } - return breakOutList; } /** {@inheritDoc} */ - public void restoreStateStackAfterBreakOut(List breakOutList) { - log.debug("Block.FIXED --> restoring context after break-out"); - for (int i = 0, c = breakOutList.size(); i < c; i++) { - ViewPortPos vps = (ViewPortPos)breakOutList.get(i); - pushViewPortPos(vps); + protected List breakOutOfStateStack() { + log.debug("Block.FIXED --> break out"); + List breakOutList = new java.util.ArrayList(); + AbstractState.AbstractData data; + while (true) { + data = currentState.getData(); + if (currentState.pop() == null) { + break; + } + breakOutList.add(0, data); //Insert because of stack-popping } + return breakOutList; } - /** Saves the graphics state of the rendering engine. */ + /** {@inheritDoc} */ public void saveGraphicsState() { - + currentState.push(); } - /** Restores the last graphics state of the rendering engine. */ + /** {@inheritDoc} */ public void restoreGraphicsState() { - + currentState.pop(); } /** Indicates the beginning of a text object. */ public void beginTextObject() { - + //TODO maybe? + log.debug("NYI beginTextObject()"); } /** Indicates the end of a text object. */ public void endTextObject() { - + //TODO maybe? + log.debug("NYI endTextObject()"); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { drawImage(image.getURL(), pos, image.getForeignAttributes()); } - /** - * {@inheritDoc} - */ - public void renderText(TextArea text) { + /** {@inheritDoc} */ + public void renderText(TextArea text) { + log.debug(text.getText()); renderInlineAreaBackAndBorders(text); String name = getInternalFontNameForArea(text); - this.currentFontSize = ((Integer) text.getTrait(Trait.FONT_SIZE)) - .intValue(); - AFPFont font = (AFPFont) fontInfo.getFonts().get(name); - - Color col = (Color) text.getTrait(Trait.COLOR); - - int vsci = mpts2units(font.getWidth(' ', currentFontSize) / 1000 - + text.getTextWordSpaceAdjust() - + text.getTextLetterSpaceAdjust()); - - // word.getOffset() = only height of text itself - // currentBlockIPPosition: 0 for beginning of line; nonzero - // where previous line area failed to take up entire allocated space - int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); - int bl = currentBPPosition + text.getOffset() - + text.getBaselineOffset(); + int fontSize = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); + currentState.setFontSize(fontSize); + AFPFont font = (AFPFont)fontInfo.getFonts().get(name); // Set letterSpacing // float ls = fs.getLetterSpacing() / this.currentFontSize; - String worddata = text.getText(); - // Create an AFPFontAttributes object from the current font details - AFPFontAttributes afpFontAttributes = new AFPFontAttributes(name, font, - currentFontSize); + AFPFontAttributes afpFontAttributes + = new AFPFontAttributes(name, font, fontSize); - if (!getCurrentPageFonts().containsKey(afpFontAttributes.getFontKey())) { + AFPPageFonts pageFonts = currentState.getPageFonts(); + if (!pageFonts.containsKey(afpFontAttributes.getFontKey())) { // Font not found on current page, so add the new one - this.pageFontCounter++; - afpFontAttributes.setFontReference(pageFontCounter); - getCurrentPageFonts().put(afpFontAttributes.getFontKey(), - afpFontAttributes); + afpFontAttributes.setFontReference(currentState.incrementPageFontCount()); + pageFonts.put(afpFontAttributes.getFontKey(), afpFontAttributes); } else { // Use the previously stored font attributes - afpFontAttributes = (AFPFontAttributes) currentPageFontMap - .get(afpFontAttributes.getFontKey()); + afpFontAttributes = (AFPFontAttributes) pageFonts.get(afpFontAttributes.getFontKey()); } // Try and get the encoding to use for the font String encoding = null; try { - encoding = font.getCharacterSet(currentFontSize).getEncoding(); + encoding = font.getCharacterSet(fontSize).getEncoding(); } catch (Throwable ex) { encoding = AFPConstants.EBCIDIC_ENCODING; log.warn("renderText():: Error getting encoding for font '" @@ -1241,19 +934,50 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { + encoding); } + byte[] data = null; try { - getAFPDataStream().createText(afpFontAttributes.getFontReference(), - mpts2units(rx), mpts2units(bl), col, vsci, - mpts2units(text.getTextLetterSpaceAdjust()), - worddata.getBytes(encoding)); + String worddata = text.getText(); + data = worddata.getBytes(encoding); } catch (UnsupportedEncodingException usee) { log.error("renderText:: Font " + afpFontAttributes.getFontKey() + " caused UnsupportedEncodingException"); + return; } + int fontReference = afpFontAttributes.getFontReference(); + + int x = (currentIPPosition + text.getBorderAndPaddingWidthStart()); + int y = (currentBPPosition + text.getOffset() + text.getBaselineOffset()); + float[] srcPts = new float[] {x, y}; + int[] coords = mpts2units(srcPts); + + Color color = (Color) text.getTrait(Trait.COLOR); + + int variableSpaceCharacterIncrement = font.getWidth(' ', fontSize) / 1000 + + text.getTextWordSpaceAdjust() + + text.getTextLetterSpaceAdjust(); + int resolution = currentState.getResolution(); + variableSpaceCharacterIncrement /= (AFPConstants.DPI_72_MPTS / resolution); + + int interCharacterAdjustment = text.getTextLetterSpaceAdjust(); + interCharacterAdjustment /= (AFPConstants.DPI_72_MPTS / resolution); + + AFPTextDataInfo textDataInfo = new AFPTextDataInfo(); + textDataInfo.setFontReference(fontReference); + textDataInfo.setX(coords[X]); + textDataInfo.setY(coords[Y]); + textDataInfo.setColor(color); + textDataInfo.setVariableSpaceCharacterIncrement(variableSpaceCharacterIncrement); + textDataInfo.setInterCharacterAdjustment(interCharacterAdjustment); + textDataInfo.setData(data); + getAFPDataStream().createText(textDataInfo); + // word.getOffset() = only height of text itself + // currentBlockIPPosition: 0 for beginning of line; nonzero + // where previous line area failed to take up entire allocated space + super.renderText(text); - renderTextDecoration(font, currentFontSize, text, bl, rx); + renderTextDecoration(font, fontSize, text, coords[Y], coords[X]); } /** @@ -1291,58 +1015,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { super.renderLeader(area); } - /** - * Sets the AFPRenderer options - * - * @param options - * the Map containing the options - */ - // UNUSED - // public void setOptions(Map options) { - // - // this.afpOptions = options; - // - // } - /** - * Determines the orientation from the string representation, this method - * guarantees to return a value of either 0, 90, 180 or 270. - * - * @return the orientation - */ - // UNUSED - // private int getOrientation(String orientationString) { - // - // int orientation = 0; - // if (orientationString != null && orientationString.length() > 0) { - // try { - // orientation = Integer.parseInt(orientationString); - // } catch (NumberFormatException nfe) { - // log.error("Cannot use orientation of " + orientation - // + " defaulting to zero."); - // orientation = 0; - // } - // } else { - // orientation = 0; - // } - // switch (orientation) { - // case 0: - // break; - // case 90: - // break; - // case 180: - // break; - // case 270: - // break; - // default: - // log.error("Cannot use orientation of " + orientation - // + " defaulting to zero."); - // orientation = 0; - // break; - // } - // - // return orientation; - // - // } /** * Sets the rotation to be used for portrait pages, valid values are 0 * (default), 90, 180, 270. @@ -1351,15 +1023,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * The rotation in degrees. */ public void setPortraitRotation(int rotation) { - if (rotation == 0 || rotation == 90 || rotation == 180 - || rotation == 270) { - portraitRotation = rotation; - } else { - throw new IllegalArgumentException( - "The portrait rotation must be one" - + " of the values 0, 90, 180, 270"); - - } + currentState.setPortraitRotation(rotation); } /** @@ -1370,14 +1034,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * The rotation in degrees. */ public void setLandscapeRotation(int rotation) { - if (rotation == 0 || rotation == 90 || rotation == 180 - || rotation == 270) { - landscapeRotation = rotation; - } else { - throw new IllegalArgumentException( - "The landscape rotation must be one" - + " of the values 0, 90, 180, 270"); - } + currentState.setLandscapeRotation(rotation); } /** @@ -1444,215 +1101,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { } - /** - * Converts FOP mpt measurement to afp measurement units - * - * @param mpt - * the millipoints value - */ - private int mpts2units(int mpt) { - return mpts2units((double) mpt); - } - - /** - * Converts FOP pt measurement to afp measurement units - * - * @param mpt - * the millipoints value - */ - private int pts2units(float mpt) { - return mpts2units(mpt * 1000d); - } - - /** - * Converts FOP mpt measurement to afp measurement units - * - * @param mpt - * the millipoints value - * @return afp measurement unit value - */ - private int mpts2units(double mpt) { - return (int) Math - .round(mpt / (DPI_CONVERSION_FACTOR / getResolution())); - } - - /** - * Converts a byte array containing 24 bit RGB image data to a grayscale - * image. - * - * @param io - * the target image object - * @param raw - * the buffer containing the RGB image data - * @param width - * the width of the image in pixels - * @param height - * the height of the image in pixels - * @param bitsPerPixel - * the number of bits to use per pixel - */ - protected static void convertToGrayScaleImage(ImageObject io, byte[] raw, - int width, int height, int bitsPerPixel) { - int pixelsPerByte = 8 / bitsPerPixel; - int bytewidth = (width / pixelsPerByte); - if ((width % pixelsPerByte) != 0) { - bytewidth++; - } - byte[] bw = new byte[height * bytewidth]; - byte ib; - for (int y = 0; y < height; y++) { - ib = 0; - int i = 3 * y * width; - for (int x = 0; x < width; x++, i += 3) { - - // see http://www.jguru.com/faq/view.jsp?EID=221919 - double greyVal = 0.212671d * ((int) raw[i] & 0xff) + 0.715160d - * ((int) raw[i + 1] & 0xff) + 0.072169d - * ((int) raw[i + 2] & 0xff); - switch (bitsPerPixel) { - case 1: - if (greyVal < 128) { - ib |= (byte) (1 << (7 - (x % 8))); - } - break; - case 4: - greyVal /= 16; - ib |= (byte) ((byte) greyVal << ((1 - (x % 2)) * 4)); - break; - case 8: - ib = (byte) greyVal; - break; - default: - throw new UnsupportedOperationException( - "Unsupported bits per pixel: " + bitsPerPixel); - } - - if ((x % pixelsPerByte) == (pixelsPerByte - 1) - || ((x + 1) == width)) { - bw[(y * bytewidth) + (x / pixelsPerByte)] = ib; - ib = 0; - } - } - } - io.setImageIDESize((byte) bitsPerPixel); - io.setImageData(bw); - } - - private final class ViewPortPos { - private int x = 0; - - private int y = 0; - - private int rot = 0; - - private ViewPortPos() { - } - - private ViewPortPos(Rectangle2D view, CTM ctm) { - ViewPortPos currentVP = (ViewPortPos) viewPortPositions - .get(viewPortPositions.size() - 1); - int xOrigin; - int yOrigin; - int width; - int height; - switch (currentVP.rot) { - case 90: - width = mpts2units(view.getHeight()); - height = mpts2units(view.getWidth()); - xOrigin = pageWidth - width - mpts2units(view.getY()) - - currentVP.y; - yOrigin = mpts2units(view.getX()) + currentVP.x; - break; - case 180: - width = mpts2units(view.getWidth()); - height = mpts2units(view.getHeight()); - xOrigin = pageWidth - width - mpts2units(view.getX()) - - currentVP.x; - yOrigin = pageHeight - height - mpts2units(view.getY()) - - currentVP.y; - break; - case 270: - width = mpts2units(view.getHeight()); - height = mpts2units(view.getWidth()); - xOrigin = mpts2units(view.getY()) + currentVP.y; - yOrigin = pageHeight - height - mpts2units(view.getX()) - - currentVP.x; - break; - default: - xOrigin = mpts2units(view.getX()) + currentVP.x; - yOrigin = mpts2units(view.getY()) + currentVP.y; - width = mpts2units(view.getWidth()); - height = mpts2units(view.getHeight()); - break; - } - this.rot = currentVP.rot; - double[] ctmf = ctm.toArray(); - if (ctmf[0] == 0.0d && ctmf[1] == -1.0d && ctmf[2] == 1.0d - && ctmf[3] == 0.d) { - this.rot += 270; - } else if (ctmf[0] == -1.0d && ctmf[1] == 0.0d && ctmf[2] == 0.0d - && ctmf[3] == -1.0d) { - this.rot += 180; - } else if (ctmf[0] == 0.0d && ctmf[1] == 1.0d && ctmf[2] == -1.0d - && ctmf[3] == 0.0d) { - this.rot += 90; - } - this.rot %= 360; - switch (this.rot) { - /* - * case 0: this.x = mpts2units(view.getX()) + x; this.y = - * mpts2units(view.getY()) + y; break; case 90: this.x = - * mpts2units(view.getY()) + y; this.y = _pageWidth - - * mpts2units(view.getX() + view.getWidth()) - x; break; case 180: - * this.x = _pageWidth - mpts2units(view.getX() + view.getWidth()) - - * x; this.y = _pageHeight - mpts2units(view.getY() + - * view.getHeight()) - y; break; case 270: this.x = _pageHeight - - * mpts2units(view.getY() + view.getHeight()) - y; this.y = - * mpts2units(view.getX()) + x; break; - */ - case 0: - this.x = xOrigin; - this.y = yOrigin; - break; - case 90: - this.x = yOrigin; - this.y = pageWidth - width - xOrigin; - break; - case 180: - this.x = pageWidth - width - xOrigin; - this.y = pageHeight - height - yOrigin; - break; - case 270: - this.x = pageHeight - height - yOrigin; - this.y = xOrigin; - break; - default: - } - } - - public String toString() { - return "x:" + x + " y:" + y + " rot:" + rot; - } - - } - - private List viewPortPositions = new java.util.ArrayList(); - - private void pushViewPortPos(ViewPortPos vpp) { - viewPortPositions.add(vpp); - getAFPDataStream().setOffsets(vpp.x, vpp.y, vpp.rot); - } - - private ViewPortPos popViewPortPos() { - ViewPortPos current = (ViewPortPos)viewPortPositions.remove(viewPortPositions.size() - 1); - if (viewPortPositions.size() > 0) { - ViewPortPos vpp = (ViewPortPos) viewPortPositions - .get(viewPortPositions.size() - 1); - getAFPDataStream().setOffsets(vpp.x, vpp.y, vpp.rot); - } - return current; - } - /** * Sets the number of bits used per pixel * @@ -1660,17 +1108,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * number of bits per pixel */ public void setBitsPerPixel(int bitsPerPixel) { - this.bitsPerPixel = bitsPerPixel; - switch (bitsPerPixel) { - case 1: - case 4: - case 8: - break; - default: - log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8."); - bitsPerPixel = 8; - break; - } + currentState.setBitsPerPixel(bitsPerPixel); } /** @@ -1680,7 +1118,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * color image output */ public void setColorImages(boolean colorImages) { - this.colorImages = colorImages; + currentState.setColorImages(colorImages); } /** @@ -1702,10 +1140,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * the output resolution (dpi) */ public void setResolution(int resolution) { - if (log.isDebugEnabled()) { - log.debug("renderer-resolution set to: " + resolution + "dpi"); - } - this.resolution = resolution; + ((AFPState)getState()).setResolution(resolution); } /** @@ -1714,18 +1149,19 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { * @return the resolution in dpi */ public int getResolution() { - return this.resolution; + return ((AFPState)getState()).getResolution(); } - private AFPState getState() { + /** + * @return the current AFP state + */ + protected AbstractState getState() { if (currentState == null) { currentState = new AFPState(); } return currentState; } - private boolean gocaEnabled = false; - /** * @param enabled * true if AFP GOCA is enabled for SVG support @@ -1740,4 +1176,61 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { protected boolean isGOCAEnabled() { return this.gocaEnabled; } + + // TODO: remove this and use the superclass implementation + /** {@inheritDoc} */ + protected void renderReferenceArea(Block block) { + // save position and offset + int saveIP = currentIPPosition; + int saveBP = currentBPPosition; + + //Establish a new coordinate system + AffineTransform at = new AffineTransform(); + at.translate(currentIPPosition, currentBPPosition); + at.translate(block.getXOffset(), block.getYOffset()); + at.translate(0, block.getSpaceBefore()); + + if (!at.isIdentity()) { + saveGraphicsState(); + concatenateTransformationMatrix(at); + } + + currentIPPosition = 0; + currentBPPosition = 0; + handleBlockTraits(block); + + List children = block.getChildAreas(); + if (children != null) { + renderBlocks(block, children); + } + + if (!at.isIdentity()) { + restoreGraphicsState(); + } + + // stacked and relative blocks effect stacking + currentIPPosition = saveIP; + currentBPPosition = saveBP; + } + + protected int[] transformPoints(float[] srcPts, float[] dstPts) { + return transformPoints(srcPts, dstPts, true); + } + + protected int[] transformPoints(float[] srcPts, float[] dstPts, boolean milli) { + if (dstPts == null) { + dstPts = new float[srcPts.length]; + } + AbstractState state = (AbstractState)getState(); + AffineTransform at = state.getData().getTransform(); + at.transform(srcPts, 0, dstPts, 0, srcPts.length / 2); + int[] coords = new int[srcPts.length]; + for (int i = 0; i < srcPts.length; i++) { + if (!milli) { + dstPts[i] *= 1000; + } + coords[i] = Math.round(dstPts[i]); + } + return coords; + } } diff --git a/src/java/org/apache/fop/render/afp/AFPRendererContextConstants.java b/src/java/org/apache/fop/render/afp/AFPRendererContextConstants.java index a4b449bb0..360132863 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererContextConstants.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererContextConstants.java @@ -35,15 +35,9 @@ public interface AFPRendererContextConstants extends RendererContextConstants { /** The font information for the AFP renderer. */ String AFP_FONT_INFO = "afpFontInfo"; - /** The afp resolution. */ - String AFP_RESOLUTION = "afpResolution"; - /** The afp datastream */ String AFP_DATASTREAM = "afpDataStream"; /** The afp state */ String AFP_STATE = "afpPageState"; - - /** The afp bits per pixel */ - String AFP_BITS_PER_PIXEL = "afpBitsPerPixel"; } diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index b0d155cd9..9173a1d23 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -34,6 +34,7 @@ import org.apache.fop.render.AbstractGenericSVGHandler; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContextConstants; +import org.apache.fop.render.afp.modca.AFPConstants; import org.apache.fop.render.afp.modca.AFPDataStream; import org.apache.fop.render.afp.modca.GraphicsObject; import org.apache.fop.svg.SVGUserAgent; @@ -71,19 +72,13 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { afpi.setHandlerConfiguration((Configuration)context.getProperty(HANDLER_CONFIGURATION)); afpi.setFontInfo((org.apache.fop.fonts.FontInfo)context.getProperty( AFPRendererContextConstants.AFP_FONT_INFO)); - afpi.setResolution(((Integer)context.getProperty( - AFPRendererContextConstants.AFP_RESOLUTION)).intValue()); afpi.setState((AFPState)context.getProperty( AFPRendererContextConstants.AFP_STATE)); afpi.setAFPDataStream((AFPDataStream)context.getProperty( AFPRendererContextConstants.AFP_DATASTREAM)); - afpi.setColor(!((Boolean)context.getProperty( - AFPRendererContextConstants.AFP_GRAYSCALE)).booleanValue()); - afpi.setBitsPerPixel(((Integer)context.getProperty( - AFPRendererContextConstants.AFP_BITS_PER_PIXEL)).intValue()); return afpi; } - + /** * Render the SVG document. * @@ -137,20 +132,20 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { double h = ctx.getDocumentSize().getHeight() * 1000f; // convert to afp inches - double sx = ((afpInfo.getWidth() / w) * res) / 72f; - double sy = ((afpInfo.getHeight() / h) * res) / 72f; - double xOffset = (afpInfo.getX() * res) / 72000f; - double yOffset = ((afpInfo.getHeight() - afpInfo.getY()) * res) / 72000f; + double scaleX = ((afpInfo.getWidth() / w) * res) / AFPConstants.DPI_72; + double scaleY = ((afpInfo.getHeight() / h) * res) / AFPConstants.DPI_72; + double xOffset = (afpInfo.getX() * res) / AFPConstants.DPI_72_MPTS; + double yOffset = ((afpInfo.getHeight() - afpInfo.getY()) * res) / AFPConstants.DPI_72_MPTS; // Transformation matrix that establishes the local coordinate system for the SVG graphic // in relation to the current coordinate system (note: y axis is inverted) - AffineTransform trans = new AffineTransform(sx, 0, 0, -sy, xOffset, yOffset); + AffineTransform trans = new AffineTransform(scaleX, 0, 0, -scaleY, xOffset, yOffset); graphics.setTransform(trans); - int x = (int)Math.round((afpInfo.getX() * 25.4f) / 1000); - int y = (int)Math.round((afpInfo.getY() * 25.4f) / 1000); - int width = (int)Math.round((afpInfo.getWidth() * res) / 72000f); - int height = (int)Math.round((afpInfo.getHeight() * res) / 72000f); + int x = (int)Math.round((afpInfo.getX() * 25.4f) / 1000f); + int y = (int)Math.round((afpInfo.getY() * 25.4f) / 1000f); + int width = (int)Math.round((afpInfo.getWidth() * res) / AFPConstants.DPI_72_MPTS); + int height = (int)Math.round((afpInfo.getHeight() * res) / AFPConstants.DPI_72_MPTS); // set the data object parameters DataObjectInfo dataObjectInfo = new DataObjectInfo(); diff --git a/src/java/org/apache/fop/render/afp/AFPState.java b/src/java/org/apache/fop/render/afp/AFPState.java index 7c28e8bf0..9d93817be 100644 --- a/src/java/org/apache/fop/render/afp/AFPState.java +++ b/src/java/org/apache/fop/render/afp/AFPState.java @@ -19,157 +19,183 @@ package org.apache.fop.render.afp; -import java.awt.Color; -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** - * This keeps information about the current state when writing to pdf. + * This keeps information about the current state when writing to an AFP datastream. */ -public class AFPState { - private Data data = new Data(); - - private List stateStack = new java.util.ArrayList(); +public class AFPState extends org.apache.fop.render.AbstractState { + + private static Log log = LogFactory.getLog("org.apache.fop.render.afp.AFPState"); /** - * Set the current color. - * Check if the new color is a change and then set the current color. - * - * @param col the color to set - * @return true if the color has changed + * The portrait rotation */ - protected boolean setColor(Color col) { - if (!col.equals(getData().color)) { - getData().color = col; - return true; + private int portraitRotation = 0; + + /** + * The landscape rotation + */ + private int landscapeRotation = 270; + + /** + * Flag to the set the output object type for images + */ + private boolean colorImages = false; + + /** + * Default value for image depth + */ + private int bitsPerPixel = 8; + + /** + * The output resolution + */ + private int resolution = 240; // 240 dpi + + /** + * The current page + */ + private AFPPageState pageState = new AFPPageState(); + + /** + * Sets the rotation to be used for portrait pages, valid values are 0 + * (default), 90, 180, 270. + * + * @param rotation + * The rotation in degrees. + */ + protected void setPortraitRotation(int rotation) { + if (rotation == 0 || rotation == 90 || rotation == 180 + || rotation == 270) { + portraitRotation = rotation; + } else { + throw new IllegalArgumentException( + "The portrait rotation must be one" + + " of the values 0, 90, 180, 270"); + } - return false; } /** - * Sets if the current painted shape is to be filled - * @param fill true if the current painted shape is to be filled - * @return true if the fill value has changed + * @return the rotation to be used for portrait pages */ - protected boolean setFill(boolean fill) { - if (fill != getData().filled) { - getData().filled = fill; - return true; - } - return false; + protected int getPortraitRotation() { + return this.portraitRotation; } /** - * Get the color. - * @return the color + * Sets the rotation to be used for landscape pages, valid values are 0, 90, + * 180, 270 (default). + * + * @param rotation + * The rotation in degrees. */ - protected Color getColor() { - if (getData().color == null) { - getData().color = Color.black; + protected void setLandscapeRotation(int rotation) { + if (rotation == 0 || rotation == 90 || rotation == 180 + || rotation == 270) { + landscapeRotation = rotation; + } else { + throw new IllegalArgumentException( + "The landscape rotation must be one" + + " of the values 0, 90, 180, 270"); } - return getData().color; } /** - * Set the current line width. - * @param width the line width in points - * @return true if the line width has changed + * @return the landscape rotation */ - protected boolean setLineWidth(float width) { - if (getData().lineWidth != width) { - getData().lineWidth = width; - return true; - } - return false; + protected int getLandscapeRotation() { + return this.landscapeRotation; } /** - * Sets the dash array (line type) for the current basic stroke - * @param dash the line dash array - * @return true if the dash array has changed + * Sets the number of bits used per pixel + * + * @param bitsPerPixel + * number of bits per pixel */ - public boolean setDashArray(float[] dash) { - if (!Arrays.equals(dash, getData().dashArray)) { - getData().dashArray = dash; - return true; + public void setBitsPerPixel(int bitsPerPixel) { + this.bitsPerPixel = bitsPerPixel; + switch (bitsPerPixel) { + case 1: + case 4: + case 8: + break; + default: + log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8."); + bitsPerPixel = 8; + break; } - return false; } /** - * Gets the current line width - * @return the current line width + * @return the number of bits per pixel */ - protected float getLineWidth() { - return getData().lineWidth; + public int getBitsPerPixel() { + return this.bitsPerPixel; } /** - * Get the background color. - * @return the background color + * Sets whether images are color or not + * + * @param colorImages + * color image output */ - protected Color getBackColor() { - if (getData().backColor == null) { - getData().backColor = Color.white; - } - return getData().backColor; + public void setColorImages(boolean colorImages) { + this.colorImages = colorImages; } /** - * Set the current background color. - * Check if the new background color is a change and then set the current background color. - * - * @param col the background color to set - * @return true if the color has changed + * @return true if color images are to be used */ - protected boolean setBackColor(Color col) { - if (!col.equals(getData().backColor)) { - getData().backColor = col; - return true; - } - return false; + protected boolean isColorImages() { + return this.colorImages; } /** - * Set the current font name - * @param internalFontName the internal font name - * @return true if the font name has changed + * Sets the output/device resolution + * + * @param resolution + * the output resolution (dpi) */ - protected boolean setFontName(String internalFontName) { - if (!internalFontName.equals(getData().fontName)) { - getData().fontName = internalFontName; - return true; + public void setResolution(int resolution) { + if (log.isDebugEnabled()) { + log.debug("renderer-resolution set to: " + resolution + "dpi"); } - return false; + this.resolution = resolution; } /** - * Gets the current font name - * @return the current font name + * Returns the output/device resolution. + * + * @return the resolution in dpi */ - protected String getFontName() { - return getData().fontName; + protected int getResolution() { + return this.resolution; } - + + /** {@inheritDoc} */ + protected AbstractData instantiateData() { + return new AFPData(); + } + /** - * Gets the current font size - * @return the current font size + * @return the state of the current page */ - protected int getFontSize() { - return getData().fontSize; + protected AFPPageState getPageState() { + return this.pageState; } - + /** - * Set the current font size. - * Check if the font size is a change and then set the current font size. - * - * @param size the font size to set - * @return true if the font size has changed + * Sets if the current painted shape is to be filled + * @param fill true if the current painted shape is to be filled + * @return true if the fill value has changed */ - protected boolean setFontSize(int size) { - if (size != getData().fontSize) { - getData().fontSize = size; + protected boolean setFill(boolean fill) { + if (fill != ((AFPData)getData()).filled) { + ((AFPData)getData()).filled = fill; return true; } return false; @@ -180,137 +206,177 @@ public class AFPState { * @return the current page fonts */ protected AFPPageFonts getPageFonts() { - if (getData().pageFonts == null) { - getData().pageFonts = new AFPPageFonts(); - } - return getData().pageFonts; + return pageState.getFonts(); } /** - * Sets the image uri of the current image being processed - * @param uri the image uri of the current image being processed - * @return true if the image uri has changed + * Increments and returns the page font count + * @return the page font count */ - public boolean setImageUri(String uri) { - if (!uri.equals(getData().imageUri)) { - getData().imageUri = uri; - return true; - } - return false; + public int incrementPageFontCount() { + return pageState.incrementFontCount(); } /** - * Returns the image uri of the current image being processed - * @return the image uri of the current image being processed + * Sets the page width + * @param pageWidth the page width */ - protected String getImageUri() { - return getData().imageUri; + public void setPageWidth(int pageWidth) { + pageState.setWidth(pageWidth); } - + /** - * Push the current state onto the stack. - * This call should be used when the q operator is used - * so that the state is known when popped. + * @return the page width */ - public void push() { - Data copy; - try { - copy = (Data)getData().clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e.getMessage()); - } - stateStack.add(copy); + public int getPageWidth() { + return pageState.getWidth(); } /** - * Get the current stack level. - * - * @return the current stack level + * Sets the page height + * @param pageHeight the page height */ - public int getStackLevel() { - return stateStack.size(); + public void setPageHeight(int pageHeight) { + pageState.setHeight(pageHeight); } /** - * 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 + * @return the page height */ - public Data pop() { - if (getStackLevel() > 0) { - Data popped = (Data)stateStack.remove(stateStack.size() - 1); - data = popped; - return popped; - } else { - return null; - } + public int getPageHeight() { + return pageState.getHeight(); } /** - * @return the currently valid state + * Sets the uri of the current image + * @param uri the uri of the current image */ - public Data getData() { - return data; + protected void setImageUri(String uri) { + ((AFPData)getData()).imageUri = uri; } - - /** the state data instance */ - public class Data implements Cloneable, Serializable { - private static final long serialVersionUID = -1789481244175275686L; - /** The current color */ - private Color color = null; + /** + * Gets the uri of the current image + * @return the uri of the current image + */ + public String getImageUri() { + return ((AFPData)getData()).imageUri; + } + + /** {@inheritDoc} */ + public String toString() { + return "AFPState{portraitRotation=" + portraitRotation + + ", landscapeRotation=" + landscapeRotation + + ", colorImages=" + colorImages + + ", bitsPerPixel=" + bitsPerPixel + + ", resolution=" + resolution + + ", pageState=" + pageState + + "}"; + } - /** The current background color */ - private Color backColor = null; + /** + * Page level state data + */ + private class AFPPageState { + /** The current page width */ + private int width = 0; - /** The current font name */ - private String fontName = null; + /** The current page height */ + private int height = 0; - /** The current font size */ - private int fontSize = 0; + /** The current page fonts */ + private AFPPageFonts fonts = new AFPPageFonts(); - /** The current line width */ - private float lineWidth = 0; + /** The current page font count */ + private int fontCount = 0; - /** The dash array for the current basic stroke (line type) */ - private float[] dashArray = null; + /** + * @return the page width + */ + protected int getWidth() { + return width; + } + + /** + * Sets the page width + * @param width the page width + */ + protected void setWidth(int width) { + this.width = width; + } + + /** + * @return the page height + */ + protected int getHeight() { + return height; + } + + /** + * Sets the page height + * @param height the page height + */ + protected void setHeight(int height) { + this.height = height; + } + + /** + * @return the page fonts + */ + protected AFPPageFonts getFonts() { + return fonts; + } + + /** + * Sets the current page fonts + * @param fonts the current page fonts + */ + protected void setFonts(AFPPageFonts fonts) { + this.fonts = fonts; + } + + /** + * @return increment and return the current page font count + */ + protected int incrementFontCount() { + return ++fontCount; + } + + /** {@inheritDoc} */ + public String toString() { + return "AFPPageState{width=" + width + + ", height=" + height + + ", fonts=" + fonts + + ", fontCount=" + fontCount + + "}"; + } + } + + /** + * Block level data + */ + private class AFPData extends org.apache.fop.render.AbstractState.AbstractData { + private static final long serialVersionUID = -1789481244175275686L; /** The current fill status */ private boolean filled = false; - /** The fonts on the current page */ - private AFPPageFonts pageFonts = null; - - /** The current image uri */ private String imageUri = null; /** {@inheritDoc} */ public Object clone() throws CloneNotSupportedException { - Data obj = new Data(); - obj.color = this.color; - obj.backColor = this.backColor; - obj.fontName = this.fontName; - obj.fontSize = this.fontSize; - obj.lineWidth = this.lineWidth; - obj.dashArray = this.dashArray; + AFPData obj = (AFPData)super.clone(); obj.filled = this.filled; - obj.pageFonts = this.pageFonts; obj.imageUri = this.imageUri; return obj; } - /** {@inheritDoc} */ + /** {@inheritDoc} */ public String toString() { - return "color=" + color - + ", backColor=" + backColor - + ", fontName=" + fontName - + ", fontSize=" + fontSize - + ", lineWidth=" + lineWidth - + ", dashArray=" + dashArray + return "AFPData{" + super.toString() + ", filled=" + filled - + ", pageFonts=" + pageFonts - + ", imageUri=" + imageUri; + + ", imageUri=" + imageUri + + "}"; } } } \ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/ObjectAreaInfo.java b/src/java/org/apache/fop/render/afp/ObjectAreaInfo.java index 49b799489..b6538b327 100644 --- a/src/java/org/apache/fop/render/afp/ObjectAreaInfo.java +++ b/src/java/org/apache/fop/render/afp/ObjectAreaInfo.java @@ -16,6 +16,7 @@ */ /* $Id$ */ + package org.apache.fop.render.afp; public class ObjectAreaInfo { @@ -144,4 +145,4 @@ public class ObjectAreaInfo { + ", heightRes=" + heightRes + ", rotation=" + rotation; } -} \ No newline at end of file +} diff --git a/src/java/org/apache/fop/render/afp/modca/AFPDataStream.java b/src/java/org/apache/fop/render/afp/modca/AFPDataStream.java index bad46dd7c..8c2c1b958 100644 --- a/src/java/org/apache/fop/render/afp/modca/AFPDataStream.java +++ b/src/java/org/apache/fop/render/afp/modca/AFPDataStream.java @@ -30,6 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.render.afp.AFPFontAttributes; +import org.apache.fop.render.afp.AFPTextDataInfo; import org.apache.fop.render.afp.DataObjectInfo; import org.apache.fop.render.afp.ObjectAreaInfo; import org.apache.fop.render.afp.ResourceInfo; @@ -121,20 +122,10 @@ public class AFPDataStream extends AbstractResourceGroupContainer { */ private int landscapeRotation = 270; - /** - * The x offset - */ - private int xOffset = 0; - - /** - * The y offset - */ - private int yOffset = 0; - /** * The rotation */ - private int rotation; + private int orientation; /** * The outputstream for the data stream @@ -276,7 +267,7 @@ public class AFPDataStream extends AbstractResourceGroupContainer { pageRotation, pageWidthRes, pageHeightRes); currentPage = currentPageObject; currentOverlay = null; - setOffsets(0, 0, 0); +// setOffsets(0, 0, 0); } /** @@ -315,7 +306,7 @@ public class AFPDataStream extends AbstractResourceGroupContainer { currentPageObject.createIncludePageOverlay(overlayName, x, y, 0); currentPage = currentOverlay; - setOffsets(0, 0, 0); +// setOffsets(0, 0, 0); } /** @@ -380,15 +371,24 @@ public class AFPDataStream extends AbstractResourceGroupContainer { * the offset in the x direction * @param yOff * the offset in the y direction - * @param rot + * @param orientation * the rotation + * @deprecated offsets are no longer used, use setOrientation() for setting the orientation */ - public void setOffsets(int xOff, int yOff, int rot) { - this.xOffset = xOff; - this.yOffset = yOff; - this.rotation = rot; + public void setOffsets(int xOff, int yOff, int orientation) { + setOrientation(orientation); } + /** + * Sets the orientation to be used for element positioning + * + * @param orientation + * the orientation used for element positioning + */ + public void setOrientation(int orientation) { + this.orientation = orientation; + } + /** * Creates the given page fonts in the current page * @@ -425,25 +425,12 @@ public class AFPDataStream extends AbstractResourceGroupContainer { * Helper method to create text on the current page, this method delegates * to the current presentation text object in order to construct the text. * - * @param fontReference - * the font reference used as the resource identifier - * @param x - * the x coordinate of the text - * @param y - * the y coordinate of the text - * @param col - * the text color - * @param vsci - * The variable space character increment. - * @param ica - * The inter character adjustment. - * @param data - * the text data to create + * @param textDataInfo + * the afp text data */ - public void createText(int fontReference, int x, int y, Color col, - int vsci, int ica, byte[] data) { - getCurrentPage().createText(fontReference, x + xOffset, y + yOffset, - rotation, col, vsci, ica, data); + public void createText(AFPTextDataInfo textDataInfo) { + textDataInfo.setOrientation(orientation); + getCurrentPage().createText(textDataInfo); } /** @@ -469,8 +456,8 @@ public class AFPDataStream extends AbstractResourceGroupContainer { //Update placement with current state ObjectAreaInfo areaInfo = dataObjectInfo.getObjectAreaInfo(); - areaInfo.setX(areaInfo.getX() + this.xOffset); - areaInfo.setY(areaInfo.getY() + this.yOffset); + areaInfo.setX(areaInfo.getX()); + areaInfo.setY(areaInfo.getY()); areaInfo.setRotation(this.rotation); Registry registry = Registry.getInstance(); @@ -593,8 +580,7 @@ public class AFPDataStream extends AbstractResourceGroupContainer { */ public void createLine(int x1, int y1, int x2, int y2, int thickness, Color col) { - getCurrentPage().createLine(x1 + xOffset, y1 + yOffset, x2 + xOffset, - y2 + yOffset, thickness, rotation, col); + getCurrentPage().createLine(x1, y1, x2, y2, thickness, orientation, col); } /** @@ -614,8 +600,7 @@ public class AFPDataStream extends AbstractResourceGroupContainer { * the shading color */ public void createShading(int x, int y, int w, int h, Color col) { - currentPageObject.createShading(x + xOffset, y + xOffset, w, h, - col.getRed(), col.getGreen(), col.getBlue()); + currentPageObject.createShading(x, y, w, h, col.getRed(), col.getGreen(), col.getBlue()); } /** @@ -626,7 +611,7 @@ public class AFPDataStream extends AbstractResourceGroupContainer { * the name of the static overlay */ public void createIncludePageOverlay(String name) { - currentPageObject.createIncludePageOverlay(name, 0, 0, rotation); + currentPageObject.createIncludePageOverlay(name, 0, 0, orientation); currentPageObject.getActiveEnvironmentGroup().createOverlay(name); } @@ -653,22 +638,22 @@ public class AFPDataStream extends AbstractResourceGroupContainer { public void createIncludePageSegment(String name, int x, int y) { int xOrigin; int yOrigin; - switch (rotation) { + switch (orientation) { case 90: - xOrigin = getCurrentPage().getWidth() - y - yOffset; - yOrigin = x + xOffset; + xOrigin = getCurrentPage().getWidth() - y; + yOrigin = x; break; case 180: - xOrigin = getCurrentPage().getWidth() - x - xOffset; - yOrigin = getCurrentPage().getHeight() - y - yOffset; + xOrigin = getCurrentPage().getWidth() - x; + yOrigin = getCurrentPage().getHeight() - y; break; case 270: - xOrigin = y + yOffset; - yOrigin = getCurrentPage().getHeight() - x - xOffset; + xOrigin = y; + yOrigin = getCurrentPage().getHeight() - x; break; default: - xOrigin = x + xOffset; - yOrigin = y + yOffset; + xOrigin = x; + yOrigin = y; break; } getCurrentPage().createIncludePageSegment(name, xOrigin, yOrigin); diff --git a/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java b/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java index 065886a49..c5b2f3ea2 100644 --- a/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java +++ b/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; +import org.apache.fop.render.afp.AFPTextDataInfo; import org.apache.fop.render.afp.fonts.AFPFont; /** @@ -189,27 +190,11 @@ public abstract class AbstractPageObject extends AbstractNamedAFPObject { * Helper method to create text on the current page, this method delegates * to the presentation text object in order to construct the text. * - * @param fontRef - * the font number used as the resource identifier - * @param x - * the x coordinate of the text data - * @param y - * the y coordinate of the text data - * @param textRotation - * the rotation of the text data - * @param col - * the text color - * @param vsci - * The variable space character increment. - * @param ica - * The inter character adjustment. - * @param data - * the text data to create + * @param textDataInfo + * the afp text data */ - public void createText(int fontRef, int x, int y, int textRotation, Color col, - int vsci, int ica, byte[] data) { - getPresentationTextObject().createTextData( - fontRef, x, y, textRotation, col, vsci, ica, data); + public void createText(AFPTextDataInfo textDataInfo) { + getPresentationTextObject().createTextData(textDataInfo); } /** diff --git a/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java b/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java index b6b367ab5..03e3f848d 100644 --- a/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java +++ b/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java @@ -24,6 +24,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import org.apache.fop.render.afp.AFPTextDataInfo; import org.apache.fop.render.afp.tools.BinaryUtils; /** @@ -33,18 +34,18 @@ import org.apache.fop.render.afp.tools.BinaryUtils; * that position them - modal control sequences that adjust the positions by * small amounts - other functions causing text to be presented with differences * in appearance. - * + * * The graphic characters are expected to conform to a coded font representation * so that they can be translated from the code point in the object data to the * character in the coded font. The units of measure for linear displacements * are derived from the PresentationTextDescriptor or from the hierarchical * defaults. - * + * * In addition to graphic character code points, Presentation Text data can * contain embedded control sequences. These are strings of two or more bytes * which signal an alternate mode of processing for the content of the current * Presentation Text data. - * + * */ public class PresentationTextData extends AbstractAFPObject { @@ -104,21 +105,20 @@ public class PresentationTextData extends AbstractAFPObject { * Constructor for the PresentationTextData, the boolean flag indicate * whether the control sequence prefix should be set to indicate the start * of a new control sequence. - * + * * @param controlInd * The control sequence indicator. */ public PresentationTextData(boolean controlInd) { - final byte[] data = { - 0x5A, // Structured field identifier - 0x00, // Record length byte 1 - 0x00, // Record length byte 2 - (byte) 0xD3, // PresentationTextData identifier byte 1 - (byte) 0xEE, // PresentationTextData identifier byte 2 - (byte) 0x9B, // PresentationTextData identifier byte 3 - 0x00, // Flag - 0x00, // Reserved - 0x00, // Reserved + final byte[] data = { 0x5A, // Structured field identifier + 0x00, // Record length byte 1 + 0x00, // Record length byte 2 + (byte) 0xD3, // PresentationTextData identifier byte 1 + (byte) 0xEE, // PresentationTextData identifier byte 2 + (byte) 0x9B, // PresentationTextData identifier byte 3 + 0x00, // Flag + 0x00, // Reserved + 0x00, // Reserved }; baos.write(data, 0, 9); @@ -131,7 +131,7 @@ public class PresentationTextData extends AbstractAFPObject { * The Set Coded Font Local control sequence activates a coded font and * specifies the character attributes to be used. This is a modal control * sequence. - * + * * @param font * The font local identifier. * @param afpdata @@ -152,13 +152,14 @@ public class PresentationTextData extends AbstractAFPObject { * Establishes the current presentation position on the baseline at a new * I-axis coordinate, which is a specified number of measurement units from * the B-axis. There is no change to the current B-axis coordinate. - * + * * @param coordinate * The coordinate for the inline move. * @param afpdata * The output stream to which data should be written. */ - private void absoluteMoveInline(int coordinate, ByteArrayOutputStream afpdata) { + private void absoluteMoveInline(int coordinate, + ByteArrayOutputStream afpdata) { byte[] b = BinaryUtils.convert(coordinate, 2); afpdata.write(new byte[] { 0x04, (byte) 0xC7, b[0], b[1], }, 0, 4); currentXCoordinate = coordinate; @@ -168,13 +169,14 @@ public class PresentationTextData extends AbstractAFPObject { * Establishes the baseline and the current presentation position at a new * B-axis coordinate, which is a specified number of measurement units from * the I-axis. There is no change to the current I-axis coordinate. - * + * * @param coordinate * The coordinate for the baseline move. * @param afpdata * The output stream to which data should be written. */ - private void absoluteMoveBaseline(int coordinate, ByteArrayOutputStream afpdata) { + private void absoluteMoveBaseline(int coordinate, + ByteArrayOutputStream afpdata) { byte[] b = BinaryUtils.convert(coordinate, 2); afpdata.write(new byte[] { 0x04, (byte) 0xD3, b[0], b[1], }, 0, 4); currentYCoordinate = coordinate; @@ -183,7 +185,7 @@ public class PresentationTextData extends AbstractAFPObject { /** * The Transparent Data control sequence contains a sequence of code points * that are presented without a scan for embedded control sequences. - * + * * @param data * The text data to add. * @param afpdata @@ -195,10 +197,10 @@ public class PresentationTextData extends AbstractAFPObject { if (l > 255) { // Check that we are not exceeding the maximum length throw new IllegalArgumentException( - "Transparent data is longer than 253 bytes: " + data); + "Transparent data is longer than 253 bytes: " + data); } afpdata.write(new byte[] { BinaryUtils.convert(l)[0], (byte) 0xDB, }, - 0, 2); + 0, 2); afpdata.write(data, 0, data.length); } @@ -206,7 +208,7 @@ public class PresentationTextData extends AbstractAFPObject { * Draws a line of specified length and specified width in the B-direction * from the current presentation position. The location of the current * presentation position is unchanged. - * + * * @param length * The length of the rule. * @param width @@ -214,9 +216,10 @@ public class PresentationTextData extends AbstractAFPObject { * @param afpdata * The output stream to which data should be written. */ - private void drawBaxisRule(int length, int width, ByteArrayOutputStream afpdata) { + private void drawBaxisRule(int length, int width, + ByteArrayOutputStream afpdata) { afpdata.write(new byte[] { 0x07, // Length - (byte) 0xE7, // Type + (byte) 0xE7, // Type }, 0, 2); // Rule length byte[] data1 = BinaryUtils.shortToByteArray((short) length); @@ -232,7 +235,7 @@ public class PresentationTextData extends AbstractAFPObject { * Draws a line of specified length and specified width in the I-direction * from the current presentation position. The location of the current * presentation position is unchanged. - * + * * @param length * The length of the rule. * @param width @@ -240,9 +243,10 @@ public class PresentationTextData extends AbstractAFPObject { * @param afpdata * The output stream to which data should be written. */ - private void drawIaxisRule(int length, int width, ByteArrayOutputStream afpdata) { + private void drawIaxisRule(int length, int width, + ByteArrayOutputStream afpdata) { afpdata.write(new byte[] { 0x07, // Length - (byte) 0xE5, // Type + (byte) 0xE5, // Type }, 0, 2); // Rule length byte[] data1 = BinaryUtils.shortToByteArray((short) length); @@ -256,73 +260,66 @@ public class PresentationTextData extends AbstractAFPObject { /** * Create the presentation text data for the byte array of data. - * - * @param fontNumber - * The font resource identifier. - * @param x - * The x coordinate for the text data. - * @param y - * The y coordinate for the text data. - * @param orientation - * The orientation of the text data. - * @param col - * The text color. - * @param vsci - * The variable space character increment. - * @param ica - * The inter character adjustment. - * @param data - * The text data to be created. + * + * @param textDataInfo + * the afp text data * @throws MaximumSizeExceededException + * thrown if the maximum number of text data is exceeded */ - public void createTextData(int fontNumber, int x, int y, int orientation, - Color col, int vsci, int ica, byte[] data) throws MaximumSizeExceededException { + public void createTextData(AFPTextDataInfo textDataInfo) + throws MaximumSizeExceededException { ByteArrayOutputStream afpdata = new ByteArrayOutputStream(); - if (currentOrientation != orientation) { - setTextOrientation(orientation, afpdata); - currentOrientation = orientation; + if (currentOrientation != textDataInfo.getOrientation()) { + setTextOrientation(textDataInfo.getOrientation(), afpdata); + currentOrientation = textDataInfo.getOrientation(); currentXCoordinate = -1; currentYCoordinate = -1; } // Avoid unnecessary specification of the Y co-ordinate - if (y != currentYCoordinate) { - absoluteMoveBaseline(y, afpdata); + if (textDataInfo.getY() != currentYCoordinate) { + absoluteMoveBaseline(textDataInfo.getY(), afpdata); currentXCoordinate = -1; } // Avoid unnecessary specification of the X co-ordinate - if (x != currentXCoordinate) { - absoluteMoveInline(x, afpdata); + if (textDataInfo.getX() != currentXCoordinate) { + absoluteMoveInline(textDataInfo.getX(), afpdata); } // Avoid unnecessary specification of the variable space increment - if (vsci != currentVariableSpaceCharacterIncrement) { - setVariableSpaceCharacterIncrement(vsci, afpdata); - currentVariableSpaceCharacterIncrement = vsci; + if (textDataInfo.getVariableSpaceCharacterIncrement() + != currentVariableSpaceCharacterIncrement) { + setVariableSpaceCharacterIncrement(textDataInfo + .getVariableSpaceCharacterIncrement(), afpdata); + currentVariableSpaceCharacterIncrement = textDataInfo + .getVariableSpaceCharacterIncrement(); } // Avoid unnecessary specification of the inter character adjustment - if (ica != currentInterCharacterAdjustment) { - setInterCharacterAdjustment(ica, afpdata); - currentInterCharacterAdjustment = ica; + if (textDataInfo.getInterCharacterAdjustment() != currentInterCharacterAdjustment) { + setInterCharacterAdjustment(textDataInfo.getInterCharacterAdjustment(), + afpdata); + currentInterCharacterAdjustment = textDataInfo + .getInterCharacterAdjustment(); } // Avoid unnecessary specification of the text color - if (!col.equals(currentColor)) { - setExtendedTextColor(col, afpdata); - currentColor = col; + if (!textDataInfo.getColor().equals(currentColor)) { + setExtendedTextColor(textDataInfo.getColor(), afpdata); + currentColor = textDataInfo.getColor(); } - setCodedFont(BinaryUtils.convert(fontNumber)[0], afpdata); - addTransparentData(data, afpdata); + setCodedFont(BinaryUtils.convert(textDataInfo.getFontReference())[0], + afpdata); + addTransparentData(textDataInfo.getData(), afpdata); currentXCoordinate = -1; - int s = afpdata.size(); + int dataSize = afpdata.size(); - if (baos.size() + s > MAX_SIZE) { + if (baos.size() + dataSize > MAX_SIZE) { currentXCoordinate = -1; currentYCoordinate = -1; throw new MaximumSizeExceededException(); @@ -335,7 +332,7 @@ public class PresentationTextData extends AbstractAFPObject { /** * Drawing of lines using the starting and ending coordinates, thickness and * colour arguments. - * + * * @param x1 * The starting X coordinate. * @param y1 @@ -350,9 +347,11 @@ public class PresentationTextData extends AbstractAFPObject { * The orientation of the text data. * @param col * The text color. + * @throws MaximumSizeExceededException + * thrown if the maximum number of line data has been exceeded */ public void createLineData(int x1, int y1, int x2, int y2, int thickness, - int orientation, Color col) throws MaximumSizeExceededException { + int orientation, Color col) throws MaximumSizeExceededException { ByteArrayOutputStream afpdata = new ByteArrayOutputStream(); @@ -399,105 +398,103 @@ public class PresentationTextData extends AbstractAFPObject { /** * The Set Text Orientation control sequence establishes the I-direction and * B-direction for the subsequent text. This is a modal control sequence. - * + * * Semantics: This control sequence specifies the I-axis and B-axis * orientations with respect to the Xp-axis for the current Presentation * Text object. The orientations are rotational values expressed in degrees * and minutes. - * + * * @param orientation - * The text orientation (0,90, 180, 270). + * The text orientation (0, 90, 180, 270). * @param afpdata * The output stream to which data should be written. */ - private void setTextOrientation(int orientation, ByteArrayOutputStream afpdata) { - afpdata.write(new byte[] { 0x06, (byte) 0xF7, }, 0, 2); + private void setTextOrientation(int orientation, + ByteArrayOutputStream afpdata) { + afpdata.write(new byte[] {0x06, (byte) 0xF7, }, 0, 2); switch (orientation) { - case 90: - afpdata.write(0x2D); - afpdata.write(0x00); - afpdata.write(0x5A); - afpdata.write(0x00); - break; - case 180: - afpdata.write(0x5A); - afpdata.write(0x00); - afpdata.write(0x87); - afpdata.write(0x00); - break; - case 270: - afpdata.write(0x87); - afpdata.write(0x00); - afpdata.write(0x00); - afpdata.write(0x00); - break; - default: - afpdata.write(0x00); - afpdata.write(0x00); - afpdata.write(0x2D); - afpdata.write(0x00); - break; + case 90: + afpdata.write(0x2D); + afpdata.write(0x00); + afpdata.write(0x5A); + afpdata.write(0x00); + break; + case 180: + afpdata.write(0x5A); + afpdata.write(0x00); + afpdata.write(0x87); + afpdata.write(0x00); + break; + case 270: + afpdata.write(0x87); + afpdata.write(0x00); + afpdata.write(0x00); + afpdata.write(0x00); + break; + default: + afpdata.write(0x00); + afpdata.write(0x00); + afpdata.write(0x2D); + afpdata.write(0x00); + break; } } /** * The Set Extended Text Color control sequence specifies a color value and - * defines the color space and encoding for that value. The specified color - * value is applied to foreground areas of the text presentation space. - * This is a modal control sequence. - * + * defines the color space and encoding for that value. The specified color + * value is applied to foreground areas of the text presentation space. This + * is a modal control sequence. + * * @param col * The color to be set. * @param afpdata * The output stream to which data should be written. */ - private void setExtendedTextColor(Color col, - ByteArrayOutputStream afpdata) { - - afpdata.write(new byte[] { - 15 // Control sequence length - , (byte)0x81 // Control sequence function type - , 0x00 // Reserved; must be zero - , 0x01 // Color space - 0x01 = RGB - , 0x00 // Reserved; must be zero - , 0x00 // Reserved; must be zero - , 0x00 // Reserved; must be zero - , 0x00 // Reserved; must be zero - , 8 // Number of bits in component 1 - , 8 // Number of bits in component 2 - , 8 // Number of bits in component 3 - , 0 // Number of bits in component 4 - , (byte)(col.getRed()) // Red intensity - , (byte)(col.getGreen()) // Green intensity - , (byte)(col.getBlue()) // Blue intensity - }, 0, 15); - + private void setExtendedTextColor(Color col, ByteArrayOutputStream afpdata) { + byte[] colorData = new byte[] { + 15, // Control sequence length + (byte) 0x81, // Control sequence function type + 0x00, // Reserved; must be zero + 0x01, // Color space - 0x01 = RGB + 0x00, // Reserved; must be zero + 0x00, // Reserved; must be zero + 0x00, // Reserved; must be zero + 0x00, // Reserved; must be zero + 8, // Number of bits in component 1 + 8, // Number of bits in component 2 + 8, // Number of bits in component 3 + 0, // Number of bits in component 4 + (byte) (col.getRed()), // Red intensity + (byte) (col.getGreen()), // Green intensity + (byte) (col.getBlue()), // Blue intensity + }; + + afpdata.write(colorData, 0, colorData.length); } /** - * //TODO - * This is a modal control sequence. - * + * //TODO This is a modal control sequence. + * * @param incr * The increment to be set. * @param afpdata * The output stream to which data should be written. */ - private void setVariableSpaceCharacterIncrement(int incr, ByteArrayOutputStream afpdata) { + private void setVariableSpaceCharacterIncrement(int incr, + ByteArrayOutputStream afpdata) { byte[] b = BinaryUtils.convert(incr, 2); afpdata.write(new byte[] { - 4 // Control sequence length - , (byte)0xC5 // Control sequence function type - , b[0] - , b[1] - }, 0, 4); + 4, // Control sequence length + (byte) 0xC5, // Control sequence function type + b[0], b[1] }, + 0, 4); } /** - * //TODO - * This is a modal control sequence. - * + * //TODO This is a modal control sequence. + * * @param incr * The increment to be set. * @param afpdata @@ -506,12 +503,10 @@ public class PresentationTextData extends AbstractAFPObject { private void setInterCharacterAdjustment(int incr, ByteArrayOutputStream afpdata) { byte[] b = BinaryUtils.convert(Math.abs(incr), 2); afpdata.write(new byte[] { - 5 // Control sequence length - , (byte)0xC3 // Control sequence function type - , b[0] - , b[1] - , (byte)(incr >= 0 ? 0 : 1) // Direction - }, 0, 5); + 5, // Control sequence length + (byte) 0xC3, // Control sequence function type + b[0], b[1], (byte) (incr >= 0 ? 0 : 1) // Direction + }, 0, 5); } /** @@ -531,8 +526,9 @@ public class PresentationTextData extends AbstractAFPObject { * and zero or more parameters. The control sequence can extend multiple * presentation text data objects, but must eventually be terminated. This * method terminates the control sequence. - * + * * @throws MaximumSizeExceededException + * thrown in the event that maximum size has been exceeded */ public void endControlSequence() throws MaximumSizeExceededException { byte[] data = new byte[2]; diff --git a/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java b/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java index 3dda2006a..290a1e46a 100644 --- a/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java +++ b/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; +import org.apache.fop.render.afp.AFPTextDataInfo; + /** * The Presentation Text object is the data object used in document processing * environments for representing text which has been prepared for presentation. @@ -76,58 +78,18 @@ public class PresentationTextObject extends AbstractNamedAFPObject { /** * Create the presentation text data for the byte array of data. * - * @param fontNum - * The font resource identifier. - * @param x - * The x coordinate for the text data. - * @param y - * The y coordinate for the text data. - * @param col - * The text color. - * @param vsci - * The variable space character increment. - * @param ica - * The inter character increment. - * @param data - * The text data to be created. - */ - public void createTextData(int fontNum, int x, int y, Color col, - int vsci, int ica, byte[] data) { - // Use a default orientation of zero - createTextData(fontNum, x, y, 0, col, vsci, ica, data); - } - - /** - * Create the presentation text data for the byte array of data. - * - * @param fontRef - * The font resource identifier. - * @param x - * The x coordinate for the text data. - * @param y - * The y coordinate for the text data. - * @param orientation - * The orientation of the text data. - * @param col - * The text color. - * @param vsci - * The variable space character increment. - * @param ica - * The inter character adjustment. - * @param data - * The text data to be created. + * @param textDataInfo + * The afp text data */ - public void createTextData(int fontRef, int x, int y, int orientation, - Color col, int vsci, int ica, byte[] data) { + public void createTextData(AFPTextDataInfo textDataInfo) { if (currentPresentationTextData == null) { startPresentationTextData(); } try { - currentPresentationTextData.createTextData(fontRef, x, y, - orientation, col, vsci, ica, data); + currentPresentationTextData.createTextData(textDataInfo); } catch (MaximumSizeExceededException msee) { endPresentationTextData(); - createTextData(fontRef, x, y, orientation, col, vsci, ica, data); + createTextData(textDataInfo); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index ff0e64806..c3e4a9657 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -112,6 +112,7 @@ import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.AbstractPathOrientedRenderer; +import org.apache.fop.render.AbstractState; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.RendererContext; import org.apache.fop.util.CharUtilities; @@ -1094,7 +1095,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { */ protected List breakOutOfStateStack() { List breakOutList = new java.util.ArrayList(); - PDFState.Data data; + AbstractState.AbstractData data; while (true) { data = currentState.getData(); if (currentState.pop() == null) { @@ -1115,10 +1116,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { */ protected void restoreStateStackAfterBreakOut(List breakOutList) { comment("------ restoring context after break-out..."); - PDFState.Data data; + AbstractState.AbstractData data; Iterator i = breakOutList.iterator(); while (i.hasNext()) { - data = (PDFState.Data)i.next(); + data = (AbstractState.AbstractData)i.next(); saveGraphicsState(); AffineTransform at = data.getTransform(); concatenateTransformationMatrix(at);