]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Borders and leaders/rules for the PostScript painter.
authorJeremias Maerki <jeremias@apache.org>
Fri, 19 Dec 2008 09:31:15 +0000 (09:31 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 19 Dec 2008 09:31:15 +0000 (09:31 +0000)
BorderPainter methods throw IOException (needed for PostScript).
Some Javadocs.

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

src/java/org/apache/fop/render/intermediate/BorderPainter.java
src/java/org/apache/fop/render/java2d/Java2DPainter.java
src/java/org/apache/fop/render/pdf/PDFPainter.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/ps/PSBorderPainter.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSPainter.java
src/java/org/apache/fop/render/ps/PSRenderer.java

index e8874dc699565b917585ff9818fb618ff6baaeb1..75f7732915bc9870a3a6642dfb5f40d27d033258 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.render.intermediate;
 import java.awt.Color;
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.io.IOException;
 
 import org.apache.fop.traits.BorderProps;
 import org.apache.fop.traits.RuleStyle;
@@ -38,10 +39,11 @@ public abstract class BorderPainter {
      * @param bpsAfter the border specification on the after side
      * @param bpsStart the border specification on the start side
      * @param bpsEnd the border specification on the end side
+     * @throws IOException if an I/O error occurs while creating the borders
      */
     public void drawBorders(Rectangle borderRect,
             BorderProps bpsBefore, BorderProps bpsAfter,
-            BorderProps bpsStart, BorderProps bpsEnd) {
+            BorderProps bpsStart, BorderProps bpsEnd) throws IOException {
         int startx = borderRect.x;
         int starty = borderRect.y;
         int width = borderRect.width;
@@ -199,21 +201,75 @@ public abstract class BorderPainter {
     }
 
 
+    /**
+     * Draws a border line.
+     * @param x1 X coordinate of the upper left corner
+     *                  of the line's bounding rectangle (in millipoints)
+     * @param y1 start Y coordinate of the upper left corner
+     *                  of the line's bounding rectangle (in millipoints)
+     * @param x2 end X coordinate of the lower right corner
+     *                  of the line's bounding rectangle (in millipoints)
+     * @param y2 end y coordinate of the lower right corner
+     *                  of the line's bounding rectangle (in millipoints)
+     * @param horz true if it is a horizontal line
+     * @param startOrBefore true if the line is the start or end edge of a border box
+     * @param style the border style
+     * @param color the border color
+     * @throws IOException if an I/O error occurs
+     */
     protected abstract void drawBorderLine(int x1, int y1, int x2, int y2,
-            boolean horz, boolean startOrBefore, int style, Color color);
+            boolean horz, boolean startOrBefore, int style, Color color) throws IOException;
 
+    /**
+     * Draws a line/rule.
+     * @param start start point (coordinates in millipoints)
+     * @param end end point (coordinates in millipoints)
+     * @param width width of the line
+     * @param color the line color
+     * @param style the rule style
+     * @throws IOException if an I/O error occurs
+     */
     public abstract void drawLine(Point start, Point end,
-            int width, Color color, RuleStyle style);
+            int width, Color color, RuleStyle style) throws IOException;
 
-    protected abstract void moveTo(int x, int y);
+    /**
+     * Moves the cursor to the given coordinate.
+     * @param x the X coordinate (in millipoints)
+     * @param y the Y coordinate (in millipoints)
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void moveTo(int x, int y) throws IOException;
 
-    protected abstract void lineTo(int x, int y);
+    /**
+     * Draws a line from the current cursor position to the given coordinates.
+     * @param x the X coordinate (in millipoints)
+     * @param y the Y coordinate (in millipoints)
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void lineTo(int x, int y) throws IOException;
 
-    protected abstract void closePath();
+    /**
+     * Closes the current path.
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void closePath() throws IOException;
+
+    /**
+     * Reduces the current clipping region to the current path.
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void clip() throws IOException;
 
-    protected abstract void clip();
+    /**
+     * Save the graphics state on the stack.
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void saveGraphicsState() throws IOException;
 
-    protected abstract void saveGraphicsState();
-    protected abstract void restoreGraphicsState();
+    /**
+     * Restore the last graphics state from the stack.
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void restoreGraphicsState() throws IOException;
 
 }
index 9da39a319c827c2a64354d0cc8ea84753706fd00..34fbb3384807958fa296def38b816bc3b43e621e 100644 (file)
@@ -190,7 +190,12 @@ public class Java2DPainter extends AbstractIFPainter {
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
             BorderProps start, BorderProps end) throws IFException {
         if (before != null || after != null || start != null || end != null) {
-            this.borderPainter.drawBorders(rect, before, after, start, end);
+            try {
+                this.borderPainter.drawBorders(rect, before, after, start, end);
+            } catch (IOException e) {
+                //Won't happen with Java2D
+                throw new IllegalStateException("Unexpected I/O error");
+            }
         }
     }
 
index 8e29ded4ceef58355e5e8e309fc7e7a3c48733c1..339acbb24972aac8527b0bc37a1d05f3a9d0e20a 100644 (file)
@@ -227,7 +227,11 @@ public class PDFPainter extends AbstractIFPainter {
             BorderProps start, BorderProps end) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             generator.endTextObject();
-            this.borderPainter.drawBorders(rect, before, after, start, end);
+            try {
+                this.borderPainter.drawBorders(rect, before, after, start, end);
+            } catch (IOException ioe) {
+                throw new IFException("I/O error while drawing borders", ioe);
+            }
         }
     }
 
index e0e1bab6927d34a7ac29e0f89eef9cef1d43ffc8..c40c94fc4edca576c9caf31a92d18d301bebc209 100644 (file)
@@ -482,6 +482,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf
             this.pdfDoc.addObject(annots);
         }
         this.pdfDoc.addObject(currentPage);
+        this.borderPainter = null;
         this.generator.flushPDFDoc();
         this.generator = null;
     }
diff --git a/src/java/org/apache/fop/render/ps/PSBorderPainter.java b/src/java/org/apache/fop/render/ps/PSBorderPainter.java
new file mode 100644 (file)
index 0000000..97f6215
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.intermediate.BorderPainter;
+import org.apache.fop.traits.RuleStyle;
+import org.apache.fop.util.ColorUtil;
+
+/**
+ * PostScript-specific implementation of the {@code BorderPainter}.
+ */
+public class PSBorderPainter extends BorderPainter {
+
+    /** logging instance */
+    private static Log log = LogFactory.getLog(PSBorderPainter.class);
+
+    private PSGenerator generator;
+
+    /**
+     * Creates a new border painter for PostScript.
+     * @param generator the PostScript generator
+     */
+    public PSBorderPainter(PSGenerator generator) {
+        this.generator = generator;
+    }
+
+    /** {@inheritDoc} */
+    protected void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz,
+            boolean startOrBefore, int style, Color col) throws IOException {
+       drawBorderLine(generator, toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2),
+               horz, startOrBefore, style, col);
+    }
+
+    private static void drawLine(PSGenerator gen,
+            float startx, float starty, float endx, float endy) throws IOException {
+        gen.writeln(gen.formatDouble(startx) + " "
+                + gen.formatDouble(starty) + " M "
+                + gen.formatDouble(endx) + " "
+                + gen.formatDouble(endy) + " lineto stroke newpath");
+    }
+
+    /** {@inheritDoc} */
+    public static void drawBorderLine(PSGenerator gen,
+            float x1, float y1, float x2, float y2, boolean horz,
+            boolean startOrBefore, int style, Color col) throws IOException {
+        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:
+                gen.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(gen, 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(gen, xm, y1, xm, y2);
+                }
+                break;
+            case Constants.EN_DOTTED:
+                gen.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(gen, 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(gen, xm, y1, xm, y2);
+                }
+                break;
+            case Constants.EN_DOUBLE:
+                gen.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(gen, x1, ym1, x2, ym1);
+                    drawLine(gen, x1, ym2, x2, ym2);
+                } else {
+                    float w3 = w / 3;
+                    gen.useLineWidth(w3);
+                    float xm1 = x1 + (w3 / 2);
+                    float xm2 = xm1 + w3 + w3;
+                    drawLine(gen, xm1, y1, xm1, y2);
+                    drawLine(gen, xm2, y1, xm2, y2);
+                }
+                break;
+            case Constants.EN_GROOVE:
+            case Constants.EN_RIDGE:
+                float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f);
+                gen.useDash(null);
+                if (horz) {
+                    Color uppercol = ColorUtil.lightenColor(col, -colFactor);
+                    Color lowercol = ColorUtil.lightenColor(col, colFactor);
+                    float h3 = h / 3;
+                    gen.useLineWidth(h3);
+                    float ym1 = y1 + (h3 / 2);
+                    gen.useColor(uppercol);
+                    drawLine(gen, x1, ym1, x2, ym1);
+                    gen.useColor(col);
+                    drawLine(gen, x1, ym1 + h3, x2, ym1 + h3);
+                    gen.useColor(lowercol);
+                    drawLine(gen, x1, ym1 + h3 + h3, x2, ym1 + h3 + h3);
+                } else {
+                    Color leftcol = ColorUtil.lightenColor(col, -colFactor);
+                    Color rightcol = ColorUtil.lightenColor(col, colFactor);
+                    float w3 = w / 3;
+                    gen.useLineWidth(w3);
+                    float xm1 = x1 + (w3 / 2);
+                    gen.useColor(leftcol);
+                    drawLine(gen, xm1, y1, xm1, y2);
+                    gen.useColor(col);
+                    drawLine(gen, xm1 + w3, y1, xm1 + w3, y2);
+                    gen.useColor(rightcol);
+                    drawLine(gen, xm1 + w3 + w3, y1, xm1 + w3 + w3, y2);
+                }
+                break;
+            case Constants.EN_INSET:
+            case Constants.EN_OUTSET:
+                colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f);
+                gen.useDash(null);
+                if (horz) {
+                    Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
+                    gen.useLineWidth(h);
+                    float ym1 = y1 + (h / 2);
+                    gen.useColor(c);
+                    drawLine(gen, x1, ym1, x2, ym1);
+                } else {
+                    Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
+                    gen.useLineWidth(w);
+                    float xm1 = x1 + (w / 2);
+                    gen.useColor(c);
+                    drawLine(gen, xm1, y1, xm1, y2);
+                }
+                break;
+            case Constants.EN_HIDDEN:
+                break;
+            default:
+                gen.useColor(col);
+                gen.useDash(null);
+                gen.useLineCap(0);
+                if (horz) {
+                    gen.useLineWidth(h);
+                    float ym = y1 + (h / 2);
+                    drawLine(gen, x1, ym, x2, ym);
+                } else {
+                    gen.useLineWidth(w);
+                    float xm = x1 + (w / 2);
+                    drawLine(gen, xm, y1, xm, y2);
+                }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void drawLine(Point start, Point end,
+            int width, Color color, RuleStyle style) throws IOException {
+        if (start.y != end.y) {
+            //TODO Support arbitrary lines if necessary
+            throw new UnsupportedOperationException(
+                    "Can only deal with horizontal lines right now");
+        }
+
+        saveGraphicsState();
+        int half = width / 2;
+        int starty = start.y - half;
+        //Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width);
+
+        switch (style.getEnumValue()) {
+            case Constants.EN_SOLID:
+            case Constants.EN_DASHED:
+            case Constants.EN_DOUBLE:
+                drawBorderLine(start.x, starty, end.x, starty + width,
+                        true, true, style.getEnumValue(), color);
+                break;
+            case Constants.EN_DOTTED:
+                clipRect(start.x, starty, end.x - start.x, width);
+                //This displaces the dots to the right by half a dot's width
+                //TODO There's room for improvement here
+                generator.concatMatrix(1, 0, 0, 1, toPoints(half), 0);
+                drawBorderLine(start.x, starty, end.x, starty + width,
+                        true, true, style.getEnumValue(), color);
+                break;
+            case Constants.EN_GROOVE:
+            case Constants.EN_RIDGE:
+                generator.useColor(ColorUtil.lightenColor(color, 0.6f));
+                moveTo(start.x, starty);
+                lineTo(end.x, starty);
+                lineTo(end.x, starty + 2 * half);
+                lineTo(start.x, starty + 2 * half);
+                closePath();
+                generator.writeln(" fill newpath");
+                generator.useColor(color);
+                if (style == RuleStyle.GROOVE) {
+                    moveTo(start.x, starty);
+                    lineTo(end.x, starty);
+                    lineTo(end.x, starty + half);
+                    lineTo(start.x + half, starty + half);
+                    lineTo(start.x, starty + 2 * half);
+                } else {
+                    moveTo(end.x, starty);
+                    lineTo(end.x, starty + 2 * half);
+                    lineTo(start.x, starty + 2 * half);
+                    lineTo(start.x, starty + half);
+                    lineTo(end.x - half, starty + half);
+                }
+                closePath();
+                generator.writeln(" fill newpath");
+                break;
+            default:
+                throw new UnsupportedOperationException("rule style not supported");
+        }
+
+        restoreGraphicsState();
+
+    }
+
+    private static float toPoints(int mpt) {
+        return mpt / 1000f;
+    }
+
+    /** {@inheritDoc} */
+    protected void moveTo(int x, int y) throws IOException {
+        generator.writeln(generator.formatDouble(toPoints(x)) + " "
+                + generator.formatDouble(toPoints(y)) + " M");
+    }
+
+    /** {@inheritDoc} */
+    protected void lineTo(int x, int y) throws IOException {
+        generator.writeln(generator.formatDouble(toPoints(x)) + " "
+                + generator.formatDouble(toPoints(y)) + " lineto");
+    }
+
+    /** {@inheritDoc} */
+    protected void closePath() throws IOException {
+        generator.writeln("cp");
+    }
+
+    private void clipRect(int x, int y, int width, int height) throws IOException {
+        generator.defineRect(toPoints(x), toPoints(y), toPoints(width), toPoints(height));
+        clip();
+    }
+
+    /** {@inheritDoc} */
+    protected void clip() throws IOException {
+        generator.writeln("clip newpath");
+    }
+
+    /** {@inheritDoc} */
+    protected void saveGraphicsState() throws IOException {
+        generator.saveGraphicsState();
+    }
+
+    /** {@inheritDoc} */
+    protected void restoreGraphicsState() throws IOException {
+        generator.restoreGraphicsState();
+    }
+
+}
index 3c937addf9012492736e873af7e45dbb7158e516..a55d20739e33cbd52eb0efca4d93c92f9cc9cee0 100644 (file)
@@ -64,6 +64,7 @@ public class PSPainter extends AbstractIFPainter {
     private static Log log = LogFactory.getLog(PSPainter.class);
 
     private PSDocumentHandler documentHandler;
+    private PSBorderPainter borderPainter;
 
     private boolean inTextMode = false;
 
@@ -74,6 +75,7 @@ public class PSPainter extends AbstractIFPainter {
     public PSPainter(PSDocumentHandler documentHandler) {
         super();
         this.documentHandler = documentHandler;
+        this.borderPainter = new PSBorderPainter(documentHandler.gen);
         this.state = IFState.create();
     }
 
@@ -236,9 +238,8 @@ public class PSPainter extends AbstractIFPainter {
             BorderProps start, BorderProps end) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             try {
-                //TODO Implement me
                 endTextObject();
-                //this.borderPainter.drawBorders(rect, before, after, start, end);
+                this.borderPainter.drawBorders(rect, before, after, start, end);
             } catch (IOException ioe) {
                 throw new IFException("I/O error in drawBorderRect()", ioe);
             }
@@ -249,9 +250,8 @@ public class PSPainter extends AbstractIFPainter {
     public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
                 throws IFException {
         try {
-            //TODO Implement me
             endTextObject();
-            //this.borderPainter.drawLine(start, end, width, color, style);
+            this.borderPainter.drawLine(start, end, width, color, style);
         } catch (IOException ioe) {
             throw new IFException("I/O error in drawLine()", ioe);
         }
index 55f714f319be7a61f35e90d8f03b2e0fe7557bbb..19fcd8af83fc215e4045b811d062c9e59349c06c 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.render.ps;
 
 // Java
 import java.awt.Color;
+import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
@@ -37,7 +38,6 @@ import java.util.Map;
 
 import javax.xml.transform.Source;
 
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -80,7 +80,6 @@ import org.apache.fop.area.inline.TextArea;
 import org.apache.fop.area.inline.WordArea;
 import org.apache.fop.datatypes.URISpecification;
 import org.apache.fop.events.ResourceEventProducer;
-import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.LazyFont;
@@ -98,8 +97,8 @@ import org.apache.fop.render.ps.extensions.PSCommentBefore;
 import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
 import org.apache.fop.render.ps.extensions.PSSetPageDevice;
 import org.apache.fop.render.ps.extensions.PSSetupCode;
+import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.CharUtilities;
-import org.apache.fop.util.ColorUtil;
 
 /**
  * Renderer that renders to PostScript.
@@ -159,6 +158,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * normal rendering process.
      */
     protected PSRenderingUtil psUtil;
+    private PSBorderPainter borderPainter;
 
     /** Is used to determine the document's bounding box */
     private Rectangle2D documentBoundingBox;
@@ -600,151 +600,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     protected void drawBorderLine(float x1, float y1, float x2, float y2,
             boolean horz, boolean startOrBefore, int style, Color 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);
-                    if (horz) {
-                        Color uppercol = ColorUtil.lightenColor(col, -colFactor);
-                        Color lowercol = ColorUtil.lightenColor(col, colFactor);
-                        float h3 = h / 3;
-                        gen.useLineWidth(h3);
-                        float ym1 = y1 + (h3 / 2);
-                        gen.useColor(uppercol);
-                        drawLine(x1, ym1, x2, ym1);
-                        gen.useColor(col);
-                        drawLine(x1, ym1 + h3, x2, ym1 + h3);
-                        gen.useColor(lowercol);
-                        drawLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3);
-                    } else {
-                        Color leftcol = ColorUtil.lightenColor(col, -colFactor);
-                        Color rightcol = ColorUtil.lightenColor(col, colFactor);
-                        float w3 = w / 3;
-                        gen.useLineWidth(w3);
-                        float xm1 = x1 + (w3 / 2);
-                        gen.useColor(leftcol);
-                        drawLine(xm1, y1, xm1, y2);
-                        gen.useColor(col);
-                        drawLine(xm1 + w3, y1, xm1 + w3, y2);
-                        gen.useColor(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);
-                    if (horz) {
-                        Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
-                        gen.useLineWidth(h);
-                        float ym1 = y1 + (h / 2);
-                        gen.useColor(c);
-                        drawLine(x1, ym1, x2, ym1);
-                    } else {
-                        Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
-                        gen.useLineWidth(w);
-                        float xm1 = x1 + (w / 2);
-                        gen.useColor(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);
-                    }
-            }
+            PSBorderPainter.drawBorderLine(gen, x1, y1, x2, y2, horz, startOrBefore, style, col);
         } catch (IOException ioe) {
             handleIOTrouble(ioe);
         }
@@ -773,6 +629,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
             }
         };
         this.gen.setPSLevel(getLanguageLevel());
