]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
A big step towards a usable PostScript renderer.
authorJeremias Maerki <jeremias@apache.org>
Sun, 21 Aug 2005 13:36:53 +0000 (13:36 +0000)
committerJeremias Maerki <jeremias@apache.org>
Sun, 21 Aug 2005 13:36:53 +0000 (13:36 +0000)
Some code that can be used by both PS and PDF renderers factored out into a common base class (AbstractPathOrientedRenderer).
Coordinate system of PS renderer and PSGraphics2D changed to be based on points rather than millipoints to be the same as the PDFRenderer. This improves the chance to reuse code.
PSGenerator set to PostScript level 2 now, should later be configurable. There may still be some PS commands which should be changed from Level 2 to Level 1, just to be able to support Level 1 later should anyone ask for it.
Bitmap, EPS and SVG support should be mostly restored now.
PSState was extended to provide the same break-out mechanism for fixed b-cs. And it has better support for tracking the graphics state.
Added some FOP-specific comments to certain constructs for people who want to post-process the PS output.
BatchDiffer shows very few visual differences now on the layout engine test cases between PDF and PS.

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

src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java [new file with mode: 0644]
src/java/org/apache/fop/render/PrintRenderer.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java
src/java/org/apache/fop/render/ps/PSGenerator.java
src/java/org/apache/fop/render/ps/PSGraphics2D.java
src/java/org/apache/fop/render/ps/PSImageUtils.java
src/java/org/apache/fop/render/ps/PSRenderer.java
src/java/org/apache/fop/render/ps/PSState.java
src/java/org/apache/fop/render/ps/PSXMLHandler.java

diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
new file mode 100644 (file)
index 0000000..7663b5f
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.CTM;
+import org.apache.fop.area.RegionViewport;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.Viewport;
+import org.apache.fop.datatypes.ColorType;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.pdf.PDFState;
+import org.apache.fop.render.pdf.CTMHelper;
+import org.apache.fop.traits.BorderProps;
+
+/**
+ * Abstract base class for renderers like PDF and PostScript where many painting operations
+ * follow similar patterns which makes it possible to share some code.
+ */
+public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
+
+    /**
+     * Converts a ColorType to a java.awt.Color (sRGB).
+     * @param col the color
+     * @return the converted color
+     */
+    protected Color toColor(ColorType col) {
+        return new Color(col.getRed(), col.getGreen(), col.getBlue());
+    }
+    
+    /**
+     * Handle block traits.
+     * The block could be any sort of block with any positioning
+     * so this should render the traits such as border and background
+     * in its position.
+     *
+     * @param block the block to render the traits
+     */
+    protected void handleBlockTraits(Block block) {
+        int borderPaddingStart = block.getBorderAndPaddingWidthStart();
+        int borderPaddingBefore = block.getBorderAndPaddingWidthBefore();
+        
+        float startx = currentIPPosition / 1000f;
+        float starty = currentBPPosition / 1000f;
+        float width = block.getIPD() / 1000f;
+        float height = block.getBPD() / 1000f;
+
+        /* using start-indent now
+        Integer spaceStart = (Integer) block.getTrait(Trait.SPACE_START);
+        if (spaceStart != null) {
+            startx += spaceStart.floatValue() / 1000f;
+        }*/
+        startx += block.getStartIndent() / 1000f;
+        startx -= block.getBorderAndPaddingWidthStart() / 1000f;
+
+        width += borderPaddingStart / 1000f;
+        width += block.getBorderAndPaddingWidthEnd() / 1000f;
+        height += borderPaddingBefore / 1000f;
+        height += block.getBorderAndPaddingWidthAfter() / 1000f;
+
+        drawBackAndBorders(block, startx, starty,
+            width, height);
+    }
+
+    /**
+     * Handle the traits for a region
+     * This is used to draw the traits for the given page region.
+     * (See Sect. 6.4.1.2 of XSL-FO spec.)
+     * @param region the RegionViewport whose region is to be drawn
+     */
+    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);
+    }
+
+    
+    /**
+     * Draw the background and borders.
+     * This draws the background and border traits for an area given
+     * the position.
+     *
+     * @param area the area to get the traits from
+     * @param startx the start x position
+     * @param starty the start y position
+     * @param width the width of the area
+     * @param height the height of the area
+     */
+    protected void drawBackAndBorders(Area area,
+                    float startx, float starty,
+                    float width, float height) {
+        // draw background then border
+
+        BorderProps bpsBefore = (BorderProps)area.getTrait(Trait.BORDER_BEFORE);
+        BorderProps bpsAfter = (BorderProps)area.getTrait(Trait.BORDER_AFTER);
+        BorderProps bpsStart = (BorderProps)area.getTrait(Trait.BORDER_START);
+        BorderProps bpsEnd = (BorderProps)area.getTrait(Trait.BORDER_END);
+
+        Trait.Background back;
+        back = (Trait.Background)area.getTrait(Trait.BACKGROUND);
+        if (back != null) {
+            endTextObject();
+
+            //Calculate padding rectangle
+            float sx = startx;
+            float sy = starty;
+            float paddRectWidth = width;
+            float paddRectHeight = height;
+            if (bpsStart != null) {
+                sx += bpsStart.width / 1000f;
+                paddRectWidth -= bpsStart.width / 1000f;
+            }
+            if (bpsBefore != null) {
+                sy += bpsBefore.width / 1000f;
+                paddRectHeight -= bpsBefore.width / 1000f;
+            }
+            if (bpsEnd != null) {
+                paddRectWidth -= bpsEnd.width / 1000f;
+            }
+            if (bpsAfter != null) {
+                paddRectHeight -= bpsAfter.width / 1000f;
+            }
+
+            if (back.getColor() != null) {
+                updateColor(back.getColor(), true);
+                fillRect(sx, sy, paddRectWidth, paddRectHeight);
+            }
+            if (back.getFopImage() != null) {
+                FopImage fopimage = back.getFopImage();
+                if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
+                    saveGraphicsState();
+                    clipRect(sx, sy, paddRectWidth, paddRectHeight);
+                    int horzCount = (int)((paddRectWidth 
+                            * 1000 / fopimage.getIntrinsicWidth()) + 1.0f); 
+                    int vertCount = (int)((paddRectHeight 
+                            * 1000 / fopimage.getIntrinsicHeight()) + 1.0f); 
+                    if (back.getRepeat() == EN_NOREPEAT) {
+                        horzCount = 1;
+                        vertCount = 1;
+                    } else if (back.getRepeat() == EN_REPEATX) {
+                        vertCount = 1;
+                    } else if (back.getRepeat() == EN_REPEATY) {
+                        horzCount = 1;
+                    }
+                    //change from points to millipoints
+                    sx *= 1000;
+                    sy *= 1000;
+                    if (horzCount == 1) {
+                        sx += back.getHoriz();
+                    }
+                    if (vertCount == 1) {
+                        sy += back.getVertical();
+                    }
+                    for (int x = 0; x < horzCount; x++) {
+                        for (int y = 0; y < vertCount; y++) {
+                            // place once
+                            Rectangle2D pos;
+                            pos = new Rectangle2D.Float(sx + (x * fopimage.getIntrinsicWidth()),
+                                                        sy + (y * fopimage.getIntrinsicHeight()),
+                                                        fopimage.getIntrinsicWidth(),
+                                                        fopimage.getIntrinsicHeight());
+                            drawImage(back.getURL(), pos);
+                        }
+                    }
+                    
+                    restoreGraphicsState();
+                } else {
+                    log.warn("Can't find background image: " + back.getURL());
+                }
+            }
+        }
+
+        boolean[] b = new boolean[] {
+            (bpsBefore != null), (bpsEnd != null), 
+            (bpsAfter != null), (bpsStart != null)};
+        if (!b[0] && !b[1] && !b[2] && !b[3]) {
+            return;
+        }
+        float[] bw = new float[] {
+            (b[0] ? bpsBefore.width / 1000f : 0.0f),
+            (b[1] ? bpsEnd.width / 1000f : 0.0f),
+            (b[2] ? bpsAfter.width / 1000f : 0.0f),
+            (b[3] ? bpsStart.width / 1000f : 0.0f)};
+        float[] clipw = new float[] {
+            BorderProps.getClippedWidth(bpsBefore) / 1000f,    
+            BorderProps.getClippedWidth(bpsEnd) / 1000f,    
+            BorderProps.getClippedWidth(bpsAfter) / 1000f,    
+            BorderProps.getClippedWidth(bpsStart) / 1000f};
+        starty += clipw[0];
+        height -= clipw[0];
+        height -= clipw[2];
+        startx += clipw[3];
+        width -= clipw[3];
+        width -= clipw[1];
+        
+        boolean[] slant = new boolean[] {
+            (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
+        if (bpsBefore != null) {
+            endTextObject();
+
+            float sx1 = startx;
+            float sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1);
+            float ex1 = startx + width;
+            float ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1);
+            float outery = starty - clipw[0];
+            float clipy = outery + clipw[0];
+            float innery = outery + bw[0];
+
+            saveGraphicsState();
+            moveTo(sx1, clipy);
+            float sx1a = sx1;
+            float ex1a = ex1;
+            if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
+                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+                    sx1a -= clipw[3];
+                }
+                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
+                    ex1a += clipw[1];
+                }
+                lineTo(sx1a, outery);
+                lineTo(ex1a, outery);
+            }
+            lineTo(ex1, clipy);
+            lineTo(ex2, innery);
+            lineTo(sx2, innery);
+            closePath();
+            clip();
+            drawBorderLine(sx1a, outery, ex1a, innery, true, true, 
+                    bpsBefore.style, bpsBefore.color);
+            restoreGraphicsState();
+        }
+        if (bpsEnd != null) {
+            endTextObject();
+
+            float sy1 = starty;
+            float sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1);
+            float ey1 = starty + height;
+            float ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1);
+            float outerx = startx + width + clipw[1];
+            float clipx = outerx - clipw[1];
+            float innerx = outerx - bw[1];
+            
+            saveGraphicsState();
+            moveTo(clipx, sy1);
+            float sy1a = sy1;
+            float ey1a = ey1;
+            if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
+                if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
+                    sy1a -= clipw[0];
+                }
+                if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
+                    ey1a += clipw[2];
+                }
+                lineTo(outerx, sy1a);
+                lineTo(outerx, ey1a);
+            }
+            lineTo(clipx, ey1);
+            lineTo(innerx, ey2);
+            lineTo(innerx, sy2);
+            closePath();
+            clip();
+            drawBorderLine(innerx, sy1a, outerx, ey1a, false, false, bpsEnd.style, bpsEnd.color);
+            restoreGraphicsState();
+        }
+        if (bpsAfter != null) {
+            endTextObject();
+
+            float sx1 = startx;
+            float sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1);
+            float ex1 = startx + width;
+            float ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1);
+            float outery = starty + height + clipw[2];
+            float clipy = outery - clipw[2];
+            float innery = outery - bw[2];
+
+            saveGraphicsState();
+            moveTo(ex1, clipy);
+            float sx1a = sx1;
+            float ex1a = ex1;
+            if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
+                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+                    sx1a -= clipw[3];
+                }
+                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
+                    ex1a += clipw[1];
+                }
+                lineTo(ex1a, outery);
+                lineTo(sx1a, outery);
+            }
+            lineTo(sx1, clipy);
+            lineTo(sx2, innery);
+            lineTo(ex2, innery);
+            closePath();
+            clip();
+            drawBorderLine(sx1a, innery, ex1a, outery, true, false, bpsAfter.style, bpsAfter.color);
+            restoreGraphicsState();
+        }
+        if (bpsStart != null) {
+            endTextObject();
+
+            float sy1 = starty;
+            float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
+            float ey1 = sy1 + height;
+            float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
+            float outerx = startx - clipw[3];
+            float clipx = outerx + clipw[3];
+            float innerx = outerx + bw[3];
+
+            saveGraphicsState();
+            moveTo(clipx, ey1);
+            float sy1a = sy1;
+            float ey1a = ey1;
+            if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+                if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
+                    sy1a -= clipw[0];
+                }
+                if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
+                    ey1a += clipw[2];
+                }
+                lineTo(outerx, ey1a);
+                lineTo(outerx, sy1a);
+            }
+            lineTo(clipx, sy1);
+            lineTo(innerx, sy2);
+            lineTo(innerx, ey2);
+            closePath();
+            clip();
+            drawBorderLine(outerx, sy1a, innerx, ey1a, false, true, bpsStart.style, bpsStart.color);
+            restoreGraphicsState();
+        }
+    }
+    
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
+     */
+    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;
+
+        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();
+            }
+            
+            CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
+            ctm = tempctm.multiply(ctm);
+
+            //This is the content-rect
+            float width = (float)bv.getIPD() / 1000f;
+            float height = (float)bv.getBPD() / 1000f;
+            
+            //Adjust for spaces (from margin or indirectly by start-indent etc.
+            Integer spaceStart = (Integer) bv.getTrait(Trait.SPACE_START);
+            if (spaceStart != null) {
+                x += spaceStart.floatValue() / 1000;
+            }
+            Integer spaceBefore = (Integer) bv.getTrait(Trait.SPACE_BEFORE);
+            if (spaceBefore != null) {
+                y += spaceBefore.floatValue() / 1000;
+            }
+
+            float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
+            float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
+
+            drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
+
+            //Now adjust for border/padding
+            x += borderPaddingStart / 1000f;
+            y += borderPaddingBefore / 1000f;
+            
+            if (bv.getClip()) {
+                saveGraphicsState();
+                clipRect(x, y, width, height);
+            }
+
+            startVParea(ctm);
+
+            currentIPPosition = 0;
+            currentBPPosition = 0;
+
+            renderBlocks(bv, children);
+            endVParea();
+
+            if (bv.getClip()) {
+                restoreGraphicsState();
+            }
+
+            // clip if necessary
+
+            if (breakOutList != null) {
+                restoreStateStackAfterBreakOut(breakOutList);
+            }
+            
+            currentIPPosition = saveIP;
+            currentBPPosition = saveBP;
+        } else {
+
+            Integer spaceBefore = (Integer)bv.getTrait(Trait.SPACE_BEFORE);
+            if (spaceBefore != null) {
+                currentBPPosition += spaceBefore.intValue();
+            }
+
+            //borders and background in the old coordinate system
+            handleBlockTraits(bv);
+
+            CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
+            ctm = tempctm.multiply(ctm);
+            
+            //Now adjust for border/padding
+            x += borderPaddingStart / 1000f;
+            y += borderPaddingBefore / 1000f;
+
+            // clip if necessary
+            if (bv.getClip()) {
+                saveGraphicsState();
+                float width = (float)bv.getIPD() / 1000f;
+                float height = (float)bv.getBPD() / 1000f;
+                clipRect(x, y, width, height);
+            }
+
+            if (ctm != null) {
+                startVParea(ctm);
+                currentIPPosition = 0;
+                currentBPPosition = 0;
+            }
+            renderBlocks(bv, children);
+            if (ctm != null) {
+                endVParea();
+            }
+
+            if (bv.getClip()) {
+                restoreGraphicsState();
+            }
+
+            currentIPPosition = saveIP;
+            currentBPPosition = saveBP;
+            
+            //Adjust BP position (alloc BPD + spaces)
+            if (spaceBefore != null) {
+                currentBPPosition += spaceBefore.intValue();
+            }
+            currentBPPosition += (int)(bv.getAllocBPD());
+            Integer spaceAfter = (Integer)bv.getTrait(Trait.SPACE_AFTER);
+            if (spaceAfter != null) {
+                currentBPPosition += spaceAfter.intValue();
+            }
+        }
+        //currentFontName = saveFontName;
+    }
+
+    /**
+     * Render an inline viewport.
+     * This renders an inline viewport by clipping if necessary.
+     * @param viewport the viewport to handle
+     */
+    public void renderViewport(Viewport viewport) {
+
+        float x = currentIPPosition / 1000f;
+        float y = (currentBPPosition + viewport.getOffset()) / 1000f;
+        float width = viewport.getIPD() / 1000f;
+        float height = viewport.getBPD() / 1000f;
+        // TODO: Calculate the border rect correctly. 
+        float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f;
+        float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f;
+        float bpwidth = borderPaddingStart 
+                + (viewport.getBorderAndPaddingWidthEnd() / 1000f);
+        float bpheight = borderPaddingBefore
+                + (viewport.getBorderAndPaddingWidthAfter() / 1000f);
+
+        drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight);
+
+        if (viewport.getClip()) {
+            saveGraphicsState();
+
+            clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height);
+        }
+        super.renderViewport(viewport);
+
+        if (viewport.getClip()) {
+            restoreGraphicsState();
+        }
+    }
+
+    /**
+     * Restores the state stack after a break out.
+     * @param breakOutList the state stack to restore.
+     */
+    protected abstract void restoreStateStackAfterBreakOut(List breakOutList);
+    
+    /**
+     * Breaks out of the state stack to handle fixed block-containers.
+     * @return the saved state stack to recreate later
+     */
+    protected abstract List breakOutOfStateStack();
+
+    /** Saves the graphics state of the rendering engine. */
+    protected abstract void saveGraphicsState();
+    
+    /** Restores the last graphics state of the rendering engine. */
+    protected abstract void restoreGraphicsState();
+
+    /** Indicates the beginning of a text object. */
+    protected abstract void beginTextObject();
+    
+    /** Indicates the end of a text object. */
+    protected abstract void endTextObject();
+    
+    /** Clip using the current path. */
+    protected abstract void clip();
+        
+    /**
+     * 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
+     */
+    protected abstract void clipRect(float x, float y, float width, float height);
+    
+    /**
+     * Moves the current point to (x, y), omitting any connecting line segment. 
+     * @param x x coordinate
+     * @param y y coordinate
+     */
+    protected abstract void moveTo(float x, float y);
+    
+    /**
+     * Appends a straight line segment from the current point to (x, y). The 
+     * new current point is (x, y). 
+     * @param x x coordinate
+     * @param y y coordinate
+     */
+    protected abstract void lineTo(float x, float y);
+    
+    /**
+     * Closes the current subpath by appending a straight line segment from 
+     * the current point to the starting point of the subpath.
+     */
+    protected abstract void closePath();
+    
+    /**
+     * Fill 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
+     */
+    protected abstract void fillRect(float x, float y, float width, float height);
+
+    /**
+     * Establishes a new foreground or fill color.
+     * @param col the color to apply (null skips this operation)
+     * @param fill true to set the fill color, false for the foreground color
+     */
+    protected abstract void updateColor(ColorType col, boolean fill);
+    
+    /**
+     * Draw an image at the indicated location.
+     * @param url the URI/URL of the image
+     * @param pos the position of the image
+     */
+    protected abstract void drawImage(String url, Rectangle2D pos);
+    
+    /**
+     * Draw a border segment of an XSL-FO style border.
+     * @param x1 starting x coordinate
+     * @param y1 starting y coordinate
+     * @param x2 ending x coordinate
+     * @param y2 ending y coordinate
+     * @param horz true for horizontal border segments, false for vertical border segments
+     * @param startOrBefore true for border segments on the start or before edge, 
+     *                      false for end or after.
+     * @param style the border style (one of Constants.EN_DASHED etc.)
+     * @param col the color for the border segment
+     */
+    protected abstract void drawBorderLine(float x1, float y1, float x2, float y2, 
+            boolean horz, boolean startOrBefore, int style, ColorType col);
+    
+}
index aafed0355ef7becbc102b48725f2748dc194d30c..1d97580b043e58d217a49bbc2d572904e83ce63c 100644 (file)
 package org.apache.fop.render;
 
 // FOP
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Trait;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontSetup;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.traits.BorderProps;
 
 // Java
