]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Fixed logic error setting the transformation matrix for block-container viewports...
authorJeremias Maerki <jeremias@apache.org>
Thu, 17 Jan 2008 13:37:04 +0000 (13:37 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 17 Jan 2008 13:37:04 +0000 (13:37 +0000)
Important: External renderer implementations need to adjust for the change and implement the new method concatenateTransformationMatrix(AffineTransform) if the renderer is derived from AbstractPathOrientedRenderer.

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.

Added missing region background painting for PCL renderer.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@612815 13f79535-47bb-0310-9956-ffa450edef68

15 files changed:
src/documentation/content/xdocs/trunk/extensions.xml
src/java/org/apache/fop/area/CTM.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
src/java/org/apache/fop/render/AbstractRenderer.java
src/java/org/apache/fop/render/afp/AFPRenderer.java
src/java/org/apache/fop/render/java2d/Java2DRenderer.java
src/java/org/apache/fop/render/pcl/PCLRenderer.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/ps/PSRenderer.java
src/java/org/apache/fop/render/txt/TXTRenderer.java
status.xml
test/layoutengine/standard-testcases/block-container_absolute-position_display-align.xml
test/layoutengine/standard-testcases/block-container_absolute-position_fixed.xml
test/layoutengine/standard-testcases/block-container_absolute-position_fox-transform.xml [new file with mode: 0644]

index f54afea00eb16de3c2d5ce2ed353fbe1661a3680..4f6911f9b5eb8d36a692afd3720cde79e6a634a6 100644 (file)
@@ -187,6 +187,27 @@ to following pages. Here is an example of FO code creating such a table-header:<
           </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>
index d27e4ca205c16df2f777243dd21f4e0680a0b45e..c9fb52311daf641b54da7a26a2b3c2ed9da9ac3b 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.area;
 
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.io.Serializable;
 
@@ -100,6 +101,22 @@ public class CTM implements 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.
@@ -245,6 +262,14 @@ public class CTM implements Serializable {
         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
index 03aa380d298292bbce295a3777b78b562a249a67..ab153090b811cef1f7ecdc8917d04682560249cc 100644 (file)
 
 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;
 
@@ -485,15 +486,8 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
         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, 
@@ -871,7 +865,8 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
             } else {
                 viewportBlockArea.setBPD(getContentAreaBPD());
             }
-
+            transferForeignAttributes(viewportBlockArea);
+            
             TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
             TraitSetter.addBorders(viewportBlockArea, 
                     getBlockContainerFO().getCommonBorderPaddingBackground(), 
index ac11d56d5f8bd4b84b755c68e101b6266bd5332d..285cc5dedb7129c4092435c5c3125df39116352f 100644 (file)
@@ -21,12 +21,15 @@ package org.apache.fop.render;
 
 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;
@@ -39,8 +42,10 @@ import org.apache.fop.area.inline.ForeignObject;
 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
@@ -401,6 +406,8 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
         
     }
     
+    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
@@ -408,25 +415,17 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
         // 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.
@@ -435,37 +434,51 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
                 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);
             }
@@ -505,9 +518,15 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
             
             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.
@@ -613,10 +632,10 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
         
     /**
      * 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);
     
index 1fff79f77c7c5623bb80c2f38f94b7b7003a5b26..c23f7ae8eb84092b81879833726dd83f4678eefe 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.render;
 
 // Java
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -836,4 +837,32 @@ public abstract class AbstractRenderer
     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);
+    }
 }
index cdb2f2ec864fdb46b1b91ece6aa638faf2685c32..5cdd55d98af38a63ad770fbedbb0ae52a1826a34 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.render.afp;
 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;
@@ -525,6 +526,12 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
         // currentFontName = saveFontName;
     }
 
+    /** {@inheritDoc} */
+    protected void concatenateTransformationMatrix(AffineTransform at) {
+        //Not used here since AFPRenderer defines its own renderBlockViewport() method.
+        throw new UnsupportedOperationException("NYI");
+    }
+    
     /**
      * {@inheritDoc}
      */
index 0f73c57613df84ff7eca81efb8119cb72acc9c9e..63d5ad04c22987720cd07990569def969baa54af 100644 (file)
@@ -399,9 +399,12 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
         state = (Java2DGraphicsState)stateStack.pop();
     }
     
-    /**
-     * {@inheritDoc} 
-     */
+    /** {@inheritDoc} */
+    protected void concatenateTransformationMatrix(AffineTransform at) {
+        state.transform(at);
+    }
+    
+    /** {@inheritDoc} */
     protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
 
         saveGraphicsState();