+        this.borderPainter = new PSBorderPainter(this.gen);
         this.currentPageNumber = 0;
 
         //Initial default page device dictionary settings
@@ -852,6 +709,8 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         if (pageDeviceDictionary != null) {
             pageDeviceDictionary.clear();
         }
+        this.borderPainter = null;
+        this.gen = null;
     }
 
     /**
@@ -1346,74 +1205,27 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         super.renderInlineParent(ip);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public void renderLeader(Leader area) {
         renderInlineAreaBackAndBorders(area);
 
-        endTextObject();
-        saveGraphicsState();
         int style = area.getRuleStyle();
-        float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
-        float starty = (currentBPPosition + area.getOffset()) / 1000f;
-        float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
-                        + area.getIPD()) / 1000f;
-        float ruleThickness = area.getRuleThickness() / 1000f;
+        int ruleThickness = area.getRuleThickness();
+        int startx = currentIPPosition + area.getBorderAndPaddingWidthStart();
+        int starty = currentBPPosition + area.getOffset() + (ruleThickness / 2);
+        int endx = currentIPPosition
+                        + area.getBorderAndPaddingWidthStart()
+                        + area.getIPD();
         Color col = (Color)area.getTrait(Trait.COLOR);
 
+        endTextObject();
         try {
-            switch (style) {
-                case EN_SOLID:
-                case EN_DASHED:
-                case EN_DOUBLE:
-                    drawBorderLine(startx, starty, endx, starty + ruleThickness,
-                            true, true, style, col);
-                    break;
-                case EN_DOTTED:
-                    clipRect(startx, starty, endx - startx, ruleThickness);
-                    //This displaces the dots to the right by half a dot's width
-                    //TODO There's room for improvement here
-                    gen.concatMatrix(1, 0, 0, 1, ruleThickness / 2, 0);
-                    drawBorderLine(startx, starty, endx, starty + ruleThickness,
-                            true, true, style, col);
-                    break;
-                case EN_GROOVE:
-                case EN_RIDGE:
-                    float half = area.getRuleThickness() / 2000f;
-
-                    gen.useColor(ColorUtil.lightenColor(col, 0.6f));
-                    moveTo(startx, starty);
-                    lineTo(endx, starty);
-                    lineTo(endx, starty + 2 * half);
-                    lineTo(startx, starty + 2 * half);
-                    closePath();
-                    gen.writeln(" fill newpath");
-                    gen.useColor(col);
-                    if (style == EN_GROOVE) {
-                        moveTo(startx, starty);
-                        lineTo(endx, starty);
-                        lineTo(endx, starty + half);
-                        lineTo(startx + half, starty + half);
-                        lineTo(startx, starty + 2 * half);
-                    } else {
-                        moveTo(endx, starty);
-                        lineTo(endx, starty + 2 * half);
-                        lineTo(startx, starty + 2 * half);
-                        lineTo(startx, starty + half);
-                        lineTo(endx - half, starty + half);
-                    }
-                    closePath();
-                    gen.writeln(" fill newpath");
-                    break;
-                default:
-                    throw new UnsupportedOperationException("rule style not supported");
-            }
+            borderPainter.drawLine(new Point(startx, starty), new Point(endx, starty),
+                    ruleThickness, col, RuleStyle.valueOf(style));
         } catch (IOException ioe) {
             handleIOTrouble(ioe);
         }
 
-        restoreGraphicsState();
         super.renderLeader(area);
     }