+import java.awt.Color;
+import java.awt.geom.Rectangle2D;
 import java.util.List;
 
 /** Abstract base class of "Print" type renderers.  */
@@ -44,4 +50,25 @@ public abstract class PrintRenderer extends AbstractRenderer {
         FontSetup.setup(fontInfo, fontList);
     }
 
+    /**
+     * Lightens up a color for groove, ridge, inset and outset border effects.
+     * @param col the color to lighten up
+     * @param factor factor by which to lighten up (negative values darken the color)
+     * @return the modified color
+     */
+    protected Color lightenColor(Color col, float factor) {
+        float[] cols = new float[3];
+        cols = col.getColorComponents(cols);
+        if (factor > 0) {
+            cols[0] += (1.0 - cols[0]) * factor;
+            cols[1] += (1.0 - cols[1]) * factor;
+            cols[2] += (1.0 - cols[2]) * factor;
+        } else {
+            cols[0] -= cols[0] * -factor;
+            cols[1] -= cols[1] * -factor;
+            cols[2] -= cols[2] * -factor;
+        }
+        return new Color(cols[0], cols[1], cols[2]);
+    }
+
 }
index 9d343314bcee3b884e0acc9df7c31d80b803b985..500baf3da293e335fc1422d27b81e7f54f95e7f2 100644 (file)
@@ -81,6 +81,7 @@ import org.apache.fop.pdf.PDFState;
 import org.apache.fop.pdf.PDFStream;
 import org.apache.fop.pdf.PDFText;
 import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.PrintRenderer;
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.traits.BorderProps;
@@ -103,7 +104,7 @@ text decoration
  * Renderer that renders areas to PDF
  *
  */