index b48c28089f54d29804cf2f63acf32141192e3e04..1d606e9196cdfc97329acb7a7399a097669b70b7 100644 (file)
@@ -66,6 +66,7 @@ import org.apache.fop.area.Block;
 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;
@@ -92,6 +93,12 @@ import org.apache.fop.traits.BorderProps;
 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.
  */
@@ -580,6 +587,24 @@ public class PCLRenderer extends PrintRenderer {
         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}
      */
@@ -881,14 +906,10 @@ public class PCLRenderer extends PrintRenderer {
         // 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;
@@ -897,9 +918,6 @@ public class PCLRenderer extends PrintRenderer {
         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.
@@ -908,36 +926,42 @@ public class PCLRenderer extends PrintRenderer {
                 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);
@@ -981,6 +1005,18 @@ public class PCLRenderer extends PrintRenderer {
         //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();
@@ -1280,8 +1316,8 @@ public class PCLRenderer extends PrintRenderer {
             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(
index 3b28cc34c9bbd2cc5dc8775722f38c9d30115b2d..f32a257251ab3ee45230543ffbea3634a0b4a114 100644 (file)
@@ -639,16 +639,24 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         }
     }
 
-    /** 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. */
@@ -786,16 +794,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         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, 
@@ -806,14 +812,21 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         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.
@@ -1015,15 +1028,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         }
     }
     
-    /**
-     * 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 ");
@@ -1104,7 +1109,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
                 comment("------ break out!");
             }
             breakOutList.add(0, data); //Insert because of stack-popping
-            restoreGraphicsState();
+            restoreGraphicsState(false);
         }
         return breakOutList;
     }
@@ -1114,23 +1119,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
      * @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.
index 21ae8525941a2ed3761f9bad0c4e6a68162bc70e..7e3ce87a261d109d1bb7e84fe697855918d85005 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.render.ps;
 
 // Java
 import java.awt.Color;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.RenderedImage;
 import java.io.File;
@@ -326,15 +327,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         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);
@@ -619,6 +612,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     /** Restores the last graphics state of the rendering engine. */
     public void restoreGraphicsState() {
         try {
+            endTextObject();
             //delegate
             gen.restoreGraphicsState();
         } catch (IOException ioe) {
@@ -657,6 +651,15 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         }
     }
 
+    /** {@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);
@@ -1284,9 +1287,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     /** 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;
         }
     }
 
@@ -1466,7 +1469,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * {@inheritDoc}
      */
     protected void endVParea() {
-        endTextObject();
         restoreGraphicsState();
     }
 
index f1d959553571013e7788abcedca29e07a023712d..b6d09d7694132cb56bbcd65f360cccd1e14b4acb 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.render.txt;
 
 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;
@@ -322,6 +323,7 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
      * {@inheritDoc}
      */
     protected void saveGraphicsState() {
+        currentState.push(new CTM());
     }
 
     /**
@@ -329,6 +331,7 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
      * {@inheritDoc}
      */
     protected void restoreGraphicsState() {
+        currentState.pop();
     }
 
     /**
@@ -564,4 +567,10 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
     protected void endVParea() {
         currentState.pop();
     }
+    
+    /** {@inheritDoc} */
+    protected void concatenateTransformationMatrix(AffineTransform at) {
+        currentState.push(new CTM(ptToMpt(at)));
+    }
+    
 }
index 5cf11f580617a3be4e3cf24fd7811ffe7e61d694..917598bbecef5f11bfe13751a57ec8c269d556f1 100644 (file)
 
   <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.
index 6fb1916c6f8ceb484b4494daeebd7920eb6cadc5..667d2a37c09981902da1403d05a44d530521c292 100644 (file)
@@ -58,7 +58,7 @@
   <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"/>
@@ -74,7 +74,7 @@
 
     <!-- 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"/>
@@ -90,7 +90,7 @@
 
     <!-- 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"/>
index 54db8dea75d0ecf888022999a1fe915b5215f04d..b35fbadfef38999f0d3e91d03bd082486b2806b8 100644 (file)
@@ -56,8 +56,8 @@
     <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"/>
diff --git a/test/layoutengine/standard-testcases/block-container_absolute-position_fox-transform.xml b/test/layoutengine/standard-testcases/block-container_absolute-position_fox-transform.xml
new file mode 100644 (file)
index 0000000..2061ccf
--- /dev/null
@@ -0,0 +1,54 @@
+<?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>