</note>
</section>
</section>
+ <section id="transform">
+ <title>Free-form Transformation for fo:block-container</title>
+ <p>
+ For <code>fo:block-container</code> elements whose <code>absolute-position</code> set to
+ "absolute" or "fixed" you can use the extension attribute <code>fox:transform</code>
+ to apply a free-form transformation to the whole block-container. The content of the
+ <code>fox:transform</code> attribute is the same as for
+ <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">SVG's transform attribute</a>.
+ The transformation specified here is performed in addition to other implicit
+ transformations of the block-container (resulting from top, left and other properties)
+ and after them.
+ </p>
+ <p>
+ An example: <code>fox:transform="rotate(45)"</code> would rotate the block-container
+ by 45 degrees clock-wise around its upper-left corner.
+ </p>
+ <note>
+ This extension attribute doesn't work for all output formats! It's currently only
+ supported for PDF, PS and Java2D-based renderers.
+ </note>
+ </section>
</section>
</body>
</document>
package org.apache.fop.area;
import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
this.f = ctm.f;
}
+ /**
+ * Initialize a CTM with the values of an AffineTransform.
+ *
+ * @param at the transformation matrix
+ */
+ public CTM(AffineTransform at) {
+ double[] matrix = new double[6];
+ at.getMatrix(matrix);
+ this.a = matrix[0];
+ this.b = matrix[1];
+ this.c = matrix[2];
+ this.d = matrix[3];
+ this.e = matrix[4];
+ this.f = matrix[5];
+ }
+
/**
* Return a CTM which will transform coordinates for a particular writing-mode
* into normalized first quandrant coordinates.
return new double[]{a, b, c, d, e, f};
}
+ /**
+ * Returns this CTM as an AffineTransform object.
+ * @return the AffineTransform representation
+ */
+ public AffineTransform toAffineTransform() {
+ return new AffineTransform(toArray());
+ }
+
/**
* Construct a coordinate transformation matrix (CTM).
* @param absRefOrient absolute reference orientation
package org.apache.fop.layoutmgr;
+import java.awt.Point;
+import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
-import java.awt.Point;
-import java.awt.geom.Rectangle2D;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.area.Area;
-import org.apache.fop.area.BlockViewport;
import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.CTM;
import org.apache.fop.area.Trait;
+import org.apache.fop.datatypes.FODimension;
+import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
-import org.apache.fop.area.CTM;
-import org.apache.fop.datatypes.FODimension;
-import org.apache.fop.datatypes.Length;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
vpContentBPD = allocBPD - getBPIndents();
setContentAreaIPD(allocIPD - getIPIndents());
- double contentRectOffsetX = offset.getX();
- contentRectOffsetX += getBlockContainerFO()
- .getCommonMarginBlock().startIndent.getValue(this);
- double contentRectOffsetY = offset.getY();
- contentRectOffsetY += getSpaceBefore(); //TODO Uhm, is that necessary?
- contentRectOffsetY += getBlockContainerFO()
- .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
- contentRectOffsetY += getBlockContainerFO()
- .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
+ double contentRectOffsetX = 0;
+ double contentRectOffsetY = 0;
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
} else {
viewportBlockArea.setBPD(getContentAreaBPD());
}
-
+ transferForeignAttributes(viewportBlockArea);
+
TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
TraitSetter.addBorders(viewportBlockArea,
getBlockContainerFO().getCommonBorderPaddingBackground(),
import java.awt.Color;
import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
+import org.apache.batik.parser.AWTTransformProducer;
+
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.fop.area.Area;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.traits.BorderProps;
+import org.apache.fop.util.QName;
/**
* Abstract base class for renderers like PDF and PostScript where many painting operations
}
+ private final QName FOX_TRANSFORM = new QName(ExtensionElementMapping.URI, "fox:transform");
+
/** {@inheritDoc} */
protected void renderBlockViewport(BlockViewport bv, List children) {
// clip and position viewport if necessary
// save positions
int saveIP = currentIPPosition;
int saveBP = currentBPPosition;
- //String saveFontName = currentFontName;
CTM ctm = bv.getCTM();
int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
- float x, y;
- x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
- y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
//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) {
- currentIPPosition = bv.getXOffset();
- currentBPPosition = bv.getYOffset();
-
//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.
breakOutList = breakOutOfStateStack();
}
- CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
- ctm = tempctm.multiply(ctm);
-
- //Adjust for spaces (from margin or indirectly by start-indent etc.
- x += bv.getSpaceStart() / 1000f;
- currentIPPosition += bv.getSpaceStart();
+ AffineTransform positionTransform = new AffineTransform();
+ positionTransform.translate(bv.getXOffset(), bv.getYOffset());
- y += bv.getSpaceBefore() / 1000f;
- currentBPPosition += bv.getSpaceBefore();
+ //"left/"top" (bv.getX/YOffset()) specify the position of the content rectangle
+ positionTransform.translate(-borderPaddingStart, -borderPaddingBefore);
+
+ //Free transformation for the block-container viewport
+ String transf;
+ transf = bv.getForeignAttributeValue(FOX_TRANSFORM);
+ if (transf != null) {
+ AffineTransform freeTransform = AWTTransformProducer.createAffineTransform(transf);
+ positionTransform.concatenate(freeTransform);
+ }
+ saveGraphicsState();
+ //Viewport position
+ concatenateTransformationMatrix(mptToPt(positionTransform));
+
+ //Background and borders
float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
+ drawBackAndBorders(bv, 0, 0, width + bpwidth, height + bpheight);
- drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
-
- //Now adjust for border/padding
- currentIPPosition += borderPaddingStart;
- currentBPPosition += borderPaddingBefore;
+ //Shift to content rectangle after border painting
+ AffineTransform contentRectTransform = new AffineTransform();
+ contentRectTransform.translate(borderPaddingStart, borderPaddingBefore);
+ concatenateTransformationMatrix(mptToPt(contentRectTransform));
- Rectangle2D clippingRect = null;
+ //Clipping
if (bv.getClip()) {
- clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
- bv.getIPD(), bv.getBPD());
+ clipRect(0f, 0f, width, height);
}
- startVParea(ctm, clippingRect);
+ saveGraphicsState();
+ //Set up coordinate system for content rectangle
+ AffineTransform contentTransform = ctm.toAffineTransform();
+ concatenateTransformationMatrix(mptToPt(contentTransform));
+
currentIPPosition = 0;
currentBPPosition = 0;
renderBlocks(bv, children);
- endVParea();
+ restoreGraphicsState();
+ restoreGraphicsState();
+
if (breakOutList != null) {
restoreStateStackAfterBreakOut(breakOutList);
}
currentBPPosition += (int)(bv.getAllocBPD());
}
- //currentFontName = saveFontName;
}
+ /**
+ * Concatenates the current transformation matrix with the given one, therefore establishing
+ * a new coordinate system.
+ * @param at the transformation matrix to process (coordinates in points)
+ */
+ protected abstract void concatenateTransformationMatrix(AffineTransform at);
+
/**
* Render an inline viewport.
* This renders an inline viewport by clipping if necessary.
/**
* Clip using a rectangular area.
- * @param x the x coordinate
- * @param y the y coordinate
- * @param width the width of the rectangle
- * @param height the height of the rectangle
+ * @param x the x coordinate (in points)
+ * @param y the y coordinate (in points)
+ * @param width the width of the rectangle (in points)
+ * @param height the height of the rectangle (in points)
*/
protected abstract void clipRect(float x, float y, float width, float height);
// Java
import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
public String getMimeType() {
return null;
}
+
+ /**
+ * Converts a millipoint-based transformation matrix to points.
+ * @param at a millipoint-based transformation matrix
+ * @return a point-based transformation matrix
+ */
+ protected AffineTransform mptToPt(AffineTransform at) {
+ double[] matrix = new double[6];
+ at.getMatrix(matrix);
+ //Convert to points
+ matrix[4] = matrix[4] / 1000;
+ matrix[5] = matrix[5] / 1000;
+ return new AffineTransform(matrix);
+ }
+
+ /**
+ * Converts a point-based transformation matrix to millipoints.
+ * @param at a point-based transformation matrix
+ * @return a millipoint-based transformation matrix
+ */
+ protected AffineTransform ptToMpt(AffineTransform at) {
+ double[] matrix = new double[6];
+ at.getMatrix(matrix);
+ //Convert to millipoints
+ matrix[4] = matrix[4] * 1000;
+ matrix[5] = matrix[5] * 1000;
+ return new AffineTransform(matrix);
+ }
}
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.io.FileNotFoundException;
// currentFontName = saveFontName;
}
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ //Not used here since AFPRenderer defines its own renderBlockViewport() method.
+ throw new UnsupportedOperationException("NYI");
+ }
+
/**
* {@inheritDoc}
*/
state = (Java2DGraphicsState)stateStack.pop();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ state.transform(at);
+ }
+
+ /** {@inheritDoc} */
protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
saveGraphicsState();
import org.apache.fop.area.BlockViewport;
import org.apache.fop.area.CTM;
import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.RegionViewport;
import org.apache.fop.area.Trait;
import org.apache.fop.area.inline.AbstractTextArea;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.util.QName;
import org.apache.fop.util.UnitConv;
+/* Note:
+ * There are some commonalities with AbstractPathOrientedRenderer but it's not possible
+ * to derive from it due to PCL's restrictions. We may need an additional common subclass to
+ * avoid methods copied from AbstractPathOrientedRenderer. Or we wait until after the IF redesign.
+ */
+
/**
* Renderer for the PCL 5 printer language. It also uses HP GL/2 for certain graphic elements.
*/
drawBackAndBorders(block, startx, starty, width, height);
}
+ /**
+ * {@inheritDoc}
+ * @todo Copied from AbstractPathOrientedRenderer
+ */
+ protected void handleRegionTraits(RegionViewport region) {
+ Rectangle2D viewArea = region.getViewArea();
+ float startx = (float)(viewArea.getX() / 1000f);
+ float starty = (float)(viewArea.getY() / 1000f);
+ float width = (float)(viewArea.getWidth() / 1000f);
+ float height = (float)(viewArea.getHeight() / 1000f);
+
+ if (region.getRegionReference().getRegionClass() == FO_REGION_BODY) {
+ currentBPPosition = region.getBorderAndPaddingWidthBefore();
+ currentIPPosition = region.getBorderAndPaddingWidthStart();
+ }
+ drawBackAndBorders(region, startx, starty, width, height);
+ }
+
/**
* {@inheritDoc}
*/
// save positions
int saveIP = currentIPPosition;
int saveBP = currentBPPosition;
- //String saveFontName = currentFontName;
CTM ctm = bv.getCTM();
int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
- float x, y;
- x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
- y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
//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) {
- currentIPPosition = bv.getXOffset();
- currentBPPosition = bv.getYOffset();
-
//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.
breakOutList = breakOutOfStateStack();
}
- CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
- ctm = tempctm.multiply(ctm);
-
- //Adjust for spaces (from margin or indirectly by start-indent etc.
- x += bv.getSpaceStart() / 1000f;
- currentIPPosition += bv.getSpaceStart();
+ AffineTransform positionTransform = new AffineTransform();
+ positionTransform.translate(bv.getXOffset(), bv.getYOffset());
- y += bv.getSpaceBefore() / 1000f;
- currentBPPosition += bv.getSpaceBefore();
+ //"left/"top" (bv.getX/YOffset()) specify the position of the content rectangle
+ positionTransform.translate(-borderPaddingStart, -borderPaddingBefore);
+ saveGraphicsState();
+ //Viewport position
+ concatenateTransformationMatrix(mptToPt(positionTransform));
+
+ //Background and borders
float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
+ drawBackAndBorders(bv, 0, 0, width + bpwidth, height + bpheight);
- drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
-
- //Now adjust for border/padding
- currentIPPosition += borderPaddingStart;
- currentBPPosition += borderPaddingBefore;
+ //Shift to content rectangle after border painting
+ AffineTransform contentRectTransform = new AffineTransform();
+ contentRectTransform.translate(borderPaddingStart, borderPaddingBefore);
+ concatenateTransformationMatrix(mptToPt(contentRectTransform));
- Rectangle2D clippingRect = null;
+ //Clipping
if (bv.getClip()) {
- clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
- bv.getIPD(), bv.getBPD());
+ clipRect(0f, 0f, width, height);
}
- startVParea(ctm, clippingRect);
+ saveGraphicsState();
+ //Set up coordinate system for content rectangle
+ AffineTransform contentTransform = ctm.toAffineTransform();
+ concatenateTransformationMatrix(mptToPt(contentTransform));
+
currentIPPosition = 0;
currentBPPosition = 0;
renderBlocks(bv, children);
- endVParea();
+
+ restoreGraphicsState();
+ restoreGraphicsState();
if (breakOutList != null) {
restoreStateStackAfterBreakOut(breakOutList);
//currentFontName = saveFontName;
}
+ /**
+ * Concatenates the current transformation matrix with the given one, therefore establishing
+ * a new coordinate system.
+ * @param at the transformation matrix to process (coordinates in points)
+ */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ if (!at.isIdentity()) {
+ graphicContext.transform(ptToMpt(at));
+ changePrintDirection();
+ }
+ }
+
private List breakOutOfStateStack() {
log.debug("Block.FIXED --> break out");
List breakOutList = new java.util.ArrayList();
final BorderProps bpsStart, final BorderProps bpsEnd) {
Graphics2DAdapter g2a = getGraphics2DAdapter();
final Rectangle.Float effBorderRect = new Rectangle2D.Float(
- borderRect.x - (currentIPPosition / 1000f),
- borderRect.y - (currentBPPosition / 1000f),
+ 0,
+ 0,
borderRect.width,
borderRect.height);
final Rectangle paintRect = new Rectangle(
}
}
- /** Saves the graphics state of the rendering engine. */
+ /** {@inheritDoc} */
protected void saveGraphicsState() {
endTextObject();
+ currentState.push();
currentStream.add("q\n");
}
- /** Restores the last graphics state of the rendering engine. */
- protected void restoreGraphicsState() {
+ private void restoreGraphicsState(boolean popState) {
endTextObject();
currentStream.add("Q\n");
+ if (popState) {
+ currentState.pop();
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void restoreGraphicsState() {
+ restoreGraphicsState(true);
}
/** Indicates the beginning of a text object. */
this.pdfDoc.output(ostream);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
+ saveGraphicsState();
+ //currentState.push();
// Set the given CTM in the graphics state
- currentState.push();
currentState.concatenate(
new AffineTransform(CTMHelper.toPDFArray(ctm)));
- saveGraphicsState();
if (clippingRect != null) {
clipRect((float)clippingRect.getX() / 1000f,
(float)clippingRect.getY() / 1000f,
currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void endVParea() {
restoreGraphicsState();
- currentState.pop();
+ //currentState.pop();
}
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ System.out.println(at);
+ if (!at.isIdentity()) {
+ currentState.concatenate(at);
+ currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
+ }
+ }
+
/**
* Handle the traits for a region
* This is used to draw the traits for the given page region.
}
}
- /**
- * Clip a rectangular area.
- * write a clipping operation given coordinates in the current
- * transform.
- * @param x the x coordinate
- * @param y the y coordinate
- * @param width the width of the area
- * @param height the height of the area
- */
+ /** {@inheritDoc} */
protected void clipRect(float x, float y, float width, float height) {
currentStream.add(format(x) + " " + format(y) + " "
+ format(width) + " " + format(height) + " re ");
comment("------ break out!");
}
breakOutList.add(0, data); //Insert because of stack-popping
- restoreGraphicsState();
+ restoreGraphicsState(false);
}
return breakOutList;
}
* @param breakOutList the state stack to restore.
*/
protected void restoreStateStackAfterBreakOut(List breakOutList) {
- CTM tempctm;
comment("------ restoring context after break-out...");
PDFState.Data data;
Iterator i = breakOutList.iterator();
- double[] matrix = new double[6];
while (i.hasNext()) {
data = (PDFState.Data)i.next();
- currentState.push();
saveGraphicsState();
AffineTransform at = data.getTransform();
- if (!at.isIdentity()) {
- currentState.concatenate(at);
- at.getMatrix(matrix);
- tempctm = new CTM(matrix[0], matrix[1], matrix[2], matrix[3],
- matrix[4] * 1000, matrix[5] * 1000);
- currentStream.add(CTMHelper.toPDFString(tempctm) + " cm\n");
- }
+ concatenateTransformationMatrix(at);
//TODO Break-out: Also restore items such as line width and color
//Left out for now because all this painting stuff is very
//inconsistent. Some values go over PDFState, some don't.
// Java
import java.awt.Color;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.io.File;
writeln("clip newpath");
}
- /**
- * Clip an area.
- * Write a clipping operation given coordinates in the current
- * transform.
- * @param x the x coordinate
- * @param y the y coordinate
- * @param width the width of the area
- * @param height the height of the area
- */
+ /** {@inheritDoc} */
protected void clipRect(float x, float y, float width, float height) {
try {
gen.defineRect(x, y, width, height);
/** Restores the last graphics state of the rendering engine. */
public void restoreGraphicsState() {
try {
+ endTextObject();
//delegate
gen.restoreGraphicsState();
} catch (IOException ioe) {
}
}
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ try {
+ gen.concatMatrix(at);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
+ }
+
private String getPostScriptNameForFontKey(String key) {
Map fonts = fontInfo.getFonts();
Typeface tf = (Typeface)fonts.get(key);
/** Indicates the end of a text object. */
protected void endTextObject() {
if (inTextMode) {
+ inTextMode = false; //set before restoreGraphicsState() to avoid recursion
writeln("ET");
restoreGraphicsState();
- inTextMode = false;
}
}
* {@inheritDoc}
*/
protected void endVParea() {
- endTextObject();
restoreGraphicsState();
}
import java.awt.Color;
import java.awt.Point;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
* {@inheritDoc}
*/
protected void saveGraphicsState() {
+ currentState.push(new CTM());
}
/**
* {@inheritDoc}
*/
protected void restoreGraphicsState() {
+ currentState.pop();
}
/**
protected void endVParea() {
currentState.pop();
}
+
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ currentState.push(new CTM(ptToMpt(at)));
+ }
+
}
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="fix">
+ Compliance fix: for absolutely positioned block-containers, "top" wasn't
+ interpreted correctly.
+ </action>
+ <action context="Code" dev="JM" type="add">
+ New extension attribute fox:transform on fo:block-container allows free-form transformation
+ (rotation, scaling etc.) of absolute and fixed block-containers. Supported only
+ for PDF, PS and Java2D-based renderers.
+ </action>
+ <action context="Code" dev="JM" type="fix">
+ Fixed logic error setting the transformation matrix for block-container viewports
+ (applies to absolute and fixed block-containers only).
+ Important: External renderer implementations need to adjust for the change and implement
+ the new method concatenateTransformationMatrix(AffineTransform) if the renderer is
+ derived from AbstractPathOrientedRenderer.
+ </action>
<action context="Code" dev="JM" importance="high" type="fix">
A new image loading framework has been introduced to fix various problems with external
graphics and improve performance.
<checks>
<!-- first block-container -->
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@is-viewport-area"/>
- <eval expected="[1.0 0.0 0.0 1.0 10000.0 10000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ctm"/>
+ <eval expected="[1.0 0.0 0.0 1.0 0.0 0.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ctm"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ipd"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ipda"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@bpd"/>
<!-- second block-container -->
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@is-viewport-area"/>
- <eval expected="[0.0 -1.0 1.0 0.0 190000.0 160000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ctm"/>
+ <eval expected="[0.0 -1.0 1.0 0.0 0.0 150000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ctm"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ipd"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ipda"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@bpd"/>
<!-- third block-container -->
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@is-viewport-area"/>
- <eval expected="[-1.0 -0.0 0.0 -1.0 160000.0 290000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@ctm"/>
+ <eval expected="[-1.0 -0.0 0.0 -1.0 150000.0 100000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@ctm"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@ipd"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@ipda"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[3]/@bpd"/>
<!-- fourth block-container -->
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@is-viewport-area"/>
- <eval expected="[0.0 1.0 -1.0 0.0 290000.0 190000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@ctm"/>
+ <eval expected="[0.0 1.0 -1.0 0.0 100000.0 0.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@ctm"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@ipd"/>
<eval expected="100000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@ipda"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[4]/@bpd"/>
<!-- fifth block-container -->
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@is-viewport-area"/>
- <eval expected="[1.0 0.0 0.0 1.0 30000.0 150000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@ctm"/>
+ <eval expected="[1.0 0.0 0.0 1.0 0.0 0.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@ctm"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@ipd"/>
<eval expected="150000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@ipda"/>
<eval expected="28800" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[5]/@bpd"/>
<true xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@is-viewport-area"/>
<eval expected="fixed" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@positioning"/>
<eval expected="absolute" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@positioning"/>
- <eval expected="[1.0 0.0 0.0 1.0 30000.0 30000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ctm"/>
- <eval expected="[1.0 0.0 0.0 1.0 30000.0 30000.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ctm"/>
+ <eval expected="[1.0 0.0 0.0 1.0 0.0 0.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@ctm"/>
+ <eval expected="[1.0 0.0 0.0 1.0 0.0 0.0]" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@ctm"/>
<eval expected="30000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@left-position"/>
<eval expected="30000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[2]/@left-position"/>
<eval expected="30000" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block[1]/@top-position"/>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks absolutely positioned block-containers with fox:transform.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container id="ro0" left="10pt" top="10pt" width="150pt" height="100pt" absolute-position="absolute" background-color="orange" margin="0pt" border="solid 5pt" border-top-color="red" reference-orientation="0">
+ <fo:block background-color="yellow" start-indent="0pt" end-indent="0pt">ro = 0</fo:block>
+ </fo:block-container>
+ <fo:block-container id="ro90" left="190pt" top="10pt" width="150pt" height="100pt" absolute-position="absolute" background-color="orange" margin="0pt" border="solid 5pt" border-top-color="red" reference-orientation="90" fox:transform="rotate(2)">
+ <fo:block background-color="yellow" start-indent="0pt" end-indent="0pt">ro = 90</fo:block>
+ </fo:block-container>
+ <fo:block-container id="ro180" left="10pt" top="190pt" width="150pt" height="100pt" absolute-position="absolute" background-color="orange" margin="0pt" border="solid 5pt" border-top-color="red" reference-orientation="180" fox:transform="scale(0.5) skewX(45)">
+ <fo:block background-color="yellow" start-indent="0pt" end-indent="0pt">ro = 180</fo:block>
+ </fo:block-container>
+ <fo:block-container id="ro270" left="190pt" top="190pt" width="150pt" height="100pt" absolute-position="absolute" background-color="orange" margin="0pt" border="solid 5pt" border-top-color="red" reference-orientation="270">
+ <fo:block background-color="yellow" start-indent="0pt" end-indent="0pt">ro = 270</fo:block>
+ </fo:block-container>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <eval expected="rotate(2)" xpath="//block[@prod-id='ro90']/@fox:transform"/>
+ <eval expected="scale(0.5) skewX(45)" xpath="//block[@prod-id='ro180']/@fox:transform"/>
+ </checks>
+</testcase>