-public class PDFRenderer extends PrintRenderer {
+public class PDFRenderer extends AbstractPathOrientedRenderer {
     
     /**
      * The mime type for pdf
@@ -514,326 +515,11 @@ public class PDFRenderer extends PrintRenderer {
      */
     protected void handleRegionTraits(RegionViewport region) {
         currentFontName = "";
-        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);
-    }
-
-    /**
-     * Handle block traits.
-     * The block could be any sort of block with any positioning
-     * so this should render the traits such as border and background
-     * in its position.
-     *
-     * @param block the block to render the traits
-     */
-    protected void handleBlockTraits(Block block) {
-        int borderPaddingStart = block.getBorderAndPaddingWidthStart();
-        int borderPaddingBefore = block.getBorderAndPaddingWidthBefore();
-        
-        float startx = currentIPPosition / 1000f;
-        float starty = currentBPPosition / 1000f;
-        float width = block.getIPD() / 1000f;
-        float height = block.getBPD() / 1000f;
-
-        /* using start-indent now
-        Integer spaceStart = (Integer) block.getTrait(Trait.SPACE_START);
-        if (spaceStart != null) {
-            startx += spaceStart.floatValue() / 1000f;
-        }*/
-        startx += block.getStartIndent() / 1000f;
-        startx -= block.getBorderAndPaddingWidthStart() / 1000f;
-
-        width += borderPaddingStart / 1000f;
-        width += block.getBorderAndPaddingWidthEnd() / 1000f;
-        height += borderPaddingBefore / 1000f;
-        height += block.getBorderAndPaddingWidthAfter() / 1000f;
-
-        drawBackAndBorders(block, startx, starty,
-            width, height);
-    }
-
-    /**
-     * Draw the background and borders.
-     * This draws the background and border traits for an area given
-     * the position.
-     *
-     * @param area the area to get the traits from
-     * @param startx the start x position
-     * @param starty the start y position
-     * @param width the width of the area
-     * @param height the height of the area
-     */
-    protected void drawBackAndBorders(Area area,
-                    float startx, float starty,
-                    float width, float height) {
-        // draw background then border
-
-        BorderProps bpsBefore = (BorderProps)area.getTrait(Trait.BORDER_BEFORE);
-        BorderProps bpsAfter = (BorderProps)area.getTrait(Trait.BORDER_AFTER);
-        BorderProps bpsStart = (BorderProps)area.getTrait(Trait.BORDER_START);
-        BorderProps bpsEnd = (BorderProps)area.getTrait(Trait.BORDER_END);
-
-        Trait.Background back;
-        back = (Trait.Background)area.getTrait(Trait.BACKGROUND);
-        if (back != null) {
-            endTextObject();
-
-            //Calculate padding rectangle
-            float sx = startx;
-            float sy = starty;
-            float paddRectWidth = width;
-            float paddRectHeight = height;
-            if (bpsStart != null) {
-                sx += bpsStart.width / 1000f;
-                paddRectWidth -= bpsStart.width / 1000f;
-            }
-            if (bpsBefore != null) {
-                sy += bpsBefore.width / 1000f;
-                paddRectHeight -= bpsBefore.width / 1000f;
-            }
-            if (bpsEnd != null) {
-                paddRectWidth -= bpsEnd.width / 1000f;
-            }
-            if (bpsAfter != null) {
-                paddRectHeight -= bpsAfter.width / 1000f;
-            }
-
-            if (back.getColor() != null) {
-                updateColor(back.getColor(), true, null);
-                currentStream.add(sx + " " + sy + " "
-                                  + paddRectWidth + " " + paddRectHeight + " re\n");
-                currentStream.add("f\n");
-            }
-            if (back.getFopImage() != null) {
-                FopImage fopimage = back.getFopImage();
-                if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
-                    saveGraphicsState();
-                    clip(sx, sy, paddRectWidth, paddRectHeight);
-                    int horzCount = (int)((paddRectWidth 
-                            * 1000 / fopimage.getIntrinsicWidth()) + 1.0f); 
-                    int vertCount = (int)((paddRectHeight 
-                            * 1000 / fopimage.getIntrinsicHeight()) + 1.0f); 
-                    if (back.getRepeat() == EN_NOREPEAT) {
-                        horzCount = 1;
-                        vertCount = 1;
-                    } else if (back.getRepeat() == EN_REPEATX) {
-                        vertCount = 1;
-                    } else if (back.getRepeat() == EN_REPEATY) {
-                        horzCount = 1;
-                    }
-                    //change from points to millipoints
-                    sx *= 1000;
-                    sy *= 1000;
-                    if (horzCount == 1) {
-                        sx += back.getHoriz();
-                    }
-                    if (vertCount == 1) {
-                        sy += back.getVertical();
-                    }
-                    for (int x = 0; x < horzCount; x++) {
-                        for (int y = 0; y < vertCount; y++) {
-                            // place once
-                            Rectangle2D pos;
-                            pos = new Rectangle2D.Float(sx + (x * fopimage.getIntrinsicWidth()),
-                                                        sy + (y * fopimage.getIntrinsicHeight()),
-                                                        fopimage.getIntrinsicWidth(),
-                                                        fopimage.getIntrinsicHeight());
-                            putImage(back.getURL(), pos);
-                        }
-                    }
-                    
-                    restoreGraphicsState();
-                } else {
-                    log.warn("Can't find background image: " + back.getURL());
-                }
-            }
-        }
-
-        boolean[] b = new boolean[] {
-            (bpsBefore != null), (bpsEnd != null), 
-            (bpsAfter != null), (bpsStart != null)};
-        if (!b[0] && !b[1] && !b[2] && !b[3]) {
-            return;
-        }
-        float[] bw = new float[] {
-            (b[0] ? bpsBefore.width / 1000f : 0.0f),
-            (b[1] ? bpsEnd.width / 1000f : 0.0f),
-            (b[2] ? bpsAfter.width / 1000f : 0.0f),
-            (b[3] ? bpsStart.width / 1000f : 0.0f)};
-        float[] clipw = new float[] {
-            BorderProps.getClippedWidth(bpsBefore) / 1000f,    
-            BorderProps.getClippedWidth(bpsEnd) / 1000f,    
-            BorderProps.getClippedWidth(bpsAfter) / 1000f,    
-            BorderProps.getClippedWidth(bpsStart) / 1000f};
-        starty += clipw[0];
-        height -= clipw[0];
-        height -= clipw[2];
-        startx += clipw[3];
-        width -= clipw[3];
-        width -= clipw[1];
-        
-        boolean[] slant = new boolean[] {
-            (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
-        if (bpsBefore != null) {
-            endTextObject();
-
-            float sx1 = startx;
-            float sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1);
-            float ex1 = startx + width;
-            float ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1);
-            float outery = starty - clipw[0];
-            float clipy = outery + clipw[0];
-            float innery = outery + bw[0];
-
-            saveGraphicsState();
-            moveTo(sx1, clipy);
-            float sx1a = sx1;
-            float ex1a = ex1;
-            if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
-                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
-                    sx1a -= clipw[3];
-                }
-                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
-                    ex1a += clipw[1];
-                }
-                lineTo(sx1a, outery);
-                lineTo(ex1a, outery);
-            }
-            lineTo(ex1, clipy);
-            lineTo(ex2, innery);
-            lineTo(sx2, innery);
-            closePath();
-            clip();
-            drawBorderLine(sx1a, outery, ex1a, innery, true, true, 
-                    bpsBefore.style, bpsBefore.color);
-            restoreGraphicsState();
-        }
-        if (bpsEnd != null) {
-            endTextObject();
-
-            float sy1 = starty;
-            float sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1);
-            float ey1 = starty + height;
-            float ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1);
-            float outerx = startx + width + clipw[1];
-            float clipx = outerx - clipw[1];
-            float innerx = outerx - bw[1];
-            
-            saveGraphicsState();
-            moveTo(clipx, sy1);
-            float sy1a = sy1;
-            float ey1a = ey1;
-            if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
-                if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
-                    sy1a -= clipw[0];
-                }
-                if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
-                    ey1a += clipw[2];
-                }
-                lineTo(outerx, sy1a);
-                lineTo(outerx, ey1a);
-            }
-            lineTo(clipx, ey1);
-            lineTo(innerx, ey2);
-            lineTo(innerx, sy2);
-            closePath();
-            clip();
-            drawBorderLine(innerx, sy1a, outerx, ey1a, false, false, bpsEnd.style, bpsEnd.color);
-            restoreGraphicsState();
-        }
-        if (bpsAfter != null) {
-            endTextObject();
-
-            float sx1 = startx;
-            float sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1);
-            float ex1 = startx + width;
-            float ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1);
-            float outery = starty + height + clipw[2];
-            float clipy = outery - clipw[2];
-            float innery = outery - bw[2];
-
-            saveGraphicsState();
-            moveTo(ex1, clipy);
-            float sx1a = sx1;
-            float ex1a = ex1;
-            if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
-                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
-                    sx1a -= clipw[3];
-                }
-                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
-                    ex1a += clipw[1];
-                }
-                lineTo(ex1a, outery);
-                lineTo(sx1a, outery);
-            }
-            lineTo(sx1, clipy);
-            lineTo(sx2, innery);
-            lineTo(ex2, innery);
-            closePath();
-            clip();
-            drawBorderLine(sx1a, innery, ex1a, outery, true, false, bpsAfter.style, bpsAfter.color);
-            restoreGraphicsState();
-        }
-        if (bpsStart != null) {
-            endTextObject();
-
-            float sy1 = starty;
-            float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
-            float ey1 = sy1 + height;
-            float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
-            float outerx = startx - clipw[3];
-            float clipx = outerx + clipw[3];
-            float innerx = outerx + bw[3];
-
-            saveGraphicsState();
-            moveTo(clipx, ey1);
-            float sy1a = sy1;
-            float ey1a = ey1;
-            if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
-                if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
-                    sy1a -= clipw[0];
-                }
-                if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
-                    ey1a += clipw[2];
-                }
-                lineTo(outerx, ey1a);
-                lineTo(outerx, sy1a);
-            }
-            lineTo(clipx, sy1);
-            lineTo(innerx, sy2);
-            lineTo(innerx, ey2);
-            closePath();
-            clip();
-            drawBorderLine(outerx, sy1a, innerx, ey1a, false, true, bpsStart.style, bpsStart.color);
-            restoreGraphicsState();
-        }
-    }
-    
-    private Color lightenColor(Color col, float factor) {
-        float[] cols = new float[3];
-        cols = col.getColorComponents(cols);
-        if (factor > 0) {
-            cols[0] += (1.0 - cols[0]) * factor;
-            cols[1] += (1.0 - cols[1]) * factor;
-            cols[2] += (1.0 - cols[2]) * factor;
-        } else {
-            cols[0] -= cols[0] * -factor;
-            cols[1] -= cols[1] * -factor;
-            cols[2] -= cols[2] * -factor;
-        }
-        return new Color(cols[0], cols[1], cols[2]);
+        super.handleRegionTraits(region);
     }
 
-    private void drawBorderLine(float x1, float y1, float x2, float y2, 
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void drawBorderLine(float x1, float y1, float x2, float y2, 
             boolean horz, boolean startOrBefore, int style, ColorType col) {
         float w = x2 - x1;
         float h = y2 - y1;
@@ -1011,12 +697,34 @@ public class PDFRenderer extends PrintRenderer {
         }
     }
 
+    /**
+     * 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
+     */
+    protected void clipRect(float x, float y, float width, float height) {
+        currentStream.add(x + " " + y + " " + width + " " + height + " re ");
+        clip();
+    }
+
+    /**
+     * Clip an area.
+     */
+    protected void clip() {
+        currentStream.add("W\n");
+        currentStream.add("n\n");
+    }
+
     /**
      * Moves the current point to (x, y), omitting any connecting line segment. 
      * @param x x coordinate
      * @param y y coordinate
      */
-    private void moveTo(float x, float y) {
+    protected void moveTo(float x, float y) {
         currentStream.add(x + " " + y + " m ");
     }
     
@@ -1026,7 +734,7 @@ public class PDFRenderer extends PrintRenderer {
      * @param x x coordinate
      * @param y y coordinate
      */
-    private void lineTo(float x, float y) {
+    protected void lineTo(float x, float y) {
         currentStream.add(x + " " + y + " l ");
     }
     
@@ -1034,9 +742,16 @@ public class PDFRenderer extends PrintRenderer {
      * Closes the current subpath by appending a straight line segment from 
      * the current point to the starting point of the subpath.
      */
-    private void closePath() {
+    protected void closePath() {
         currentStream.add("h ");
     }
+
+    /** 
+     * @see org.apache.fop.render.AbstractPathOrientedRenderer#fillRect(float, float, float, float)
+     */
+    protected void fillRect(float x, float y, float w, float h) {
+        currentStream.add(x + " " + y + " " + w + " " + h + " re f\n");
+    }
     
     /**
      * Draw a line.
@@ -1053,7 +768,7 @@ public class PDFRenderer extends PrintRenderer {
 
     /**
      * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
-     */
+     *//*
     protected void renderBlockViewport(BlockViewport bv, List children) {
         // clip and position viewport if necessary
 
@@ -1077,21 +792,7 @@ public class PDFRenderer extends PrintRenderer {
             //after the block-container has been painted. See below.
             List breakOutList = null;
             if (bv.getPositioning() == Block.FIXED) {
-                //break out
-                breakOutList = new java.util.ArrayList();
-                PDFState.Data data;
-                while (true) {
-                    data = currentState.getData();
-                    if (currentState.pop() == null) {
-                        break;
-                    }
-                    if (breakOutList.size() == 0) {
-                        comment("------ break out!");
-                    }
-                    breakOutList.add(0, data); //Insert because of stack-popping
-                    //getLogger().debug("Adding to break out list: " + data);
-                    restoreGraphicsState();
-                }
+                breakOutList = breakOutOfStateStack();
             }
             
             CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
@@ -1122,7 +823,7 @@ public class PDFRenderer extends PrintRenderer {
             
             if (bv.getClip()) {
                 saveGraphicsState();
-                clip(x, y, width, height);
+                clipRect(x, y, width, height);
             }
 
             startVParea(ctm);
@@ -1140,31 +841,7 @@ public class PDFRenderer extends PrintRenderer {
             // clip if necessary
 
             if (breakOutList != null) {
-                comment("------ restoring context after break-out...");
-                PDFState.Data data;
-                Iterator i = breakOutList.iterator();
-                while (i.hasNext()) {
-                    data = (PDFState.Data)i.next();
-                    //getLogger().debug("Restoring: " + data);
-                    currentState.push();
-                    saveGraphicsState();
-                    if (data.concatenations != null) {
-                        Iterator tr = data.concatenations.iterator();
-                        while (tr.hasNext()) {
-                            AffineTransform at = (AffineTransform)tr.next();
-                            currentState.setTransform(at);
-                            double[] matrix = new double[6];
-                            at.getMatrix(matrix);
-                            tempctm = new CTM(matrix[0], matrix[1], matrix[2], matrix[3], 
-                                    matrix[4] * 1000, matrix[5] * 1000);
-                            currentStream.add(CTMHelper.toPDFString(tempctm) + " cm\n");
-                        }
-                    }
-                    //TODO Break-out: Also restore items such as line width and color
-                    //Left out for now because all this painting stuff is very
-                    //inconsistent. Some values go over PDFState, some don't.
-                }
-                comment("------ done.");
+                restoreStateStackAfterBreakOut(breakOutList);
             }
             
             currentIPPosition = saveIP;
@@ -1191,7 +868,7 @@ public class PDFRenderer extends PrintRenderer {
                 saveGraphicsState();
                 float width = (float)bv.getIPD() / 1000f;
                 float height = (float)bv.getBPD() / 1000f;
-                clip(x, y, width, height);
+                clipRect(x, y, width, height);
             }
 
             if (ctm != null) {
@@ -1222,28 +899,59 @@ public class PDFRenderer extends PrintRenderer {
             }
         }
         currentFontName = saveFontName;
-    }
-
+    }*/
+   
     /**
-     * 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
+     * Breaks out of the state stack to handle fixed block-containers.
+     * @return the saved state stack to recreate later
      */
-    protected void clip(float x, float y, float width, float height) {
-        currentStream.add(x + " " + y + " " + width + " " + height + " re ");
-        clip();
+    protected List breakOutOfStateStack() {
+        List breakOutList = new java.util.ArrayList();
+        PDFState.Data data;
+        while (true) {
+            data = currentState.getData();
+            if (currentState.pop() == null) {
+                break;
+            }
+            if (breakOutList.size() == 0) {
+                comment("------ break out!");
+            }
+            breakOutList.add(0, data); //Insert because of stack-popping
+            restoreGraphicsState();
+        }
+        return breakOutList;
     }
 
     /**
-     * Clip an area.
+     * Restores the state stack after a break out.
+     * @param breakOutList the state stack to restore.
      */
-    protected void clip() {
-        currentStream.add("W\n");
-        currentStream.add("n\n");
+    protected void restoreStateStackAfterBreakOut(List breakOutList) {
+        CTM tempctm;
+        comment("------ restoring context after break-out...");
+        PDFState.Data data;
+        Iterator i = breakOutList.iterator();
+        while (i.hasNext()) {
+            data = (PDFState.Data)i.next();
+            currentState.push();
+            saveGraphicsState();
+            if (data.concatenations != null) {
+                Iterator tr = data.concatenations.iterator();
+                while (tr.hasNext()) {
+                    AffineTransform at = (AffineTransform)tr.next();
+                    currentState.setTransform(at);
+                    double[] matrix = new double[6];
+                    at.getMatrix(matrix);
+                    tempctm = new CTM(matrix[0], matrix[1], matrix[2], matrix[3], 
+                            matrix[4] * 1000, matrix[5] * 1000);
+                    currentStream.add(CTMHelper.toPDFString(tempctm) + " cm\n");
+                }
+            }
+            //TODO Break-out: Also restore items such as line width and color
+            //Left out for now because all this painting stuff is very
+            //inconsistent. Some values go over PDFState, some don't.
+        }
+        comment("------ done.");
     }
 
     /**
@@ -1592,15 +1300,6 @@ public class PDFRenderer extends PrintRenderer {
         }
     }
     
-    /**
-     * Converts a ColorType to a java.awt.Color (sRGB).
-     * @param col the color
-     * @return the converted color
-     */
-    private Color toColor(ColorType col) {
-        return new Color(col.getRed(), col.getGreen(), col.getBlue());
-    }
-    
     /**
      * Establishes a new foreground or fill color.
      * @param col the color to apply (null skips this operation)
@@ -1625,6 +1324,11 @@ public class PDFRenderer extends PrintRenderer {
         }
     }
 
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected  void updateColor(ColorType col, boolean fill) {
+        updateColor(col, fill, null);
+    }
+    
     private void updateFont(String name, int size, StringBuffer pdf) {
         if ((!name.equals(this.currentFontName))
                 || (size != this.currentFontSize)) {
@@ -1646,6 +1350,12 @@ public class PDFRenderer extends PrintRenderer {
         putImage(url, pos);
     }
 
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void drawImage(String url, Rectangle2D pos) {
+        endTextObject();
+        putImage(url, pos);
+    }
+    
     /**
      * Adds a PDF XObject (a bitmap) to the PDF that will later be referenced.
      * @param url URL of the bitmap
@@ -1791,32 +1501,6 @@ public class PDFRenderer extends PrintRenderer {
 
     }
 
-    /**
-     * Render an inline viewport.
-     * This renders an inline viewport by clipping if necessary.
-     * @param viewport the viewport to handle
-     */
-    public void renderViewport(Viewport viewport) {
-
-        float x = currentIPPosition / 1000f;
-        float y = (currentBPPosition + viewport.getOffset()) / 1000f;
-        float width = viewport.getIPD() / 1000f;
-        float height = viewport.getBPD() / 1000f;
-        // TODO: Calculate the border rect correctly. 
-        drawBackAndBorders(viewport, x, y, width, height);
-
-        if (viewport.getClip()) {
-            saveGraphicsState();
-
-            clip(x, y, width, height);
-        }
-        super.renderViewport(viewport);
-
-        if (viewport.getClip()) {
-            restoreGraphicsState();
-        }
-    }
-
     /**
      * Render leader area.
      * This renders a leader area which is an area with a rule.
index d970194c4eaad569838993ef2f5e9037b3d159b2..2c684d5b9cca5c1bbc65cd7e6498ebbf0f8eb437 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@ import org.apache.fop.fonts.FontSetup;
  * <tt>PSGraphics2D</tt>.
  *
  * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
  * @version $Id$
  * @see org.apache.fop.render.ps.PSGraphics2D
  */
@@ -218,15 +217,14 @@ public abstract class AbstractPSDocumentGraphics2D extends PSGraphics2D {
         }
           
         writePageHeader();
-        gen.writeln("0.001 0.001 scale");
         if ((this.viewportWidth != this.width 
                 || this.viewportHeight != this.height)
                 && (this.viewportWidth > 0) && (this.viewportHeight > 0)){
             gen.concatMatrix(this.width / this.viewportWidth, 0, 
                        0, -1 * (this.height / this.viewportHeight), 
-                       0, this.height * 1000);
+                       0, this.height);
         } else {
-            gen.concatMatrix(1, 0, 0, -1, 0, this.height * 1000);
+            gen.concatMatrix(1, 0, 0, -1, 0, this.height);
         }
         gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
         this.pagePending = true;
index f7cf64c4b459d0042dcc8f1f6547cd2c7a7d9c87..9a21cb0dbe9ea58647e61875bfa2ff3dc064a431 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
  
 package org.apache.fop.render.ps;
 
+import java.awt.Color;
 import java.awt.geom.AffineTransform;
 import java.io.OutputStream;
 import java.io.IOException;
@@ -32,8 +33,7 @@ import java.util.Stack;
  * This class is used to output PostScript code to an OutputStream.
  *
  * @author <a href="mailto:fop-dev@xml.apache.org">Apache XML FOP Development Team</a>
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
- * @version $Id: PSGenerator.java,v 1.3 2003/03/07 09:46:30 jeremias Exp $
+ * @version $Id$
  */
 public class PSGenerator {
 
@@ -49,8 +49,8 @@ public class PSGenerator {
     
     private Stack graphicsStateStack = new Stack();
     private PSState currentState;
-    private DecimalFormat df3 = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US));
-    private DecimalFormat df1 = new DecimalFormat("0.#", new DecimalFormatSymbols(Locale.US));
+    //private DecimalFormat df3 = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US));
+    private DecimalFormat df3 = new DecimalFormat("0.###", new DecimalFormatSymbols(Locale.US));
     private DecimalFormat df5 = new DecimalFormat("0.#####", new DecimalFormatSymbols(Locale.US));
 
     private StringBuffer tempBuffer = new StringBuffer(256);
@@ -59,7 +59,7 @@ public class PSGenerator {
     public PSGenerator(OutputStream out) {
         this.out = out;
         this.currentState = new PSState();
-        this.graphicsStateStack.push(this.currentState);
+        //this.graphicsStateStack.push(this.currentState);
     }
     
     /**
@@ -72,13 +72,13 @@ public class PSGenerator {
 
     /**
      * Returns the selected PostScript level. 
-     * (Hardcoded to level 3 for the moment.)
+     * (Hardcoded to level 2 for the moment.)
      * @return the PostScript level
      */
     public int getPSLevel() {
-        return 3
+        return 2
     }
-
+    
     /**
      * Writes a newline character to the OutputStream.
      * 
@@ -95,7 +95,7 @@ public class PSGenerator {
      * @return the formatted value
      */
     public String formatDouble(double value) {
-        return df1.format(value);
+        return df3.format(value);
     }
 
     /**
@@ -327,20 +327,35 @@ public class PSGenerator {
     public void saveGraphicsState() throws IOException {
         writeln("gsave");
         
-        PSState state = (PSState)this.currentState.clone();
+        PSState state = new PSState(this.currentState, false);
         this.graphicsStateStack.push(this.currentState);
         this.currentState = state;
     }
     
     /** 
      * Restores the last graphics state of the rendering engine.
+     * @return true if the state was restored, false if there's a stack underflow.
      * @exception IOException In case of an I/O problem
      */
-    public void restoreGraphicsState() throws IOException {
-        writeln("grestore");
-        this.currentState = (PSState)this.graphicsStateStack.pop();
+    public boolean restoreGraphicsState() throws IOException {
+        if (this.graphicsStateStack.size() > 0) {
+            writeln("grestore");
+            this.currentState = (PSState)this.graphicsStateStack.pop();
+            return true;
+        } else {
+            return false;
+        }
     }
     
+    
+    /**
+     * Returns the current graphics state.
+     * @return the current graphics state
+     */
+    public PSState getCurrentState() {
+        return this.currentState;
+    }
+
     /**
      * Concats the transformation matrix.
      * @param a A part
@@ -352,14 +367,11 @@ public class PSGenerator {
      * @exception IOException In case of an I/O problem
      */
     public void concatMatrix(double a, double b,
-                                double c, double d, 
-                                double e, double f) throws IOException {
-        writeln("[" + formatDouble5(a) + " "
-                    + formatDouble5(b) + " "
-                    + formatDouble5(c) + " "
-                    + formatDouble5(d) + " "
-                    + formatDouble5(e) + " "
-                    + formatDouble5(f) + "] concat");
+            double c, double d, 
+            double e, double f) throws IOException {
+        AffineTransform at = new AffineTransform(a, b, c, d, e, f);
+        concatMatrix(at);
+        
     }
     
     /**
@@ -381,7 +393,13 @@ public class PSGenerator {
     public void concatMatrix(AffineTransform at) throws IOException {
         double[] matrix = new double[6];
         at.getMatrix(matrix);
-        concatMatrix(matrix);                   
+        getCurrentState().concatMatrix(at);
+        writeln("[" + formatDouble5(matrix[0]) + " "
+                + formatDouble5(matrix[1]) + " "
+                + formatDouble5(matrix[2]) + " "
+                + formatDouble5(matrix[3]) + " "
+                + formatDouble5(matrix[4]) + " "
+                + formatDouble5(matrix[5]) + "] concat");
     }
                
     /**
@@ -400,18 +418,76 @@ public class PSGenerator {
             + " " + formatDouble(h) 
             + " re");
     }
+    
+    /**
+     * Establishes the specified line cap style.
+     * @param linecap the line cap style (0, 1 or 2) as defined by the setlinecap command.
+     * @exception IOException In case of an I/O problem
+     */
+    public void useLineCap(int linecap) throws IOException {
+        if (getCurrentState().useLineCap(linecap)) {
+            writeln(linecap + " setlinecap");
+        }
+    }
                                 
     /**
-     * Returns the current graphics state.
-     * @return the current graphics state
+     * Establishes the specified line width.
+     * @param width the line width as defined by the setlinewidth command.
+     * @exception IOException In case of an I/O problem
      */
-    public PSState getCurrentState() {
-        return this.currentState;
+    public void useLineWidth(double width) throws IOException {
+        if (getCurrentState().useLineWidth(width)) {
+            writeln(formatDouble(width) + " setlinewidth");
+        }
+    }
+                                
+    /**
+     * Establishes the specified dash pattern.
+     * @param pattern the dash pattern as defined by the setdash command.
+     * @exception IOException In case of an I/O problem
+     */
+    public void useDash(String pattern) throws IOException {
+        if (pattern == null) {
+            pattern = PSState.DEFAULT_DASH;
+        }
+        if (getCurrentState().useDash(pattern)) {
+            writeln(pattern + " setdash");
+        }
+    }
+                                
+    /**
+     * Establishes the specified color (RGB).
+     * @param col the color as defined by the setrgbcolor command.
+     * @exception IOException In case of an I/O problem
+     */
+    public void useRGBColor(Color col) throws IOException {
+        if (col == null) {
+            col = PSState.DEFAULT_RGB_COLOR;
+        }
+        if (getCurrentState().useColor(col)) {
+            float[] comps = col.getColorComponents(null);
+            writeln(formatDouble(comps[0])
+                    + " " + formatDouble(comps[1])
+                    + " " + formatDouble(comps[2])
+                    + " setrgbcolor");
+        }
     }
-
     
+    /**
+     * Establishes the specified font and size.
+     * @param name name of the font for the "F" command (see FOP Std Proc Set)
+     * @param size size of the font
+     * @exception IOException In case of an I/O problem
+     */
+    public void useFont(String name, float size) throws IOException {
+        if (getCurrentState().useFont(name, size)) {
+            writeln(name + " " + formatDouble(size) + " F");
+        }
+    }
+
     /** Used for the ATEND constant. See there. */
     private static interface AtendIndicator {
     }
 
+
 }
index 7959ce43f22b679f1b1a99234c7cf0ee0383da53..be0c50aa63c5ea36d874d7be92b462e04d81f847 100644 (file)
@@ -73,18 +73,20 @@ import org.apache.fop.image.FopImage;
  * implementing a <tt>Graphic2D</tt> piece-meal.
  *
  * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
- * @version $Id: PSGraphics2D.java,v 1.11 2003/03/11 08:42:24 jeremias Exp $
+ * @version $Id$
  * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
  */
 public class PSGraphics2D extends AbstractGraphics2D {
 
+    private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
+    
     /** the logger for this class */
     protected Log log = LogFactory.getLog(PSTextPainter.class);
 
     /** the PostScript generator being created */
     protected PSGenerator gen;
 
-    private boolean clippingDisabled = true;
+    private boolean clippingDisabled = false;
 
     /** Currently valid FontState */
     protected Font font;
@@ -279,8 +281,8 @@ public class PSGraphics2D extends AbstractGraphics2D {
             Shape imclip = getClip();
             writeClip(imclip);
             gen.concatMatrix(at);
-            PSImageUtils.renderFopImage(fopimg, 
-                1000 * x, 1000 * y, 1000 * width, 1000 * height, gen);
+            PSImageUtils.renderBitmapImage(fopimg, 
+                x, y, width, height, gen);
             gen.restoreGraphicsState();
         } catch (IOException ioe) {
             handleIOException(ioe);
@@ -396,12 +398,12 @@ public class PSGraphics2D extends AbstractGraphics2D {
 
         /** @see org.apache.fop.image.FopImage#getIntrinsicWidth() */
         public int getIntrinsicWidth() {
-            return (int)(getWidth() * 72000 / getHorizontalResolution());
+            return (int)(getWidth() * 72 / getHorizontalResolution());
         }
 
         /** @see org.apache.fop.image.FopImage#getIntrinsicHeight() */
         public int getIntrinsicHeight() {
-            return (int)(getHeight() * 72000 / getVerticalResolution());
+            return (int)(getHeight() * 72 / getVerticalResolution());
         }
 
         /** @see org.apache.fop.image.FopImage#getHorizontalResolution() */
@@ -503,29 +505,29 @@ public class PSGraphics2D extends AbstractGraphics2D {
             int type = iter.currentSegment(vals);
             switch (type) {
             case PathIterator.SEG_CUBICTO:
-                gen.writeln(gen.formatDouble(1000 * vals[0]) + " "
-                                 + gen.formatDouble(1000 * vals[1]) + " "
-                                 + gen.formatDouble(1000 * vals[2]) + " "
-                                 + gen.formatDouble(1000 * vals[3]) + " "
-                                 + gen.formatDouble(1000 * vals[4]) + " "
-                                 + gen.formatDouble(1000 * vals[5])
+                gen.writeln(gen.formatDouble(vals[0]) + " "
+                                 + gen.formatDouble(vals[1]) + " "
+                                 + gen.formatDouble(vals[2]) + " "
+                                 + gen.formatDouble(vals[3]) + " "
+                                 + gen.formatDouble(vals[4]) + " "
+                                 + gen.formatDouble(vals[5])
                                  + " curveto");
                 break;
             case PathIterator.SEG_LINETO:
-                gen.writeln(gen.formatDouble(1000 * vals[0]) + " "
-                                 + gen.formatDouble(1000 * vals[1])
+                gen.writeln(gen.formatDouble(vals[0]) + " "
+                                 + gen.formatDouble(vals[1])
                                  + " lineto");
                 break;
             case PathIterator.SEG_MOVETO:
-                gen.writeln(gen.formatDouble(1000 * vals[0]) + " "
-                                 + gen.formatDouble(1000 * vals[1])
+                gen.writeln(gen.formatDouble(vals[0]) + " "
+                                 + gen.formatDouble(vals[1])
                                  + " M");
                 break;
             case PathIterator.SEG_QUADTO:
-                gen.writeln(gen.formatDouble(1000 * vals[0]) + " " 
-                          + gen.formatDouble(1000 * vals[1]) + " " 
-                          + gen.formatDouble(1000 * vals[2]) + " " 
-                          + gen.formatDouble(1000 * vals[3]) + " QUADTO ");
+                gen.writeln(gen.formatDouble(vals[0]) + " " 
+                          + gen.formatDouble(vals[1]) + " " 
+                          + gen.formatDouble(vals[2]) + " " 
+                          + gen.formatDouble(vals[3]) + " QUADTO ");
                 break;
             case PathIterator.SEG_CLOSE:
                 gen.writeln("closepath");
@@ -557,15 +559,23 @@ public class PSGraphics2D extends AbstractGraphics2D {
         preparePainting();
         try {
             gen.saveGraphicsState();
+            
+            AffineTransform trans = getTransform();
+            boolean newTransform = gen.getCurrentState().checkTransform(trans)
+                    && !trans.isIdentity();
+
             Shape imclip = getClip();
             writeClip(imclip);
+            if (newTransform) {
+                gen.concatMatrix(trans);
+            }
             establishColor(getColor());
 
             applyPaint(getPaint(), false);
             applyStroke(getStroke());
 
             gen.writeln("newpath");
-            PathIterator iter = s.getPathIterator(getTransform());
+            PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
             processPathIterator(iter);
             doDrawing(false, true, false);
             gen.restoreGraphicsState();
@@ -589,7 +599,7 @@ public class PSGraphics2D extends AbstractGraphics2D {
                 PathIterator iter = s.getPathIterator(getTransform());
                 processPathIterator(iter);
                 // clip area
-                gen.writeln("clippath");
+                gen.writeln("clip");
             } catch (IOException ioe) {
                 handleIOException(ioe);
             }
@@ -624,14 +634,14 @@ public class PSGraphics2D extends AbstractGraphics2D {
                 if (da != null) {
                     gen.write("[");
                     for (int count = 0; count < da.length; count++) {
-                        gen.write("" + (1000 * (int)da[count]));
+                        gen.write("" + ((int)da[count]));
                         if (count < da.length - 1) {
                             gen.write(" ");
                         }
                     }
                     gen.write("] ");
                     float offset = bs.getDashPhase();
-                    gen.writeln((1000 * (int)offset) + " setdash");
+                    gen.writeln(((int)offset) + " setdash");
                 }
                 int ec = bs.getEndCap();
                 switch (ec) {
@@ -661,10 +671,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
                 default: log.warn("Unsupported line join: " + lj);
                 }
                 float lw = bs.getLineWidth();
-                gen.writeln(gen.formatDouble(1000 * lw) + " setlinewidth");
+                gen.writeln(gen.formatDouble(lw) + " setlinewidth");
 
                 float ml = bs.getMiterLimit();
-                gen.writeln(gen.formatDouble(1000 * ml) + " setmiterlimit");
+                gen.writeln(gen.formatDouble(ml) + " setmiterlimit");
             }
         } catch (IOException ioe) {
             handleIOException(ioe);
@@ -857,9 +867,9 @@ public class PSGraphics2D extends AbstractGraphics2D {
 
             //Prepare correct transformation
             AffineTransform trans = getTransform();
-            gen.writeln("[" + toArray(trans) + "] concat"); 
-            gen.writeln(gen.formatDouble(1000 * x) + " "
-                      + gen.formatDouble(1000 * y) + " moveto ");
+            gen.concatMatrix(trans);
+            gen.writeln(gen.formatDouble(x) + " "
+                      + gen.formatDouble(y) + " moveto ");
             gen.writeln("1 -1 scale");
       
             StringBuffer sb = new StringBuffer("(");
@@ -874,22 +884,6 @@ public class PSGraphics2D extends AbstractGraphics2D {
         }
     }
 
-    /**
-     * Converts an AffineTransform to a value array.
-     * @param at AffineTransform to convert
-     * @return a String (array of six space-separated values)
-     */
-    protected String toArray(AffineTransform at) {
-        final double[] vals = new double[6];
-        at.getMatrix(vals);
-        return gen.formatDouble5(vals[0]) + " " 
-                + gen.formatDouble5(vals[1]) + " " 
-                + gen.formatDouble5(vals[2]) + " "   
-                + gen.formatDouble5(vals[3]) + " "   
-                + gen.formatDouble(1000 * vals[4]) + " "   
-                + gen.formatDouble(1000 * vals[5]); 
-    }
-
     private void escapeText(final String text, StringBuffer target) {
         final int l = text.length();
         for (int i = 0; i < l; i++) {
index 7edc340ada33045631f7d3d2414c878fbee4d0f5..ebc67a4dff8271edfc2d7ab856f899da60336564 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,20 +22,29 @@ import java.awt.color.ColorSpace;
 import java.io.IOException;
 import java.io.OutputStream;
 
+import org.apache.commons.io.output.CountingOutputStream;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.image.EPSImage;
 import org.apache.fop.image.FopImage;
 import org.apache.fop.image.JpegImage;
+import org.apache.fop.image.XMLImage;
 import org.apache.fop.util.ASCII85OutputStream;
 import org.apache.fop.util.Finalizable;
 import org.apache.fop.util.FlateEncodeOutputStream;
 import org.apache.fop.util.RunLengthEncodeOutputStream;
+import org.w3c.dom.Document;
 
 /**
  * Utility code for rendering images in PostScript. 
  */
 public class PSImageUtils {
 
+    /** logging instance */
+    protected static Log log = LogFactory.getLog(PSImageUtils.class);
+
     /**
-     * Renders an image to PostScript.
+     * Renders a bitmap image to PostScript.
      * @param img image to render
      * @param x x position
      * @param y y position
@@ -44,12 +53,26 @@ public class PSImageUtils {
      * @param gen PS generator
      * @throws IOException In case of an I/O problem while rendering the image
      */
-    public static void renderFopImage(FopImage img, int x, int y, int w, int h, PSGenerator gen) throws IOException {
+    public static void renderBitmapImage(FopImage img, 
+                float x, float y, float w, float h, PSGenerator gen)
+                    throws IOException {
+        if (img instanceof JpegImage) {
+            if (!img.load(FopImage.ORIGINAL_DATA)) {
+                gen.commentln("%JPEG image could not be processed: " + img);
+                return;
+            }
+        } else {
+            if (!img.load(FopImage.BITMAP)) {
+                gen.commentln("%Bitmap image could not be processed: " + img);
+                return;
+            }
+        }
         boolean iscolor = img.getColorSpace().getType()
                           != ColorSpace.CS_GRAY;
         byte[] imgmap = img.getBitmaps();
 
         gen.saveGraphicsState();
+        gen.commentln("%FOPBeginBitmap: " + img.getMimeType());
         if (img.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
             gen.writeln("/DeviceCMYK setcolorspace");
         } else if (img.getColorSpace().getType() == ColorSpace.CS_GRAY) {
@@ -108,22 +131,6 @@ public class PSImageUtils {
         gen.writeln("  RawData flushfile");
         gen.writeln("} exec");
 
-        /*
-         * for (int y=0; y<img.getHeight(); y++) {
-         * int indx = y * img.getWidth();
-         * if (iscolor) indx*= 3;
-         * for (int x=0; x<img.getWidth(); x++) {
-         * if (iscolor) {
-         * writeASCIIHex(imgmap[indx++] & 0xFF);
-         * writeASCIIHex(imgmap[indx++] & 0xFF);
-         * writeASCIIHex(imgmap[indx++] & 0xFF);
-         * } else {
-         * writeASCIIHex(imgmap[indx++] & 0xFF);
-         * }
-         * }
-         * }
-         */
-
         OutputStream out = gen.getOutputStream();
         out = new ASCII85OutputStream(out);
         if (img instanceof JpegImage) {
@@ -143,8 +150,70 @@ public class PSImageUtils {
         }
 
         gen.writeln("");
+        gen.commentln("%FOPEndBitmap");
         gen.restoreGraphicsState();
     }
 
+    public static void renderEPS(EPSImage img, 
+            float x, float y, float w, float h,
+            PSGenerator gen) {
+        try {
+            if (!img.load(FopImage.ORIGINAL_DATA)) {
+                gen.commentln("%EPS image could not be processed: " + img);
+                return;
+            }
+            int[] bbox = img.getBBox();
+            int bboxw = bbox[2] - bbox[0];
+            int bboxh = bbox[3] - bbox[1];
+            renderEPS(img.getEPSImage(), img.getDocName(),
+                x, y, w, h,
+                bbox[0], bbox[1], bboxw, bboxh, gen);
+
+        } catch (Exception e) {
+            log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
+                                   + e.getMessage() + ")", e);
+        }
+    }
+
+    /**
+     * Places an EPS file in the PostScript stream.
+     * @param rawEPS byte array containing the raw EPS data
+     * @param name name for the EPS document
+     * @param x x-coordinate of viewport in millipoints
+     * @param y y-coordinate of viewport in millipoints
+     * @param w width of viewport in millipoints
+     * @param h height of viewport in millipoints
+     * @param bboxx x-coordinate of EPS bounding box in points
+     * @param bboxy y-coordinate of EPS bounding box in points
+     * @param bboxw width of EPS bounding box in points
+     * @param bboxh height of EPS bounding box in points
+     * @throws IOException in case an I/O error happens during output
+     */
+    public static void renderEPS(byte[] rawEPS, String name,
+                    float x, float y, float w, float h,
+                    int bboxx, int bboxy, int bboxw, int bboxh,
+                    PSGenerator gen) throws IOException {
+        gen.writeln("BeginEPSF");
+        gen.writeln("%%BeginDocument: " + name);
+
+        gen.writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " translate");
+        gen.writeln("0 " + gen.formatDouble(h) + " translate");
+        gen.writeln("1 -1 scale");
+        float sx = w / bboxw;
+        float sy = h / bboxh;
+        if (sx != 1 || sy != 1) {
+            gen.writeln(gen.formatDouble(sx) + " " + gen.formatDouble(sy) + " scale");
+        }
+        if (bboxx != 0 || bboxy != 0) {
+            gen.writeln(gen.formatDouble(-bboxx) + " " + gen.formatDouble(-bboxy) + " translate");
+        }
+        gen.writeln(gen.formatDouble(bboxy) + " " + gen.formatDouble(bboxy) 
+                + " " + gen.formatDouble(bboxw) + " " + gen.formatDouble(bboxh) + " re clip");
+        gen.writeln("newpath");
+        gen.writeByteArr(rawEPS);
+        gen.writeln("%%EndDocument");
+        gen.writeln("EndEPSF");
+        gen.writeln("");
+    }
 
 }
index 1a360990cc23f6984d5700dcf0574c1beb2d4552..7413f77176744d846e8a3dcd684dadfe43e36d79 100644 (file)
 package org.apache.fop.render.ps;
 
 // Java
+import java.awt.Color;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Iterator;
 import java.util.List;
 
 // FOP
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.RegionViewport;
 import org.apache.fop.apps.FOPException;
-import org.apache.fop.area.Block;
+import org.apache.fop.area.Area;
 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.ForeignObject;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.InlineParent;
 import org.apache.fop.area.inline.TextArea;
 import org.apache.fop.datatypes.ColorType;
 import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fonts.FontSetup;
 import org.apache.fop.fonts.Typeface;
-import org.apache.fop.render.PrintRenderer;
-import org.apache.fop.render.RendererContext;
-
+import org.apache.fop.image.EPSImage;
 import org.apache.fop.image.FopImage;
 import org.apache.fop.image.ImageFactory;
-import org.apache.fop.traits.BorderProps;
+import org.apache.fop.image.XMLImage;
+import org.apache.fop.render.AbstractPathOrientedRenderer;
+import org.apache.fop.render.RendererContext;
 
 import org.w3c.dom.Document;
 
@@ -62,14 +66,14 @@ import org.w3c.dom.Document;
  * sure to also follow the DSC to make it simpler to programmatically modify
  * the generated Postscript files (ex. extract pages etc.).
  * <br>
- * The PS renderer operates in millipoints as the layout engine. Since PostScript
- * initially uses points, scaling is applied as needed.
+ * This renderer inserts FOP-specific comments into the PostScript stream which
+ * may help certain users to do certain types of post-processing of the output.
+ * These comments all start with "%FOP". 
  *
  * @author <a href="mailto:fop-dev@xml.apache.org">Apache XML FOP Development Team</a>
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
- * @version $Id: PSRenderer.java,v 1.31 2003/03/11 08:42:24 jeremias Exp $
+ * @version $Id$
  */
-public class PSRenderer extends PrintRenderer {
+public class PSRenderer extends AbstractPathOrientedRenderer {
 
     /** The MIME type for PostScript */
     public static final String MIME_TYPE = "application/postscript";
@@ -84,11 +88,7 @@ public class PSRenderer extends PrintRenderer {
     protected PSGenerator gen;
     private boolean ioTrouble = false;
 
-    private String currentFontName;
-    private int currentFontSize;
-    private float currRed;
-    private float currGreen;
-    private float currBlue;
+    private boolean inTextMode = false;
 
     /**
      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
@@ -144,7 +144,11 @@ public class PSRenderer extends PrintRenderer {
      */
     protected void comment(String comment) {
         if (this.enableComments) {
-            writeln(comment);
+            if (comment.startsWith("%")) {
+                writeln(comment);
+            } else {
+                writeln("%" + comment);
+            }
         }
     }
 
@@ -155,17 +159,129 @@ public class PSRenderer extends PrintRenderer {
         moveTo(this.currentIPPosition, this.currentBPPosition);
     }
 
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() */
+    protected void clip() {
+        writeln("clip newpath");
+        //writeln("newpath");
+    }
+    
     /**
-     * Moves the cursor.
-     * @param x X coordinate
-     * @param y Y coordinate
+     * 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
      */
-    protected void moveTo(int x, int y) {
-        writeln(x + " " + y + " M");
+    protected void clipRect(float x, float y, float width, float height) {
+        try {
+            gen.defineRect(x, y, width, height);
+            gen.writeln("clip");
+            //comment("clip here");
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+        }
     }
 
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) */
+    protected void moveTo(float x, float y) {
+        writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " M");
+    }
+    
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) */
+    protected void lineTo(float x, float y) {
+        writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " lineto");
+    }
+    
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() */
+    protected void closePath() {
+        writeln("cp");
+    }
+    
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void fillRect(float x, float y, float width, float height) {
+        try {
+            gen.defineRect(x, y, width, height);
+            gen.writeln("fill");
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+        }
+    }
+
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void updateColor(ColorType col, boolean fill) {
+        try {
+            useColor(col);
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+        }
+    }
+
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void drawImage(String url, Rectangle2D pos) {
+        endTextObject();
+        url = ImageFactory.getURL(url);
+        ImageFactory fact = ImageFactory.getInstance();
+        FopImage fopimage = fact.getImage(url, userAgent);
+        if (fopimage == null) {
+            return;
+        }
+        if (!fopimage.load(FopImage.DIMENSIONS)) {
+            return;
+        }
+        float x = (float)pos.getX() / 1000f;
+        x += currentIPPosition / 1000f;
+        float y = (float)pos.getY() / 1000f;
+        y += currentBPPosition / 1000f;
+        float w = (float)pos.getWidth() / 1000f;
+        float h = (float)pos.getHeight() / 1000f;
+        try {
+            String mime = fopimage.getMimeType();
+            if ("text/xml".equals(mime)) {
+                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                    return;
+                }
+                Document doc = ((XMLImage) fopimage).getDocument();
+                String ns = ((XMLImage) fopimage).getNameSpace();
+
+                renderDocument(doc, ns, pos);
+            } else if ("image/svg+xml".equals(mime)) {
+                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                    return;
+                }
+                Document doc = ((XMLImage) fopimage).getDocument();
+                String ns = ((XMLImage) fopimage).getNameSpace();
+
+                renderDocument(doc, ns, pos);
+            } else if (fopimage instanceof EPSImage) {
+                PSImageUtils.renderEPS((EPSImage)fopimage, x, y, w, h, gen);
+            } else {
+                PSImageUtils.renderBitmapImage(fopimage, x, y, w, h, gen);
+            }
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+        }
+    }
+
+    /**
+     * Draw a line.
+     *
+     * @param startx the start x position
+     * @param starty the start y position
+     * @param endx the x end position
+     * @param endy the y end position
+     */
+    private void drawLine(float startx, float starty, float endx, float endy) {
+        writeln(gen.formatDouble(startx) + " " 
+                + gen.formatDouble(starty) + " M " 
+                + gen.formatDouble(endx) + " " 
+                + gen.formatDouble(endy) + " lineto stroke newpath");
+    }
+    
     /** Saves the graphics state of the rendering engine. */
     public void saveGraphicsState() {
+        endTextObject();
         try {
             //delegate
             gen.saveGraphicsState();
@@ -176,6 +292,7 @@ public class PSRenderer extends PrintRenderer {
 
     /** Restores the last graphics state of the rendering engine. */
     public void restoreGraphicsState() {
+        endTextObject();
         try {
             //delegate
             gen.restoreGraphicsState();
@@ -186,12 +303,18 @@ public class PSRenderer extends PrintRenderer {
 
     /** Indicates the beginning of a text object. */
     protected void beginTextObject() {
-        writeln("BT");
+        if (!inTextMode) {
+            writeln("BT");
+            inTextMode = true;
+        }
     }
 
     /** Indicates the end of a text object. */
     protected void endTextObject() {
-        writeln("ET");
+        if (inTextMode) {
+            writeln("ET");
+            inTextMode = false;
+        }
     }
 
     /**
@@ -225,81 +348,194 @@ public class PSRenderer extends PrintRenderer {
         }
     }
 
-    /**
-     * Draws a filled rectangle.
-     * @param x x-coordinate
-     * @param y y-coordinate
-     * @param w width
-     * @param h height
-     * @param col color to fill with
-     */
-    protected void fillRect(float x, float y, float w, float h,
-                                 ColorType col) {
-        useColor(col);
-        writeln(gen.formatDouble(x) 
-            + " " + gen.formatDouble(y) 
-            + " " + gen.formatDouble(w) 
-            + " " + gen.formatDouble(h) 
-            + " rectfill");
-    }
-
-    /**
-     * Draws a stroked rectangle with the current stroke settings.
-     * @param x x-coordinate
-     * @param y y-coordinate
-     * @param w width
-     * @param h height
-     */
-    protected void drawRect(float x, float y, float w, float h) {
-        writeln(gen.formatDouble(x) 
-            + " " + gen.formatDouble(y) 
-            + " " + gen.formatDouble(w) 
-            + " " + gen.formatDouble(h) 
-            + " rectstroke");
-    }
-
-    /**
-     * 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
-     */
-    protected void clip(float x, float y, float width, float height) {
-        writeln(x + " " + y + " " + width + " " + height + " rectclip");
-    }
-
     /**
      * Changes the currently used font.
      * @param name name of the font
      * @param size font size
      */
     public void useFont(String name, int size) {
-        if ((currentFontName != name) || (currentFontSize != size)) {
-            writeln(name + " " + size + " F");
-            currentFontName = name;
-            currentFontSize = size;
+        try {
+            gen.useFont(name, size / 1000f);
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
         }
     }
 
-    private void useColor(ColorType col) {
-        useColor(col.getRed(), col.getGreen(), col.getBlue());
+    private void useColor(ColorType col) throws IOException {
+        gen.useRGBColor(toColor(col));
     }
 
-    private void useColor(float red, float green, float blue) {
-        if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
-            writeln(gen.formatDouble(red)
-                + " " + gen.formatDouble(green)
-                + " " + gen.formatDouble(blue)
-                + " setrgbcolor");
-            currRed = red;
-            currGreen = green;
-            currBlue = blue;
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void drawBackAndBorders(Area area, float startx, float starty,
+            float width, float height) {
+        if (area.hasTrait(Trait.BACKGROUND)
+                || area.hasTrait(Trait.BORDER_BEFORE)
+                || area.hasTrait(Trait.BORDER_AFTER)
+                || area.hasTrait(Trait.BORDER_START)
+                || area.hasTrait(Trait.BORDER_END)) {
+            comment("%FOPBeginBackgroundAndBorder: " 
+                    + startx + " " + starty + " " + width + " " + height);
+            super.drawBackAndBorders(area, startx, starty, width, height);
+            comment("%FOPEndBackgroundAndBorder"); 
         }
     }
-
+    
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void drawBorderLine(float x1, float y1, float x2, float y2, 
+            boolean horz, boolean startOrBefore, int style, ColorType col) {
+        try {
+            float w = x2 - x1;
+            float h = y2 - y1;
+            if ((w < 0) || (h < 0)) {
+                log.error("Negative extent received. Border won't be painted.");
+                return;
+            }
+            switch (style) {
+                case Constants.EN_DASHED: 
+                    useColor(col);
+                    if (horz) {
+                        float unit = Math.abs(2 * h);
+                        int rep = (int)(w / unit);
+                        if (rep % 2 == 0) {
+                            rep++;
+                        }
+                        unit = w / rep;
+                        gen.useDash("[" + unit + "] 0");
+                        gen.useLineCap(0);
+                        gen.useLineWidth(h);
+                        float ym = y1 + (h / 2);
+                        drawLine(x1, ym, x2, ym);
+                    } else {
+                        float unit = Math.abs(2 * w);
+                        int rep = (int)(h / unit);
+                        if (rep % 2 == 0) {
+                            rep++;
+                        }
+                        unit = h / rep;
+                        gen.useDash("[" + unit + "] 0");
+                        gen.useLineCap(0);
+                        gen.useLineWidth(w);
+                        float xm = x1 + (w / 2);
+                        drawLine(xm, y1, xm, y2);
+                    }
+                    break;
+                case Constants.EN_DOTTED:
+                    useColor(col);
+                    gen.useLineCap(1); //Rounded!
+                    if (horz) {
+                        float unit = Math.abs(2 * h);
+                        int rep = (int)(w / unit);
+                        if (rep % 2 == 0) {
+                            rep++;
+                        }
+                        unit = w / rep;
+                        gen.useDash("[0 " + unit + "] 0");
+                        gen.useLineWidth(h);
+                        float ym = y1 + (h / 2);
+                        drawLine(x1, ym, x2, ym);
+                    } else {
+                        float unit = Math.abs(2 * w);
+                        int rep = (int)(h / unit);
+                        if (rep % 2 == 0) {
+                            rep++;
+                        }
+                        unit = h / rep;
+                        gen.useDash("[0 " + unit + "] 0");
+                        gen.useLineWidth(w);
+                        float xm = x1 + (w / 2);
+                        drawLine(xm, y1, xm, y2);
+                    }
+                    break;
+                case Constants.EN_DOUBLE:
+                    useColor(col);
+                    gen.useDash(null);
+                    if (horz) {
+                        float h3 = h / 3;
+                        gen.useLineWidth(h3);
+                        float ym1 = y1 + (h3 / 2);
+                        float ym2 = ym1 + h3 + h3;
+                        drawLine(x1, ym1, x2, ym1);
+                        drawLine(x1, ym2, x2, ym2);
+                    } else {
+                        float w3 = w / 3;
+                        gen.useLineWidth(w3);
+                        float xm1 = x1 + (w3 / 2);
+                        float xm2 = xm1 + w3 + w3;
+                        drawLine(xm1, y1, xm1, y2);
+                        drawLine(xm2, y1, xm2, y2);
+                    }
+                    break;
+                case Constants.EN_GROOVE:
+                case Constants.EN_RIDGE:
+                    float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
+                    gen.useDash(null);
+                    Color c = toColor(col);
+                    if (horz) {
+                        Color uppercol = lightenColor(c, -colFactor);
+                        Color lowercol = lightenColor(c, colFactor);
+                        float h3 = h / 3;
+                        gen.useLineWidth(h3);
+                        float ym1 = y1 + (h3 / 2);
+                        gen.useRGBColor(uppercol);
+                        drawLine(x1, ym1, x2, ym1);
+                        gen.useRGBColor(c);
+                        drawLine(x1, ym1 + h3, x2, ym1 + h3);
+                        gen.useRGBColor(lowercol);
+                        drawLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3);
+                    } else {
+                        Color leftcol = lightenColor(c, -colFactor);
+                        Color rightcol = lightenColor(c, colFactor);
+                        float w3 = w / 3;
+                        gen.useLineWidth(w3);
+                        float xm1 = x1 + (w3 / 2);
+                        gen.useRGBColor(leftcol);
+                        drawLine(xm1, y1, xm1, y2);
+                        gen.useRGBColor(c);
+                        drawLine(xm1 + w3, y1, xm1 + w3, y2);
+                        gen.useRGBColor(rightcol);
+                        drawLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2);
+                    }
+                    break;
+                case Constants.EN_INSET:
+                case Constants.EN_OUTSET:
+                    colFactor = (style == EN_OUTSET ? 0.4f : -0.4f);
+                    gen.useDash(null);
+                    c = toColor(col);
+                    if (horz) {
+                        c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor);
+                        gen.useLineWidth(h);
+                        float ym1 = y1 + (h / 2);
+                        gen.useRGBColor(c);
+                        drawLine(x1, ym1, x2, ym1);
+                    } else {
+                        c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor);
+                        gen.useLineWidth(w);
+                        float xm1 = x1 + (w / 2);
+                        gen.useRGBColor(c);
+                        drawLine(xm1, y1, xm1, y2);
+                    }
+                    break;
+                case Constants.EN_HIDDEN:
+                    break;
+                default:
+                    useColor(col);
+                    gen.useDash(null);
+                    gen.useLineCap(0);
+                    if (horz) {
+                        gen.useLineWidth(h);
+                        float ym = y1 + (h / 2);
+                        drawLine(x1, ym, x2, ym);
+                    } else {
+                        gen.useLineWidth(w);
+                        float xm = x1 + (w / 2);
+                        drawLine(xm, y1, xm, y2);
+                    }
+            }
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+        }
+    }
+    
     /**
      * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
      */
@@ -402,8 +638,8 @@ public class PSRenderer extends PrintRenderer {
                 + Math.round(pspageheight) + "]");
         gen.writeln("/ImagingBBox null");
         gen.writeln(">> setpagedevice");
-        gen.writeln("0.001 0.001 scale");
-        concatMatrix(1, 0, 0, -1, 0, pageheight);
+        //gen.writeln("0.001 0.001 scale");
+        concatMatrix(1, 0, 0, -1, 0, pageheight / 1000f);
 
         gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
 
@@ -415,6 +651,15 @@ public class PSRenderer extends PrintRenderer {
         gen.writeDSCComment(DSCConstants.END_PAGE);
     }
 
+    /** @see org.apache.fop.render.AbstractRenderer */
+    protected void renderRegionViewport(RegionViewport port) {
+        if (port != null) {
+            comment("%FOPBeginRegionViewport: " + port.getRegionReference().getRegionName());
+            super.renderRegionViewport(port);
+            comment("%FOPEndRegionViewport");
+        }
+    }
+    
     /**
      * Paints text.
      * @param rx X coordinate
@@ -424,7 +669,9 @@ public class PSRenderer extends PrintRenderer {
      */
     protected void paintText(int rx, int bl, String text, Typeface font) {
         saveGraphicsState();
-        writeln("1 0 0 -1 " + rx + " " + bl + " Tm");
+        beginTextObject();
+        writeln("1 0 0 -1 " + gen.formatDouble(rx / 1000f) 
+                + " " + gen.formatDouble(bl / 1000f) + " Tm");
 
         int initialSize = text.length();
         initialSize += initialSize / 2;
@@ -457,7 +704,11 @@ public class PSRenderer extends PrintRenderer {
         useFont(fontname, fontsize);
         ColorType ct = (ColorType)area.getTrait(Trait.COLOR);
         if (ct != null) {
-            useColor(ct);
+            try {
+                useColor(ct);
+            } catch (IOException ioe) {
+                handleIOTrouble(ioe);
+            }
         }
         paintText(rx, bl, area.getTextArea(), f);
 
@@ -495,130 +746,58 @@ public class PSRenderer extends PrintRenderer {
         super.renderText(area); //Updates IPD
     }
 
-
-    /**
-     * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
-     */
-    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();
-
-        if (bv.getPositioning() == Block.ABSOLUTE) {
-
-            currentIPPosition = 0;
-            currentBPPosition = 0;
-
-            //closeText();
-            endTextObject();
-
-            if (bv.getClip()) {
-                saveGraphicsState();
-                int x = bv.getXOffset() + containingIPPosition;
-                int y = bv.getYOffset() + containingBPPosition;
-                int width = bv.getIPD();
-                int height = bv.getBPD();
-                clip(x, y, width, height);
-            }
-
-            CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
-            ctm = tempctm.multiply(ctm);
-
-            startVParea(ctm);
-            handleBlockTraits(bv);
-            renderBlocks(bv, children);
-            endVParea();
-
-            if (bv.getClip()) {
-                restoreGraphicsState();
-            }
-            beginTextObject();
-
-            // clip if necessary
-
-            currentIPPosition = saveIP;
-            currentBPPosition = saveBP;
-        } else {
-
-            if (ctm != null) {
-                currentIPPosition = 0;
-                currentBPPosition = 0;
-
-                //closeText();
-                endTextObject();
-
-                double[] vals = ctm.toArray();
-                //boolean aclock = vals[2] == 1.0;
-                if (vals[2] == 1.0) {
-                    ctm = ctm.translate(-saveBP - bv.getBPD(), -saveIP);
-                } else if (vals[0] == -1.0) {
-                    ctm = ctm.translate(-saveIP - bv.getIPD(), -saveBP - bv.getBPD());
-                } else {
-                    ctm = ctm.translate(saveBP, saveIP - bv.getIPD());
-                }
-            }
-
-            // clip if necessary
-            if (bv.getClip()) {
-                if (ctm == null) {
-                    //closeText();
-                    endTextObject();
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer#breakOutOfStateStack() */
+    protected List breakOutOfStateStack() {
+        try {
+            List breakOutList = new java.util.ArrayList();
+            PSState state;
+            while (true) {
+                if (breakOutList.size() == 0) {
+                    comment("------ break out!");
                 }
-                saveGraphicsState();
-                int x = bv.getXOffset();
-                int y = bv.getYOffset();
-                int width = bv.getIPD();
-                int height = bv.getBPD();
-                clip(x, y, width, height);
-            }
-
-            if (ctm != null) {
-                startVParea(ctm);
-            }
-            handleBlockTraits(bv);
-            renderBlocks(bv, children);
-            if (ctm != null) {
-                endVParea();
-            }
-
-            if (bv.getClip()) {
-                restoreGraphicsState();
-                if (ctm == null) {
-                    beginTextObject();
+                state = gen.getCurrentState();
+                if (!gen.restoreGraphicsState()) {
+                    break;
                 }
+                breakOutList.add(0, state); //Insert because of stack-popping
             }
-            if (ctm != null) {
-                beginTextObject();
+            return breakOutList;
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
+            return null;
+        }
+    }
+    
+    /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
+    protected void restoreStateStackAfterBreakOut(List breakOutList) {
+        try {
+            comment("------ restoring context after break-out...");
+            PSState state;
+            Iterator i = breakOutList.iterator();
+            while (i.hasNext()) {
+                state = (PSState)i.next();
+                saveGraphicsState();
+                state.reestablish(gen);
             }
-
-            currentIPPosition = saveIP;
-            currentBPPosition = saveBP;
-            currentBPPosition += (int)(bv.getAllocBPD());
+            comment("------ done.");
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
         }
-        currentFontName = saveFontName;
     }
-
+    
     /**
      * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
      */
     protected void startVParea(CTM ctm) {
-        // Set the given CTM in the graphics state
-        //currentState.push();
-        //currentState.setTransform(new AffineTransform(CTMHelper.toPDFArray(ctm)));
 
         saveGraphicsState();
         // multiply with current CTM
-        //writeln(CTMHelper.toPDFString(ctm) + " cm\n");
         final double[] matrix = ctm.toArray();
+        matrix[4] /= 1000f;
+        matrix[5] /= 1000f;
         concatMatrix(matrix);
 
         // Set clip?
-        beginTextObject();
     }
 
     /**
@@ -627,181 +806,33 @@ public class PSRenderer extends PrintRenderer {
     protected void endVParea() {
         endTextObject();
         restoreGraphicsState();
-        //currentState.pop();
     }
 
-    /**
-     * Handle the traits for a region
-     * This is used to draw the traits for the given page region.
-     * (See Sect. 6.4.1.2 of XSL-FO spec.)
-     * @param region the RegionViewport whose region is to be drawn
-     */
-    protected void handleRegionTraits(RegionViewport region) {
-        currentFontName = "";
-        float startx = 0;
-        float starty = 0;
-        Rectangle2D viewArea = region.getViewArea();
-        float width = (float)(viewArea.getWidth());
-        float height = (float)(viewArea.getHeight());
-        /*
-        Trait.Background back;
-        back = (Trait.Background)region.getTrait(Trait.BACKGROUND);
-        */
-        drawBackAndBorders(region, startx, starty, width, height);
+    /** @see org.apache.fop.render.AbstractRenderer */
+    protected void renderBlockViewport(BlockViewport bv, List children) {
+        comment("%FOPBeginBlockViewport: " + bv.toString());
+        super.renderBlockViewport(bv, children);
+        comment("%FOPEndBlockViewport");
     }
+    
+    /** @see org.apache.fop.render.AbstractRenderer */
+    protected void renderInlineParent(InlineParent ip) {
+        float start = currentIPPosition / 1000f;
+        float top = (ip.getOffset() + currentBPPosition) / 1000f;
+        float width = ip.getIPD() / 1000f;
+        float height = ip.getBPD() / 1000f;
+        drawBackAndBorders(ip, start, top, width, height);
 
-    /**
-     * Handle block traits.
-     * The block could be any sort of block with any positioning
-     * so this should render the traits such as border and background
-     * in its position.
-     *
-     * @param block the block to render the traits
-     */
-    protected void handleBlockTraits(Block block) {
-        float startx = currentIPPosition;
-        float starty = currentBPPosition;
-        drawBackAndBorders(block, startx, starty,
-                           block.getIPD(), block.getBPD());
+        super.renderInlineParent(ip);
     }
-
+    
     /**
-     * Draw the background and borders.
-     * This draws the background and border traits for an area given
-     * the position.
-     *
-     * @param block the area to get the traits from
-     * @param startx the start x position
-     * @param starty the start y position
-     * @param width the width of the area
-     * @param height the height of the area
+     * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
      */
-    protected void drawBackAndBorders(Area block,
-                                    float startx, float starty,
-                                    float width, float height) {
-        // draw background then border
-
-        boolean started = false;
-        Trait.Background back;
-        back = (Trait.Background)block.getTrait(Trait.BACKGROUND);
-        if (back != null) {
-            started = true;
-//            closeText();
-            endTextObject();
-            //saveGraphicsState();
-
-            if (back.getColor() != null) {
-                fillRect(startx, starty, width, height, back.getColor());
-            }
-            if (back.getURL() != null) {
-                ImageFactory fact = ImageFactory.getInstance();
-                FopImage fopimage = fact.getImage(back.getURL(), userAgent);
-                if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
-                    if (back.getRepeat() == EN_REPEAT) {
-                        // create a pattern for the image
-                    } else {
-                        // place once
-                        Rectangle2D pos;
-                        pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000,
-                                                    (starty + back.getVertical()) * 1000,
-                                                    fopimage.getWidth() * 1000,
-                                                    fopimage.getHeight() * 1000);
-                       // putImage(back.url, pos);
-                    }
-                }
-            }
-        }
-
-        BorderProps bps = (BorderProps)block.getTrait(Trait.BORDER_BEFORE);
-        if (bps != null) {
-            float endx = startx + width;
-
-            if (!started) {
-                started = true;
-//                closeText();
-                endTextObject();
-                //saveGraphicsState();
-            }
-
-            float bwidth = bps.width;
-            useColor(bps.color);
-            writeln(bwidth + " setlinewidth");
-
-            drawLine(startx, starty + bwidth / 2, endx, starty + bwidth / 2);
-        }
-        bps = (BorderProps)block.getTrait(Trait.BORDER_START);
-        if (bps != null) {
-            float endy = starty + height;
-
-            if (!started) {
-                started = true;
-//                closeText();
-                endTextObject();
-                //saveGraphicsState();
-            }
-
-            float bwidth = bps.width;
-            useColor(bps.color);
-            writeln(bwidth + " setlinewidth");
-
-            drawLine(startx + bwidth / 2, starty, startx + bwidth / 2, endy);
-        }
-        bps = (BorderProps)block.getTrait(Trait.BORDER_AFTER);
-        if (bps != null) {
-            float sy = starty + height;
-            float endx = startx + width;
-
-            if (!started) {
-                started = true;
-//                closeText();
-                endTextObject();
-                //saveGraphicsState();
-            }
-
-            float bwidth = bps.width;
-            useColor(bps.color);
-            writeln(bwidth + " setlinewidth");
-
-            drawLine(startx, sy - bwidth / 2, endx, sy - bwidth / 2);
-        }
-        bps = (BorderProps)block.getTrait(Trait.BORDER_END);
-        if (bps != null) {
-            float sx = startx + width;
-            float endy = starty + height;
-
-            if (!started) {
-                started = true;
- //               closeText();
-                endTextObject();
-                //saveGraphicsState();
-            }
-
-            float bwidth = bps.width;
-            useColor(bps.color);
-            writeln(bwidth + " setlinewidth");
-            drawLine(sx - bwidth / 2, starty, sx - bwidth / 2, endy);
-        }
-        if (started) {
-            //restoreGraphicsState();
-            beginTextObject();
-            // font last set out of scope in text section
-            currentFontName = "";
-        }
+    public void renderImage(Image image, Rectangle2D pos) {
+        drawImage(image.getURL(), pos);
     }
 
-    /**
-     * Draw a line.
-     *
-     * @param startx the start x position
-     * @param starty the start y position
-     * @param endx the x end position
-     * @param endy the y end position
-     */
-    private void drawLine(float startx, float starty, float endx, float endy) {
-        writeln(startx + " " + starty + " M ");
-        writeln(endx + " " + endy + " lineto");
-    }
-    
     /**
      * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
      */
index e39697c690b89c0d124ccadbdada1097d5e81989..7421c5f28e700b6df331f4bd0934389d52850c93 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  
 package org.apache.fop.render.ps;
 
+import java.io.IOException;
 import java.io.Serializable;
+import java.util.List;
+import java.awt.Color;
 import java.awt.geom.AffineTransform;
 
 /**
  * This class holds the current state of the PostScript interpreter.
  * 
  * @author <a href="mailto:fop-dev@xml.apache.org">Apache XML FOP Development Team</a>
- * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
- * @version $Id: PSState.java,v 1.2 2003/03/07 09:46:30 jeremias Exp $
+ * @version $Id$
  */
-public class PSState implements Serializable, Cloneable {
+public class PSState implements Serializable {
 
+    /** Default for setdash */
+    public static final String DEFAULT_DASH = "[] 0"; 
+    /** Default color in PostScript */
+    public static final Color DEFAULT_RGB_COLOR = Color.black; 
+    
     private AffineTransform transform = new AffineTransform();
+    private List transformConcatList = new java.util.ArrayList();
  
+    private int linecap = 0;
+    private double linewidth = 1.0f;
+    private String dashpattern = DEFAULT_DASH;
+    private Color rgbColor = DEFAULT_RGB_COLOR;
+    
+    //Font state
+    private String fontname;
+    private float fontsize;
+
+    /**
+     * Default constructor
+     */
+    public PSState() {
+        //nop
+    }
+
+    /**
+     * Copy constructor
+     * @param org the original to copy from
+     * @param copyTransforms true if the list of matrix concats should be cloned, too
+     */
+    public PSState(PSState org, boolean copyTransforms) {
+        this.transform = (AffineTransform)org.transform.clone();
+        if (copyTransforms) {
+            this.transformConcatList.addAll(org.transformConcatList);
+        }
+        this.linecap = org.linecap;
+        this.linewidth = org.linewidth;
+        this.dashpattern = org.dashpattern;
+        this.rgbColor = org.rgbColor;
+        this.fontname = org.fontname;
+        this.fontsize = org.fontsize;
+    }
  
     /**
      * Returns the transform.
@@ -42,23 +82,119 @@ public class PSState implements Serializable, Cloneable {
         return this.transform;
     }
 
+    /**
+     * 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(this.transform);
+    }
+    
     /**
      * Concats the given transformation matrix with the current one.
      * @param transform The new transformation matrix
      */
     public void concatMatrix(AffineTransform transform) {
+        this.transformConcatList.add(transform);
         this.transform.concatenate(transform);
     }
 
     /**
-     * @see java.lang.Object#clone()
+     * Establishes the specified line cap.
+     * @param value line cap (0, 1 or 2) as defined by the setlinecap command
+     * @return true if the line cap changed compared to the previous setting
      */
-    public Object clone() {
-        try {
-            return super.clone();
-        } catch (CloneNotSupportedException e) { 
-            // this shouldn't happen, since we are Cloneable
-            throw new InternalError();
+    public boolean useLineCap(int value) {
+        if (linecap != value) {
+            linecap = value;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Establishes the specified line width.
+     * @param value line width as defined by the setlinewidth command
+     * @return true if the line width changed compared to the previous setting
+     */
+    public boolean useLineWidth(double value) {
+        if (linewidth != value) {
+            linewidth = value;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Establishes the specified dash.
+     * @param pattern dash pattern as defined by the setdash command
+     * @return true if the dash pattern changed compared to the previous setting
+     */
+    public boolean useDash(String pattern) {
+        if (!dashpattern.equals(pattern)) {
+            dashpattern = pattern;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Establishes the specified color (RGB).
+     * @param value color as defined by the setrgbcolor command
+     * @return true if the color changed compared to the previous setting
+     */
+    public boolean useColor(Color value) {
+        if (!rgbColor.equals(value)) {
+            rgbColor = value;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Establishes the specified font and size.
+     * @param name name of the font for the "F" command (see FOP Std Proc Set)
+     * @param size size of the font
+     * @return true if the font changed compared to the previous setting
+     */
+    public boolean useFont(String name, float size) {
+        if (name == null) {
+            throw new NullPointerException("font name must not be null");
+        }
+        if (fontname == null || !fontname.equals(name) || fontsize != size) {
+            fontname = name;
+            fontsize = size;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Reestablishes the graphics state represented by this instance by issueing the
+     * necessary commands.
+     * @param gen The generator to use for output
+     * @exception IOException In case of an I/O problem
+     */
+    public void reestablish(PSGenerator gen) throws IOException {
+        for (int i = 0, len = transformConcatList.size(); i < len; i++) {
+            gen.concatMatrix((AffineTransform)transformConcatList.get(i));
+        }
+        gen.useLineCap(linecap);
+        gen.useLineWidth(linewidth);
+        gen.useDash(dashpattern);
+        gen.useRGBColor(rgbColor);
+        if (fontname != null) {
+            gen.useFont(fontname, fontsize);
         }
     }
     
index d73267d74d43cd126a1eef878d56c1b3659cbe5b..8c1fa1b3eb9332ee97b4e2cfa5cd2dfa7f87481c 100644 (file)
@@ -46,13 +46,13 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 /**
- * PostScript XML handler.
+ * PostScript XML handler for SVG. Uses Apache Batik for SVG processing.
  * This handler handles XML for foreign objects when rendering to PostScript.
  * It renders SVG to the PostScript document using the PSGraphics2D.
  * The properties from the PostScript renderer are subject to change.
  *
  * @author <a href="mailto:fop-dev@xml.apache.org">Apache XML FOP Development Team</a>
- * @version $Id: PSXMLHandler.java,v 1.4 2003/03/11 08:42:24 jeremias Exp $
+ * @version $Id$
  */
 public class PSXMLHandler implements XMLHandler {
 
@@ -297,7 +297,7 @@ public class PSXMLHandler implements XMLHandler {
             builder = null;
 
             try {
-                gen.writeln("%SVG graphic start ---");
+                gen.commentln("%FOPBeginSVG");
                 gen.saveGraphicsState();
                 /*
                  * Clip to the svg area.
@@ -305,18 +305,19 @@ public class PSXMLHandler implements XMLHandler {
                  * an fo:block-container
                  */
                 gen.writeln("newpath");
-                gen.defineRect(xOffset, yOffset, w, h);
+                gen.defineRect(xOffset / 1000f, yOffset / 1000f, 
+                        psInfo.getWidth() / 1000f, psInfo.getWidth() / 1000f);
                 gen.writeln("clip");
                 
                 // transform so that the coordinates (0,0) is from the top left
                 // and positive is down and to the right. (0,0) is where the
                 // viewBox puts it.
-                gen.concatMatrix(sx, 0, 0, sy, xOffset, yOffset);
+                gen.concatMatrix(sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f);
 
                 SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
                 AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
-                                    w / 1000f, h / 1000f);
-                if (!at.isIdentity()) {
+                        psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f);
+                if (false && !at.isIdentity()) {
                     double[] vals = new double[6];
                     at.getMatrix(vals);
                     gen.concatMatrix(vals);
@@ -345,7 +346,7 @@ public class PSXMLHandler implements XMLHandler {
 
                 psInfo.psGenerator.restoreGraphicsState();
                 //psInfo.pdfState.pop();
-                gen.writeln("%SVG graphic end ---");
+                gen.commentln("%FOPEndSVG");
             } catch (IOException ioe) {
                 log.error("SVG graphic could not be rendered: "
                                        + ioe.getMessage(), ioe);