]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #49913:
authorJeremias Maerki <jeremias@apache.org>
Thu, 30 Sep 2010 11:51:08 +0000 (11:51 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 30 Sep 2010 11:51:08 +0000 (11:51 +0000)
Initial code drop for support of rounded corners.
Submitted by: Peter Hancock <peter.hancock.at.gmail.com>

Changes to patch:
- Java 1.5 methods eliminated
- Some (not all!) Checkstyle violations fixed.
- Fixed some excessive and missing whitespace
- paintCornersAsBitmap() in AFPPainter was missing a call to ImageSize.calcPixelsFromSize()
- Made AT representation of border traits cleaner
- Added a TODO for table borders (inner and outer versions)

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

33 files changed:
src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
src/java/org/apache/fop/afp/AFPResourceManager.java
src/java/org/apache/fop/afp/AFPStreamer.java
src/java/org/apache/fop/area/Trait.java
src/java/org/apache/fop/fo/Constants.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java [new file with mode: 0644]
src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
src/java/org/apache/fop/layoutmgr/TraitSetter.java
src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
src/java/org/apache/fop/render/afp/AFPImageHandler.java
src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
src/java/org/apache/fop/render/afp/AFPPainter.java
src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java
src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
src/java/org/apache/fop/render/intermediate/BorderPainter.java
src/java/org/apache/fop/render/intermediate/IFException.java
src/java/org/apache/fop/render/intermediate/IFPainter.java
src/java/org/apache/fop/render/intermediate/IFParser.java
src/java/org/apache/fop/render/intermediate/IFRenderer.java
src/java/org/apache/fop/render/intermediate/IFSerializer.java
src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java
src/java/org/apache/fop/render/java2d/Java2DPainter.java
src/java/org/apache/fop/render/pcl/PCLPainter.java
src/java/org/apache/fop/render/pdf/PDFBorderPainter.java
src/java/org/apache/fop/render/pdf/PDFPainter.java
src/java/org/apache/fop/render/ps/PSBorderPainter.java
src/java/org/apache/fop/render/ps/PSPainter.java
src/java/org/apache/fop/traits/BorderProps.java
src/sandbox/org/apache/fop/render/svg/SVGPainter.java

index 4e0b7d1151137bd41d5f28e3a3c905c78813dd0b..904eeec21c6262e5963758ac9b184bb441f74d85 100644 (file)
@@ -99,6 +99,7 @@
           <xs:attribute name="end" type="mf:borderDef"/>
           <xs:attribute name="before" type="mf:borderDef"/>
           <xs:attribute name="after" type="mf:borderDef"/>
+          <xs:attribute name="inner-background-color" type="mf:colorType"/>
         </xs:complexType>
       </xs:element>
       <xs:element name="image">
index b7e1abc014f03545c23d6604332ae58bb9ae2e33..6aad0ca50d60975dc427c1d47b0a9a77d089339e 100644 (file)
@@ -61,11 +61,9 @@ public class AFPResourceManager {
     /** Maintain a reference count of instream objects for referencing purposes */
     private int instreamObjectCount = 0;
 
-    /** a mapping of resourceInfo --> include name */
-    private final Map/*<AFPResourceInfo,String>*/ includeNameMap
-        = new java.util.HashMap()/*<AFPResourceInfo,String>*/;
-
-    private Map pageSegmentMap = new java.util.HashMap();
+    /** Mapping of resourceInfo to AbstractCachedObject */
+    private final Map/*<AFPResourceInfo, AbstractCachedObject>*/ includeObjectCache
+    = new java.util.HashMap()/*<AFPResourceInfo,String>*/;
 
     private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults();
 
@@ -134,17 +132,7 @@ public class AFPResourceManager {
         AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
         updateResourceInfoUri(resourceInfo);
 
-        String objectName = (String)includeNameMap.get(resourceInfo);
-        if (objectName != null) {
-            // an existing data resource so reference it by adding an include to the current page
-            includeObject(dataObjectInfo, objectName);
-            return;
-        }
-
-        objectName = (String)pageSegmentMap.get(resourceInfo);
-        if (objectName != null) {
-            // an existing data resource so reference it by adding an include to the current page
-            includePageSegment(dataObjectInfo, objectName);
+        if (includeCachedObject(resourceInfo, null)) {
             return;
         }
 
@@ -170,7 +158,7 @@ public class AFPResourceManager {
 
         useInclude &= resourceGroup != null;
         if (useInclude) {
-            boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
+            final boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
 
             // if it is to reside within a resource group at print-file or external level
             if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
@@ -189,22 +177,109 @@ public class AFPResourceManager {
             // add data object into its resource group destination
             resourceGroup.addObject(namedObj);
 
-            // create the include object
-            objectName = namedObj.getName();
-            if (usePageSegment) {
-                includePageSegment(dataObjectInfo, objectName);
-                pageSegmentMap.put(resourceInfo, objectName);
-            } else {
-                includeObject(dataObjectInfo, objectName);
-                // record mapping of resource info to data object resource name
-                includeNameMap.put(resourceInfo, objectName);
-            }
+            includeObject(namedObj, dataObjectInfo);
         } else {
             // not to be included so inline data object directly into the current page
             dataStream.getCurrentPage().addObject(namedObj);
         }
     }
 
+    private abstract class AbstractCachedObject {
+        protected String objectName;
+        protected AFPDataObjectInfo dataObjectInfo;
+
+        public AbstractCachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
+            this.objectName = objectName;
+            this.dataObjectInfo = dataObjectInfo;
+
+
+        }
+        protected abstract void includeObject();
+    }
+
+    private class CachedPageSegment extends AbstractCachedObject {
+
+        public CachedPageSegment(String objectName, AFPDataObjectInfo dataObjectInfo) {
+           super(objectName, dataObjectInfo);
+        }
+
+        protected void includeObject() {
+            includePageSegment(dataObjectInfo, objectName);
+        }
+
+    }
+
+    private class CachedObject extends AbstractCachedObject {
+
+        public CachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
+           super(objectName, dataObjectInfo);
+        }
+
+        protected void includeObject() {
+            AFPResourceManager.this.includeObject(dataObjectInfo, objectName);
+        }
+
+    }
+
+
+    private void includeObject(AbstractNamedAFPObject namedObj, AFPDataObjectInfo dataObjectInfo) {
+
+        // create the include object
+        AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
+        String objectName = namedObj.getName();
+
+        AbstractCachedObject cachedObject;
+
+        if (dataObjectInfo.isCreatePageSegment()) {
+            cachedObject = new CachedPageSegment(objectName, dataObjectInfo);
+        } else {
+            cachedObject = new CachedObject(objectName, dataObjectInfo);
+        }
+
+        cachedObject.includeObject();
+
+        includeObjectCache.put(dataObjectInfo.getResourceInfo(), cachedObject);
+
+        //The data field of dataObjectInfo is not further required
+        // therefore we are safe to null the reference, saving memory
+        dataObjectInfo.setData(null);
+
+    }
+
+    /**
+     * TODO
+     * @param resourceInfo
+     * @return
+     */
+    public boolean isObjectCached(AFPResourceInfo resourceInfo) {
+        return includeObjectCache.containsKey(resourceInfo);
+    }
+
+    /**
+     * TODO
+     * @param resourceInfo
+     * @param areaInfo
+     * @return
+     */
+    public boolean includeCachedObject(AFPResourceInfo resourceInfo, AFPObjectAreaInfo areaInfo) {
+
+            String objectName;
+
+            AbstractCachedObject cachedObject = (AbstractCachedObject)includeObjectCache.get(resourceInfo);
+
+            if (cachedObject != null) {
+                if (areaInfo != null) {
+                    cachedObject.dataObjectInfo.setObjectAreaInfo(areaInfo);
+                }
+                cachedObject.includeObject();
+
+                return true;
+            } else {
+                return false;
+            }
+    }
+
+
     private void updateResourceInfoUri(AFPResourceInfo resourceInfo) {
         String uri = resourceInfo.getUri();
         if (uri == null) {
@@ -292,8 +367,12 @@ public class AFPResourceManager {
         resourceInfo.setName(resourceName);
         resourceInfo.setUri(uri.toASCIIString());
 
-        String objectName = (String)includeNameMap.get(resourceInfo);
-        if (objectName == null) {
+
+        AbstractCachedObject cachedObject = (AbstractCachedObject)
+                includeObjectCache.get(resourceInfo);
+
+        if (cachedObject == null ) {
+
             if (log.isDebugEnabled()) {
                 log.debug("Adding included resource: " + resourceName);
             }
@@ -306,8 +385,12 @@ public class AFPResourceManager {
 
             ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
             resourceGroup.addObject(resourceObject);
+
+            //TODO what is the data object?
+            cachedObject = new CachedObject(resourceName, null);
+
             // record mapping of resource info to data object resource name
-            includeNameMap.put(resourceInfo, resourceName);
+            includeObjectCache.put(resourceInfo, cachedObject);
         } else {
             //skip, already created
         }
index 33d1dbf9036f3a42c87a3af7846ec114c2396d8e..154ca4cc9cc47bccc1819319c7cdf778083c3cf4 100644 (file)
@@ -158,7 +158,7 @@ public class AFPStreamer implements Streamable {
      */
         // write out any external resource groups
     public void close() throws IOException {
-        Iterator it = pathResourceGroupMap.entrySet().iterator();
+        Iterator it = pathResourceGroupMap.values().iterator();
         while (it.hasNext()) {
             StreamedResourceGroup resourceGroup = (StreamedResourceGroup)it.next();
             resourceGroup.close();
index 42c5462e445dd4fefe8a37125f423800a5d749ae..9e9d576c320221c57815867e7368a62bdde61080 100644 (file)
@@ -197,12 +197,13 @@ public final class Trait implements Serializable {
     public static final Integer OVERLINE_COLOR = new Integer(35);
     /** Trait for color of linethrough decorations when rendering inline parent. */
     public static final Integer LINETHROUGH_COLOR = new Integer(36);
-    
+
     /** The ptr trait. Used for accessibility   */
     public static final Integer PTR = new Integer(37);
 
+
     /** Maximum value used by trait keys */
-    public static final int MAX_TRAIT_KEY = 37;
+    public static final int MAX_TRAIT_KEY = 38;
 
     private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];
 
index 5f23502f376faa78117d0e48e84e847750318bd7..d9b0f252721fd005da7d216054b6aba3ee20a2ad 100644 (file)
@@ -779,8 +779,30 @@ public interface Constants {
      */
     int PR_X_ALT_TEXT = 275;
 
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_BEFORE_RADIUS_START = 276;
+
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_BEFORE_RADIUS_END = 277;
+
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_AFTER_RADIUS_START = 278;
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_AFTER_RADIUS_END = 279;
+
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_START_RADIUS_START = 280;
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_START_RADIUS_END = 281;
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_END_RADIUS_START = 282;
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_END_RADIUS_END = 283;
+    /** Property constant FOP proprietary*/
+    int PR_X_BORDER_RADIUS = 284;
+
     /** Number of property constants defined */
-    int PROPERTY_COUNT = 275;
+    int PROPERTY_COUNT = 284;
 
     // compound property constants
 
index c711a76c05e19848827a32a30a775d90603527f5..e79ed25b64d7afe883e1c71342d7eb836bd5da2e 100644 (file)
@@ -29,6 +29,7 @@ import org.apache.fop.fo.flow.table.TableFObj.ColumnNumberPropertyMaker;
 import org.apache.fop.fo.properties.BackgroundPositionShorthand;
 import org.apache.fop.fo.properties.BorderSpacingShorthandParser;
 import org.apache.fop.fo.properties.BorderWidthPropertyMaker;
+import org.apache.fop.fo.properties.BoxCornerPropShorthandParser;
 import org.apache.fop.fo.properties.BoxPropShorthandParser;
 import org.apache.fop.fo.properties.CharacterProperty;
 import org.apache.fop.fo.properties.ColorProperty;
@@ -97,6 +98,7 @@ public final class FOPropertyMapping implements Constants {
     private PropertyMaker genericCondBorderWidth = null;
     private PropertyMaker genericBorderWidth = null;
     private PropertyMaker genericBorderStyle = null;
+    private PropertyMaker genericCondCornerRadius = null;
     private PropertyMaker genericBreak = null;
     private PropertyMaker genericSpace = null;
 
@@ -201,6 +203,15 @@ public final class FOPropertyMapping implements Constants {
         genericBorderStyle.addEnum("outset", getEnumProperty(EN_OUTSET, "OUTSET"));
         genericBorderStyle.setDefault("none");
 
+        // GenericCondCornerRadius
+        genericCondCornerRadius =  new CondLengthProperty.Maker(0);
+        genericCondCornerRadius.useGeneric(genericCondLength);
+        genericCondCornerRadius.setInherited(false);
+        genericCondCornerRadius.getSubpropMaker(CP_LENGTH).setDefault("0pt");
+        genericCondCornerRadius.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT);
+        genericCondCornerRadius.addShorthand(generics[PR_X_BORDER_RADIUS]);
+
+
         // GenericBreak
         genericBreak = new EnumProperty.Maker(0);
         genericBreak.setInherited(false);
@@ -2544,6 +2555,59 @@ public final class FOPropertyMapping implements Constants {
         m.setDefault("");
         addPropertyMaker("fox:alt-text", m);
 
+
+        // fox:border-*-radius-*
+
+        // border-before-radius-start
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+
+        addPropertyMaker("fox:border-before-radius-start", m);
+
+        // border-before-radius-end
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_END);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-before-radius-end", m);
+
+        // border-after-radius-start
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_START);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-after-radius-start", m);
+
+        // border-after-radius-end
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_END);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-after-radius-end", m);
+
+        // border-start-radius-before
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_START);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-start-radius-before", m);
+
+        // border-start-radius-after
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_END);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-start-radius-after", m);
+
+        // border-end-radius-before
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_START);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-end-radius-before", m);
+
+        // border-end-radius-after
+        m  = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_END);
+        m.useGeneric(genericCondCornerRadius);
+        m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
+        addPropertyMaker("fox:border-end-radius-after", m);
+
+
         // provisional-label-separation
         m  = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION);
         m.setInherited(true);
@@ -2696,6 +2760,13 @@ public final class FOPropertyMapping implements Constants {
         m.setDatatypeParser(new BoxPropShorthandParser());
         addPropertyMaker("border-width", m);
 
+        // fox:border-radius
+        m  = new ListProperty.Maker(PR_X_BORDER_RADIUS);
+        m.setInherited(false);
+        m.setDatatypeParser(new BoxCornerPropShorthandParser());
+        m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH);
+        addPropertyMaker("fox:border-radius", m);
+
         // cue
         m  = new ToBeImplementedProperty.Maker(PR_CUE);
         m.setInherited(false);
index a12bed0fa8e4dca15a2ce6589e3df4f5857c47a4..1ff44700dd302dfaf7e7697f2fba87705a7fe998 100644 (file)
@@ -49,6 +49,17 @@ public class ExtensionElementMapping extends ElementMapping {
         PROPERTY_ATTRIBUTES.add("disable-column-balancing");
         //These are FOP's extension properties for accessibility
         PROPERTY_ATTRIBUTES.add("alt-text");
+
+        //fox:border-*-radius-*
+        PROPERTY_ATTRIBUTES.add("border-before-radius-start");
+        PROPERTY_ATTRIBUTES.add("border-before-radius-end");
+        PROPERTY_ATTRIBUTES.add("border-after-radius-start");
+        PROPERTY_ATTRIBUTES.add("border-after-radius-end");
+        PROPERTY_ATTRIBUTES.add("border-start-radius-before");
+        PROPERTY_ATTRIBUTES.add("border-start-radius-after");
+        PROPERTY_ATTRIBUTES.add("border-end-radius-before");
+        PROPERTY_ATTRIBUTES.add("border-end-radius-after");
+        PROPERTY_ATTRIBUTES.add("border-radius");
     }
 
     /**
diff --git a/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java b/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java
new file mode 100644 (file)
index 0000000..d3e37a7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.fo.properties;
+
+import org.apache.fop.fo.FOPropertyMapping;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.expr.PropertyException;
+
+/**
+ * Shorthand property parser for Box rounded corner properties
+ */
+public class BoxCornerPropShorthandParser extends GenericShorthandParser {
+
+    /**
+     * Default constructor.
+     */
+    public BoxCornerPropShorthandParser() {
+    }
+
+    /**
+     * Stores 1 or 2 values of same type representing rounded corner radii.
+     * If 2 value are present the first is the corner radius in the IP direction,
+     * the second in the BP direction.
+     * {@inheritDoc}
+     * int, Property, PropertyMaker, PropertyList)
+     */
+    protected Property convertValueForProperty(int propId,
+                                               Property property,
+                                               PropertyMaker maker,
+                                               PropertyList propertyList)
+                throws PropertyException {
+        String name = FOPropertyMapping.getPropertyName(propId);
+        Property p = null;
+        int count = property.getList().size();
+
+        if (name.indexOf("border-start") > -1 || name.indexOf("border-end") > -1) {
+            p = getElement(property, 0);
+        } else if (name.indexOf("border-before") > -1 || name.indexOf("border-after") > -1) {
+            p = getElement(property, count > 1 ? 1 : 0);
+        }
+
+        // if p not null, try to convert it to a value of the correct type
+        if (p != null) {
+            return maker.convertShorthandProperty(propertyList, p, null);
+        }
+        return p;
+    }
+
+}
index d39dc24f075b6c9d7b5013308195fa10402bda23..3b4b818b5c78c111dbd3686027b02913760e58dd 100644 (file)
@@ -96,6 +96,8 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
     /** the "end" edge */
     public static final int END = 3;
 
+
+
     /**
      * Utility class to express border info.
      */
@@ -108,32 +110,40 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
         private int mStyle; // Enum for border style
         private Color mColor; // Border color
         private CondLengthProperty mWidth;
+        private CondLengthProperty radiusStart;
+        private CondLengthProperty radiusEnd;
 
         private int hash = -1;
 
         /**
          * Hidden constructor
          */
-        private BorderInfo(int style, CondLengthProperty width, Color color) {
+        private BorderInfo(int style, CondLengthProperty width, Color color,
+                CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
             mStyle = style;
             mWidth = width;
             mColor = color;
+            this.radiusStart = radiusStart;
+            this.radiusEnd = radiusEnd;
         }
 
         /**
-         * Returns a BorderInfo instance corresponding to the given values
+         * Returns a BorderInfo instance corresponding to the given values.
          *
          * @param style the border-style
          * @param width the border-width
          * @param color the border-color
+         * @param radiusStart the start radius for rounded borders
+         * @param radiusEnd the end radius for rounded borders
          * @return a cached BorderInfo instance
          */
-        public static BorderInfo getInstance(int style, CondLengthProperty width, Color color) {
-            return CACHE.fetch(new BorderInfo(style, width, color));
+        public static BorderInfo getInstance(int style, CondLengthProperty width, Color color,
+                CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
+            return CACHE.fetch(new BorderInfo(style, width, color, radiusStart, radiusEnd));
         }
 
         /**
-         * @return  the border-style
+         * @return the border-style
          */
         public int getStyle() {
             return this.mStyle;
@@ -168,6 +178,20 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
             }
         }
 
+        /**
+         * @return the border-*-start-radius
+         */
+        public CondLengthProperty getRadiusStart() {
+            return this.radiusStart;
+        }
+
+        /**
+         * @return the border-*-end-radius
+         */
+        public CondLengthProperty getRadiusEnd() {
+            return this.radiusEnd;
+        }
+
         /** {@inheritDoc} */
         public String toString() {
             StringBuffer sb = new StringBuffer("BorderInfo");
@@ -177,6 +201,10 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
             sb.append(mColor);
             sb.append(", ");
             sb.append(mWidth);
+            sb.append(", ");
+            sb.append(radiusStart);
+            sb.append(", ");
+            sb.append(radiusEnd);
             sb.append("}");
             return sb.toString();
         }
@@ -190,8 +218,10 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
             if (obj instanceof BorderInfo) {
                 BorderInfo bi = (BorderInfo)obj;
                 return (this.mColor == bi.mColor
-                    && this.mStyle == bi.mStyle
-                    && this.mWidth == bi.mWidth);
+                        && this.mStyle == bi.mStyle
+                        && this.mWidth == bi.mWidth
+                        && this.radiusStart == bi.radiusStart
+                        && this.radiusEnd == bi.radiusEnd);
             }
 
             return false;
@@ -204,18 +234,27 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
                 hash = 37 * hash + (mColor == null ? 0 : mColor.hashCode());
                 hash = 37 * hash + mStyle;
                 hash = 37 * hash + (mWidth == null ? 0 : mWidth.hashCode());
+                hash = 37 * hash + (radiusStart == null ? 0 : radiusStart.hashCode());
+                hash = 37 * hash + (radiusEnd == null ? 0 : radiusEnd.hashCode());
                 this.hash = hash;
             }
             return this.hash;
         }
     }
 
+
+
+
     /**
      * A border info with style "none". Used as a singleton, in the collapsing-border model,
      * for elements which don't specify any border on some of their sides.
      */
     private static final BorderInfo DEFAULT_BORDER_INFO
-            = BorderInfo.getInstance(Constants.EN_NONE, new ConditionalNullLength(), null);
+    = BorderInfo.getInstance(Constants.EN_NONE, new ConditionalNullLength(), null,
+            new ConditionalNullLength(), new ConditionalNullLength());
+
+
+
 
     /**
      * A conditional length of value 0. Returned by the
@@ -292,8 +331,11 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
 
         backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();
 
+
+
+
         Color bc = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
-                                        pList.getFObj().getUserAgent());
+                pList.getFObj().getUserAgent());
         if (bc.getAlpha() == 0) {
             backgroundColor = null;
         } else {
@@ -319,22 +361,30 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
                 Constants.PR_BORDER_BEFORE_COLOR,
                 Constants.PR_BORDER_BEFORE_STYLE,
                 Constants.PR_BORDER_BEFORE_WIDTH,
-                Constants.PR_PADDING_BEFORE);
+                Constants.PR_PADDING_BEFORE,
+                Constants.PR_X_BORDER_BEFORE_RADIUS_START,
+                Constants.PR_X_BORDER_BEFORE_RADIUS_END);
         initBorderInfo(pList, AFTER,
                 Constants.PR_BORDER_AFTER_COLOR,
                 Constants.PR_BORDER_AFTER_STYLE,
                 Constants.PR_BORDER_AFTER_WIDTH,
-                Constants.PR_PADDING_AFTER);
+                Constants.PR_PADDING_AFTER,
+                Constants.PR_X_BORDER_AFTER_RADIUS_START,
+                Constants.PR_X_BORDER_AFTER_RADIUS_END);
         initBorderInfo(pList, START,
                 Constants.PR_BORDER_START_COLOR,
                 Constants.PR_BORDER_START_STYLE,
                 Constants.PR_BORDER_START_WIDTH,
-                Constants.PR_PADDING_START);
+                Constants.PR_PADDING_START,
+                Constants.PR_X_BORDER_START_RADIUS_START,
+                Constants.PR_X_BORDER_START_RADIUS_END);
         initBorderInfo(pList, END,
                 Constants.PR_BORDER_END_COLOR,
                 Constants.PR_BORDER_END_STYLE,
                 Constants.PR_BORDER_END_WIDTH,
-                Constants.PR_PADDING_END);
+                Constants.PR_PADDING_END,
+                Constants.PR_X_BORDER_END_RADIUS_START,
+                Constants.PR_X_BORDER_END_RADIUS_END);
 
     }
 
@@ -347,10 +397,10 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
      * @throws PropertyException in case of an error
      */
     public static CommonBorderPaddingBackground getInstance(PropertyList pList)
-        throws PropertyException {
+    throws PropertyException {
 
         CommonBorderPaddingBackground newInstance
-                = new CommonBorderPaddingBackground(pList);
+        = new CommonBorderPaddingBackground(pList);
         CommonBorderPaddingBackground cachedInstance = null;
         /* if padding-* and background-position-* resolve to absolute lengths
          * the whole instance can be cached */
@@ -402,21 +452,26 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
     }
 
     private void initBorderInfo(PropertyList pList, int side,
-                    int colorProp, int styleProp, int widthProp, int paddingProp)
-                throws PropertyException {
+            int colorProp, int styleProp, int widthProp, int paddingProp,
+            int radiusStartProp, int radiusEndProp)
+    throws PropertyException {
 
         padding[side] = pList.get(paddingProp).getCondLength();
         // If style = none, force width to 0, don't get Color (spec 7.7.20)
         int style = pList.get(styleProp).getEnum();
-        if (style != Constants.EN_NONE) {
+      //  if (style != Constants.EN_NONE) {
             FOUserAgent ua = pList.getFObj().getUserAgent();
             setBorderInfo(BorderInfo.getInstance(style,
-                pList.get(widthProp).getCondLength(),
-                pList.get(colorProp).getColor(ua)), side);
-        }
+                    pList.get(widthProp).getCondLength(),
+                    pList.get(colorProp).getColor(ua),
+                    pList.get(radiusStartProp).getCondLength(),
+                    pList.get(radiusEndProp).getCondLength()), side);
+      //  }
 
     }
 
+
+
     /**
      * Sets a border.
      * @param info the border information
@@ -539,6 +594,40 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
         }
     }
 
+    /**
+     * Returns the border corner radius of the starting edge
+     *   i.e. the edge either adjacent to the before or start border.
+     * @param side the border side
+     * @param discard indicates whether the .conditionality component should be
+     *          considered (end of a reference-area)
+     * @param context the context for percentage calculations
+     * @return the border radius of the of the starting corner
+     */
+    public int getBorderRadiusStart(int side, boolean discard, PercentBaseContext context) {
+        if (borderInfo[side] == null) {
+            return 0;
+        } else {
+            return borderInfo[side].radiusStart.getLengthValue(context);
+        }
+    }
+
+    /**
+     * Returns the border corner radius of the ending edge
+     *   i.e. the edge either adjacent to the after or end border
+     * @param side the border side
+     * @param discard indicates whether the .conditionality component should be
+     *          considered (end of a reference-area)
+     * @param context the context for percentage calculations
+     * @return the border radius of the of the ending corner
+     */
+    public int getBorderRadiusEnd(int side, boolean discard, PercentBaseContext context) {
+        if (borderInfo[side] == null) {
+            return 0;
+        } else {
+            return borderInfo[side].radiusEnd.getLengthValue(context);
+        }
+    }
+
     /**
      * The border-color for the given side
      *
@@ -603,9 +692,9 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
      */
     public int getIPPaddingAndBorder(boolean discard, PercentBaseContext context) {
         return getPaddingStart(discard, context)
-            + getPaddingEnd(discard, context)
-            + getBorderStartWidth(discard)
-            + getBorderEndWidth(discard);
+        + getPaddingEnd(discard, context)
+        + getBorderStartWidth(discard)
+        + getBorderEndWidth(discard);
     }
 
     /**
@@ -617,18 +706,18 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
      */
     public int getBPPaddingAndBorder(boolean discard, PercentBaseContext context) {
         return getPaddingBefore(discard, context) + getPaddingAfter(discard, context)
-               + getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
+        + getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
     }
 
     /** {@inheritDoc} */
     public String toString() {
         return "CommonBordersAndPadding (Before, After, Start, End):\n"
-            + "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
-            + getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
-            + "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
-            + getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
-            + "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
-            + ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
+        + "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
+        + getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
+        + "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
+        + getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
+        + "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
+        + ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
     }
 
     /**
@@ -739,19 +828,20 @@ public class CommonBorderPaddingBackground {                    // CSOK: FinalCl
         if (obj instanceof CommonBorderPaddingBackground) {
             CommonBorderPaddingBackground cbpb = (CommonBorderPaddingBackground)obj;
             return (this.backgroundAttachment == cbpb.backgroundAttachment
-                && this.backgroundColor == cbpb.backgroundColor
-                && this.backgroundImage.equals(cbpb.backgroundImage)
-                && this.backgroundPositionHorizontal == cbpb.backgroundPositionHorizontal
-                && this.backgroundPositionVertical == cbpb.backgroundPositionVertical
-                && this.backgroundRepeat == cbpb.backgroundRepeat
-                && this.borderInfo[BEFORE] == cbpb.borderInfo[BEFORE]
-                && this.borderInfo[AFTER] == cbpb.borderInfo[AFTER]
-                && this.borderInfo[START] == cbpb.borderInfo[START]
-                && this.borderInfo[END] == cbpb.borderInfo[END]
-                && this.padding[BEFORE] == cbpb.padding[BEFORE]
-                && this.padding[AFTER] == cbpb.padding[AFTER]
-                && this.padding[START] == cbpb.padding[START]
-                && this.padding[END] == cbpb.padding[END]);
+                    && this.backgroundColor == cbpb.backgroundColor
+                    && this.backgroundImage.equals(cbpb.backgroundImage)
+                    && this.backgroundPositionHorizontal == cbpb.backgroundPositionHorizontal
+                    && this.backgroundPositionVertical == cbpb.backgroundPositionVertical
+                    && this.backgroundRepeat == cbpb.backgroundRepeat
+                    && this.borderInfo[BEFORE] == cbpb.borderInfo[BEFORE]
+                    && this.borderInfo[AFTER] == cbpb.borderInfo[AFTER]
+                    && this.borderInfo[START] == cbpb.borderInfo[START]
+                    && this.borderInfo[END] == cbpb.borderInfo[END]
+                    && this.padding[BEFORE] == cbpb.padding[BEFORE]
+                    && this.padding[AFTER] == cbpb.padding[AFTER]
+                    && this.padding[START] == cbpb.padding[START]
+                    && this.padding[END] == cbpb.padding[END]
+                    );
         }
 
         return false;
index 6039ad941a88a5ad0810b62c801ff1773eff87ef..a3d44f0a50e9910f6a9786e367fb4850673347ad 100644 (file)
@@ -79,19 +79,19 @@ public final class TraitSetter {
 
         addBorderTrait(area, bpProps, bNotFirst,
                 CommonBorderPaddingBackground.START,
-                BorderProps.SEPARATE, Trait.BORDER_START);
+                BorderProps.SEPARATE, Trait.BORDER_START, context);
 
         addBorderTrait(area, bpProps, bNotLast,
                 CommonBorderPaddingBackground.END,
-                BorderProps.SEPARATE, Trait.BORDER_END);
+                BorderProps.SEPARATE, Trait.BORDER_END, context);
 
         addBorderTrait(area, bpProps, false,
                 CommonBorderPaddingBackground.BEFORE,
-                BorderProps.SEPARATE, Trait.BORDER_BEFORE);
+                BorderProps.SEPARATE, Trait.BORDER_BEFORE, context);
 
         addBorderTrait(area, bpProps, false,
                 CommonBorderPaddingBackground.AFTER,
-                BorderProps.SEPARATE, Trait.BORDER_AFTER);
+                BorderProps.SEPARATE, Trait.BORDER_AFTER, context);
     }
 
     /**
@@ -104,13 +104,17 @@ public final class TraitSetter {
     private static void addBorderTrait(Area area,
                                        CommonBorderPaddingBackground bpProps,
                                        boolean bDiscard, int iSide, int mode,
-                                       Object oTrait) {
+                                       Object oTrait, PercentBaseContext context) {
         int iBP = bpProps.getBorderWidth(iSide, bDiscard);
-        if (iBP > 0) {
-            area.addTrait(oTrait,
-                    new BorderProps(bpProps.getBorderStyle(iSide),
-                            iBP, bpProps.getBorderColor(iSide),
-                            mode));
+        int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context);
+        int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context);
+        if (iBP > 0 || radiusStart > 0 || radiusEnd > 0) {
+            BorderProps bps =  new BorderProps(bpProps.getBorderStyle(iSide),
+                    iBP, bpProps.getBorderColor(iSide),
+                    mode);
+            bps.setRadiusStart(radiusStart);
+            bps.setRadiusEnd(radiusEnd);
+            area.addTrait(oTrait, bps);
         }
     }
 
@@ -125,19 +129,19 @@ public final class TraitSetter {
      */
     public static void addBorders(Area area, CommonBorderPaddingBackground bordProps,
                                   PercentBaseContext context) {
-        BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
+        BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE, context);
         if (bps != null) {
             area.addTrait(Trait.BORDER_BEFORE, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER, context);
         if (bps != null) {
             area.addTrait(Trait.BORDER_AFTER, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START, context);
         if (bps != null) {
             area.addTrait(Trait.BORDER_START, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END, context);
         if (bps != null) {
             area.addTrait(Trait.BORDER_END, bps);
         }
@@ -161,22 +165,23 @@ public final class TraitSetter {
                 boolean discardBefore, boolean discardAfter,
                 boolean discardStart, boolean discardEnd,
                 PercentBaseContext context) {
-        BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
+        BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE, context);
         if (bps != null && !discardBefore) {
             area.addTrait(Trait.BORDER_BEFORE, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER, context);
         if (bps != null && !discardAfter) {
             area.addTrait(Trait.BORDER_AFTER, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START, context);
         if (bps != null && !discardStart) {
             area.addTrait(Trait.BORDER_START, bps);
         }
-        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
+        bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END, context);
         if (bps != null && !discardEnd) {
             area.addTrait(Trait.BORDER_END, bps);
         }
+
     }
 
     /**
@@ -260,14 +265,19 @@ public final class TraitSetter {
 
     }
 
-    private static BorderProps getBorderProps(CommonBorderPaddingBackground bordProps, int side) {
+    private static BorderProps getBorderProps(CommonBorderPaddingBackground bordProps,
+            int side, PercentBaseContext context) {
         int width = bordProps.getBorderWidth(side, false);
-        if (width != 0) {
+        int radiusStart = bordProps.getBorderRadiusStart(side, false, context);
+        int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context);
+        if (width != 0 || radiusStart != 0 || radiusEnd != 0) {
             BorderProps bps;
             bps = new BorderProps(bordProps.getBorderStyle(side),
                                   width,
                                   bordProps.getBorderColor(side),
                                   BorderProps.SEPARATE);
+            bps.setRadiusStart(radiusStart);
+            bps.setRadiusEnd(radiusEnd);
             return bps;
         } else {
             return null;
@@ -280,12 +290,19 @@ public final class TraitSetter {
         if (width != 0) {
             BorderProps bps = new BorderProps(borderInfo.getStyle(), width, borderInfo.getColor(),
                     (outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER));
+
+            //TODO Enabling this causes graphic problems. Revisit!
+            /*
+            bps.setRadiusStart(borderInfo.getRadiusStart().getLengthValue());
+            bps.setRadiusEnd(borderInfo.getRadiusEnd().getLengthValue());
+            */
             return bps;
         } else {
             return null;
         }
     }
 
+
     /**
      * Add background to an area. This method is mainly used by table-related layout
      * managers to add background for column, body or row. Since the area corresponding to
index cfce3a875cc52592574eb365c13b3268fca64a98..ea78b958ca10c9b0b9a3c68252a19cf0a0f1c94b 100644 (file)
@@ -48,6 +48,7 @@ import org.apache.fop.area.inline.Viewport;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
 import org.apache.fop.fonts.FontMetrics;
+import org.apache.fop.render.intermediate.BorderPainter;
 import org.apache.fop.traits.BorderProps;
 
 /**
@@ -151,11 +152,20 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
         BorderProps bpsStart = (BorderProps)borderArea.getTrait(Trait.BORDER_START);
         BorderProps bpsEnd = (BorderProps)borderArea.getTrait(Trait.BORDER_END);
 
+        Trait.Background backgroundTrait
+            = (Trait.Background)backgroundArea.getTrait(Trait.BACKGROUND);
+
         drawBackground(startx, starty, width, height,
                 (Trait.Background) backgroundArea.getTrait(Trait.BACKGROUND),
                 bpsBefore, bpsAfter, bpsStart, bpsEnd);
+
+        Color bg = null;
+        if (backgroundTrait != null) {
+            bg = backgroundTrait.getColor();
+        }
+
         drawBorders(startx, starty, width, height,
-                bpsBefore, bpsAfter, bpsStart, bpsEnd);
+                bpsBefore, bpsAfter, bpsStart, bpsEnd, bg);
     }
 
     /**
@@ -199,14 +209,23 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
                 paddRectHeight -= bpsAfter.width / 1000f;
             }
 
+            saveGraphicsState();
+
+            //TODO remove this choice
+            if (BorderPainter.isRoundedCornersSupported()) {
+                clipBackground(sx, sy,  paddRectWidth, paddRectHeight,
+                        bpsBefore, bpsAfter, bpsStart,  bpsEnd);
+            } else {
+                clipRect(sx, sy,  paddRectWidth, paddRectHeight);
+            }
+
             if (back.getColor() != null) {
                 updateColor(back.getColor(), true);
                 fillRect(sx, sy, paddRectWidth, paddRectHeight);
             }
             if (back.getImageInfo() != null) {
                 ImageSize imageSize = back.getImageInfo().getSize();
-                saveGraphicsState();
-                clipRect(sx, sy, paddRectWidth, paddRectHeight);
+
                 int horzCount = (int)((paddRectWidth
                         * 1000 / imageSize.getWidthMpt()) + 1.0f);
                 int vertCount = (int)((paddRectHeight
@@ -242,12 +261,33 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
                         drawImage(back.getURL(), pos);
                     }
                 }
-
-                restoreGraphicsState();
             }
+            restoreGraphicsState();
         }
     }
 
+    /**
+     * Clip the background to the inner border.
+     * This draws the border traits given the position and the traits.
+     *
+     * @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
+     * @param bpsBefore the border-before traits
+     * @param bpsAfter the border-after traits
+     * @param bpsStart the border-start traits
+     * @param bpsEnd the border-end traits
+     */
+    protected void clipBackground (float startx, float starty,
+            float width, float height,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+
+        clipRect(startx, starty, width, height);
+
+    }
+
     /**
      * Draw the borders.
      * This draws the border traits given the position and the traits.
@@ -264,9 +304,9 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
     protected void drawBorders(                                  // CSOK: ParameterNumber
             float startx, float starty, float width, float height,
             BorderProps bpsBefore, BorderProps bpsAfter,
-            BorderProps bpsStart, BorderProps bpsEnd) {
+            BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) {
         Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height);
-        drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+        drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd, innerBackgroundColor);
     }
 
     private static final int BEFORE = 0;
@@ -284,7 +324,8 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
      */
     protected void drawBorders(                                  // CSOK: MethodLength
             Rectangle2D.Float borderRect,
-            BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) {
+            BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd,
+            Color innerBackgroundColor) {
         //TODO generalize each of the four conditions into using a parameterized drawBorder()
         boolean[] border = new boolean[] {
                 (bpsBefore != null), (bpsEnd != null),
index 21d4faf56ec45c1b01f3639041b5d4f18454f6e0..e90f0b29a2ff232c5d994547724517b9a1469fff 100644 (file)
@@ -23,6 +23,7 @@ import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.geom.AffineTransform;
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.fop.afp.AFPDitheredRectanglePainter;
@@ -79,6 +80,13 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
     private Map/*<String,String>*/pageSegmentMap
         = new java.util.HashMap/*<String,String>*/();
 
+
+    // Rounded corners are cached at the document level
+    private Map/*<String, String>*/ roundedCornerNameCache
+            = new HashMap()/*<String, String>*/;
+
+    private int roundedCornerCount = 0;
+
     /** Medium Map referenced on previous page **/
     private String lastMediumMap;
 
@@ -339,6 +347,49 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
         }
     }
 
+    /**
+     * Corner images can be reused by storing at the document level in the AFP
+     * The cache is used to map cahced images to caller generated descriptions of the corner
+     * @param cornerKey caller's identifier for the corner
+     * @return document id of the corner image
+     */
+    public String cacheRoundedCorner(String cornerKey) {
+
+        // Make a unique id
+        StringBuffer idBuilder = new StringBuffer("RC");
+
+        String tmp = Integer.toHexString(roundedCornerCount).toUpperCase();
+        if (tmp.length() > 6) {
+            //Will never happen
+            //log.error("Rounded corners cache capacity exceeded");
+            //We should get a visual clue
+            roundedCornerCount = 0;
+            tmp = "000000";
+        } else if (tmp.length() < 6) {
+            for (int i = 0; i < 6 - tmp.length(); i++) {
+                idBuilder.append("0");
+            }
+            idBuilder.append(tmp);
+        }
+
+       roundedCornerCount++;
+
+       String id =  idBuilder.toString();
+
+       //cache the corner id
+       roundedCornerNameCache.put(cornerKey, id);
+       return id;
+    }
+    /**
+     * This method returns the an id that identifies a cached corner or null if non existent
+     * @param cornerKey caller's identifier for the corner
+     * @return document id of the corner image
+     */
+    public String getCachedRoundedCorner(String cornerKey) {
+        return (String)roundedCornerNameCache.get(cornerKey);
+    }
+
+
     // ---=== AFPCustomizable ===---
 
     /** {@inheritDoc} */
index 24426321312818fd72202559fb5e2e088ab63993..0e4e7a63a5da63ed19f1766096d562852f9b1345 100644 (file)
 
 package org.apache.fop.render.afp;
 
+import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
 import java.util.Map;
 
 import org.apache.fop.afp.AFPDataObjectInfo;
@@ -37,21 +40,60 @@ public abstract class AFPImageHandler implements ImageHandlerBase {
     private static final int Y = 1;
 
     /** foreign attribute reader */
-    private final AFPForeignAttributeReader foreignAttributeReader
+    private static final AFPForeignAttributeReader FOREIGN_ATTRIBUTE_READER
         = new AFPForeignAttributeReader();
 
+    /**
+     * Generates an intermediate AFPDataObjectInfo that is later used to construct
+     * the appropriate data object in the AFP DataStream.
+     *
+     * @param rendererImageInfo the renderer image info
+     * @return a data object info object
+     * @throws IOException thrown if an I/O exception of some sort has occurred.
+     */
+    public AFPDataObjectInfo generateDataObjectInfo(
+            AFPRendererImageInfo rendererImageInfo) throws IOException {
+        AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();
+
+        // set resource information
+        dataObjectInfo.setResourceInfo(createResourceInformation(
+                rendererImageInfo.getURI(),
+                rendererImageInfo.getForeignAttributes()));
+
+
+        Point origin = rendererImageInfo.getOrigin();
+        Rectangle2D position = rendererImageInfo.getPosition();
+        int srcX = Math.round(origin.x + (float)position.getX());
+        int srcY = Math.round(origin.y + (float)position.getY());
+        Rectangle targetRect = new Rectangle(
+                srcX,
+                srcY,
+                (int)Math.round(position.getWidth()),
+                (int)Math.round(position.getHeight()));
+
+        AFPRendererContext rendererContext
+            = (AFPRendererContext)rendererImageInfo.getRendererContext();
+        AFPInfo afpInfo = rendererContext.getInfo();
+        AFPPaintingState paintingState = afpInfo.getPaintingState();
+
+        dataObjectInfo.setObjectAreaInfo(createObjectAreaInfo(paintingState, targetRect));
+
+        return dataObjectInfo;
+    }
+
     /**
      * Sets resource information on the data object info.
-     * @param dataObjectInfo the data object info instance
      * @param uri the image's URI (or null if no URI is available)
      * @param foreignAttributes a Map of foreign attributes (or null)
+     * @return the resource information object
      */
-    protected void setResourceInformation(AFPDataObjectInfo dataObjectInfo,
+    public static AFPResourceInfo createResourceInformation(
             String uri, Map foreignAttributes) {
         AFPResourceInfo resourceInfo
-            = foreignAttributeReader.getResourceInfo(foreignAttributes);
+            = FOREIGN_ATTRIBUTE_READER.getResourceInfo(foreignAttributes);
         resourceInfo.setUri(uri);
-        dataObjectInfo.setResourceInfo(resourceInfo);
+
+       return resourceInfo;
     }
 
     /**
index 07b712ceb0dd47e1e1da6fac0b5075b44bafcdef..5617c46683431cd1b85fd2efe63c735abc1b0e78 100644 (file)
@@ -26,8 +26,6 @@ import java.io.IOException;
 import org.apache.xmlgraphics.image.loader.Image;
 import org.apache.xmlgraphics.image.loader.ImageFlavor;
 import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
-import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
-import org.apache.xmlgraphics.util.MimeConstants;
 
 import org.apache.fop.afp.AFPDataObjectInfo;
 import org.apache.fop.afp.AFPGraphics2D;
@@ -86,9 +84,10 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH
         AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)createDataObjectInfo();
 
         // set resource information
-        setResourceInformation(graphicsObjectInfo,
+
+        graphicsObjectInfo.setResourceInfo(createResourceInformation(
                 image.getInfo().getOriginalURI(),
-                afpContext.getForeignAttributes());
+                afpContext.getForeignAttributes()));
 
         // Positioning
         graphicsObjectInfo.setObjectAreaInfo(
index ca0544542270dcd3f4ab7800885904a3f085e2a9..48eeb79bf86bf5a9fd24b3d7d32b8f6d2b45ade8 100644 (file)
@@ -317,9 +317,9 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
         AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)createDataObjectInfo();
 
         // set resource information
-        setResourceInformation(imageObjectInfo,
+        imageObjectInfo.setResourceInfo(createResourceInformation(
                 image.getInfo().getOriginalURI(),
-                afpContext.getForeignAttributes());
+                afpContext.getForeignAttributes()));
         setDefaultResourceLevel(imageObjectInfo, afpContext.getResourceManager());
 
         // Positioning
index 0bbfef3e815f3a4fa58e93bcf9d4d013dde63cd3..895316e6b96a27e708fb261ff280253cb2a121e7 100644 (file)
@@ -21,20 +21,37 @@ package org.apache.fop.render.afp;
 
 import java.awt.Color;
 import java.awt.Dimension;
+import java.awt.Graphics2D;
 import java.awt.Paint;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.security.MessageDigest;
 import java.util.Map;
 
 import org.w3c.dom.Document;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
 import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
 import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
 
 import org.apache.fop.afp.AFPBorderPainter;
+import org.apache.fop.afp.AFPObjectAreaInfo;
 import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceInfo;
 import org.apache.fop.afp.AFPUnitConverter;
 import org.apache.fop.afp.AbstractAFPPainter;
 import org.apache.fop.afp.BorderPaintingInfo;
@@ -51,6 +68,7 @@ import org.apache.fop.afp.ptoca.PtocaProducer;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.render.ImageHandlerUtil;
 import org.apache.fop.render.RenderingContext;
 import org.apache.fop.render.intermediate.AbstractIFPainter;
 import org.apache.fop.render.intermediate.BorderPainter;
@@ -66,8 +84,11 @@ import org.apache.fop.util.CharUtilities;
  */
 public class AFPPainter extends AbstractIFPainter {
 
+
+
+
     //** logging instance */
-    //private static Log log = LogFactory.getLog(AFPPainter.class);
+    private static Log log = LogFactory.getLog(AFPPainter.class);
 
     private static final int X = 0;
     private static final int Y = 1;
@@ -90,8 +111,9 @@ public class AFPPainter extends AbstractIFPainter {
         super();
         this.documentHandler = documentHandler;
         this.state = IFState.create();
+
         this.borderPainter = new AFPBorderPainterAdapter(
-                new AFPBorderPainter(getPaintingState(), getDataStream()));
+                new AFPBorderPainter(getPaintingState(), getDataStream()), this, documentHandler);
         this.rectanglePainter = documentHandler.createRectanglePainter();
         this.unitConv = getPaintingState().getUnitConverter();
     }
@@ -115,7 +137,7 @@ public class AFPPainter extends AbstractIFPainter {
 
     /** {@inheritDoc} */
     public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
-            throws IFException {
+    throws IFException {
         //AFP doesn't support clipping, so we treat viewport like a group
         //this is the same code as for startGroup()
         try {
@@ -172,13 +194,13 @@ public class AFPPainter extends AbstractIFPainter {
 
     /** {@inheritDoc} */
     protected RenderingContext createRenderingContext() {
-        AFPRenderingContext psContext = new AFPRenderingContext(
+        AFPRenderingContext renderingContext = new AFPRenderingContext(
                 getUserAgent(),
                 documentHandler.getResourceManager(),
                 getPaintingState(),
                 getFontInfo(),
                 getContext().getForeignAttributes());
-        return psContext;
+        return renderingContext;
     }
 
     /** {@inheritDoc} */
@@ -196,6 +218,32 @@ public class AFPPainter extends AbstractIFPainter {
         }
     }
 
+    /** {@inheritDoc} */
+    protected void drawImage(Image image, Rectangle rect,
+            RenderingContext context, boolean convert, Map additionalHints)
+    throws IOException, ImageException {
+
+
+        AFPRenderingContext afpContext = (AFPRenderingContext)context;
+
+        AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation(
+                image.getInfo().getOriginalURI(),
+                afpContext.getForeignAttributes());
+
+        //Check if the image is cached before processing it again
+        if (afpContext.getResourceManager().isObjectCached(resourceInfo)) {
+
+            AFPObjectAreaInfo areaInfo = AFPImageHandler.createObjectAreaInfo(
+                    afpContext.getPaintingState(), rect);
+
+            afpContext.getResourceManager().includeCachedObject(resourceInfo, areaInfo);
+
+        } else {
+            super.drawImage(image, rect, context, convert, additionalHints);
+        }
+
+    }
+
     /** {@inheritDoc} */
     public void drawImage(Document doc, Rectangle rect) throws IFException {
         drawImageUsingDocument(doc, rect);
@@ -233,24 +281,769 @@ public class AFPPainter extends AbstractIFPainter {
 
     /** {@inheritDoc} */
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException {
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             try {
-                this.borderPainter.drawBorders(rect, before, after, start, end);
-            } catch (IOException ife) {
-                throw new IFException("IO error while painting borders", ife);
+                this.borderPainter.drawBorders(rect, before, after, start, end,
+                        innerBackgroundColor);
+            } catch (IFException ife) {
+                throw new IFException("Error while painting borders", ife);
             }
         }
     }
 
+
     //TODO Try to resolve the name-clash between the AFPBorderPainter in the afp package
     //and this one. Not done for now to avoid a lot of re-implementation and code duplication.
+
     private static class AFPBorderPainterAdapter extends BorderPainter {
 
+
+        private final class BorderImagePainter implements Graphics2DImagePainter {
+            private final double esf;
+            private final Rectangle borderRect;
+            private final BorderProps bpsStart;
+            private final BorderProps bpsEnd;
+            private final BorderProps bpsBefore;
+            private final BorderProps bpsAfter;
+            private final boolean[] roundCorner;
+            private final Color innerBackgroundColor;
+
+            private BorderImagePainter(double esf, Rectangle borderRect,
+                    BorderProps bpsStart, BorderProps bpsEnd,
+                    BorderProps bpsBefore, BorderProps bpsAfter,
+                    boolean[] roundCorner, Color innerBackgroundColor) {
+                this.esf = esf;
+                this.borderRect = borderRect;
+                this.bpsStart = bpsStart;
+                this.bpsBefore = bpsBefore;
+                this.roundCorner = roundCorner;
+                this.bpsEnd = bpsEnd;
+                this.bpsAfter = bpsAfter;
+                this.innerBackgroundColor = innerBackgroundColor;
+            }
+
+            public void paint(Graphics2D g2d, Rectangle2D area) {
+
+                //background
+                Area background = new Area(area);
+                Area cornerRegion = new Area();
+                Area[] cornerBorder
+                        = new Area[]{new Area(), new Area(), new Area(), new Area()};
+
+                if (roundCorner[BEFORE_START]) {
+
+                    AffineTransform transform =  new AffineTransform();
+                    int beforeRadius = (int)(esf * bpsBefore.getRadiusStart());
+                    int startRadius = (int)(esf * bpsStart.getRadiusStart());
+
+                    int beforeWidth = bpsBefore.width;
+                    int startWidth = bpsStart.width;
+                    int corner = BEFORE_START;
+
+                    background.subtract(makeCornerClip(beforeRadius, startRadius,
+                            transform));
+
+                    Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+                    clip.transform(transform);
+                    cornerRegion.add(clip);
+
+                    cornerBorder[BEFORE].add(makeCornerBorderBPD(beforeRadius,
+                                    startRadius, beforeWidth, startWidth, transform));
+
+                    cornerBorder[START].add(makeCornerBorderIPD(beforeRadius,
+                            startRadius, beforeWidth, startWidth, transform));
+                }
+
+                if (roundCorner[BEFORE_END]) {
+                    AffineTransform transform
+                            = new AffineTransform(-1, 0, 0, 1, borderRect.width, 0);
+
+                    int beforeRadius = (int)(esf * bpsBefore.getRadiusEnd());
+                    int startRadius = (int)(esf * bpsEnd.getRadiusStart());
+
+                    int beforeWidth = bpsBefore.width;
+                    int startWidth = bpsEnd.width;
+                    int corner = BEFORE_END;
+
+                    background.subtract(makeCornerClip(beforeRadius, startRadius,
+                            transform));
+
+                    Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+                    clip.transform(transform);
+                    cornerRegion.add(clip);
+
+                    cornerBorder[BEFORE].add(makeCornerBorderBPD(beforeRadius,
+                                    startRadius, beforeWidth, startWidth, transform));
+
+                    cornerBorder[END].add(makeCornerBorderIPD(beforeRadius,
+                            startRadius, beforeWidth, startWidth, transform));
+                }
+
+                if (roundCorner[AFTER_END]) {
+                    AffineTransform transform = new AffineTransform(-1, 0, 0, -1,
+                            borderRect.width, borderRect.height);
+
+                    int beforeRadius = (int)(esf * bpsAfter.getRadiusEnd());
+                    int startRadius = (int)(esf * bpsEnd.getRadiusEnd());
+
+                    int beforeWidth = bpsAfter.width;
+                    int startWidth = bpsEnd.width;
+                    int corner = AFTER_END;
+
+                    background.subtract(makeCornerClip(beforeRadius, startRadius,
+                            transform));
+
+                    Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+                    clip.transform(transform);
+                    cornerRegion.add(clip);
+
+                    cornerBorder[AFTER].add(makeCornerBorderBPD(beforeRadius,
+                            startRadius, beforeWidth, startWidth, transform));
+                    cornerBorder[END].add(makeCornerBorderIPD(beforeRadius,
+                            startRadius, beforeWidth, startWidth, transform));
+                }
+
+                if (roundCorner[AFTER_START]) {
+                    AffineTransform transform
+                            = new AffineTransform(1, 0, 0, -1, 0, borderRect.height);
+
+                    int beforeRadius = (int)(esf * bpsAfter.getRadiusStart());
+                    int startRadius = (int)(esf * bpsStart.getRadiusEnd());
+
+                    int beforeWidth = bpsAfter.width;
+                    int startWidth = bpsStart.width;
+                    int corner = AFTER_START;
+
+                    background.subtract(makeCornerClip(beforeRadius, startRadius,
+                            transform));
+
+                    Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+                    clip.transform(transform);
+                    cornerRegion.add(clip);
+
+                    cornerBorder[AFTER].add(makeCornerBorderBPD(beforeRadius,
+                                    startRadius, beforeWidth, startWidth, transform));
+                    cornerBorder[START].add(makeCornerBorderIPD(beforeRadius,
+                            startRadius, beforeWidth, startWidth, transform));
+                }
+
+                g2d.setColor(innerBackgroundColor);
+                g2d.fill(background);
+
+                //paint the borders
+                //TODO refactor to repeating code into method
+                if (bpsBefore != null && bpsBefore.width > 0) {
+                    GeneralPath borderPath = new GeneralPath();
+                    borderPath.moveTo(0, 0);
+                    borderPath.lineTo(borderRect.width, 0);
+                    borderPath.lineTo(
+                            borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width),
+                            bpsBefore.width);
+                    borderPath.lineTo(bpsStart == null ? 0 : bpsStart.width, bpsBefore.width);
+
+                    Area border = new Area(borderPath);
+
+                    border.subtract(cornerRegion);
+
+                    g2d.setColor(bpsBefore.color);
+                    g2d.fill(border);
+                    g2d.fill(cornerBorder[BEFORE]);
+                }
+
+                if (bpsEnd != null && bpsEnd.width > 0) {
+                    GeneralPath borderPath = new GeneralPath();
+                    borderPath.moveTo(borderRect.width, 0);
+                    borderPath.lineTo(borderRect.width, borderRect.height);
+                    borderPath.lineTo(
+                            borderRect.width - bpsEnd.width,
+                            borderRect.height - (bpsAfter == null ? 0 : bpsAfter.width));
+                    borderPath.lineTo(
+                            borderRect.width - bpsEnd.width,
+                            bpsBefore == null ? 0 : bpsBefore.width);
+
+                    Area border = new Area(borderPath);
+
+                    border.subtract(cornerRegion);
+
+                    g2d.setColor(bpsEnd.color);
+                    g2d.fill(border);
+                    g2d.fill(cornerBorder[END]);
+                }
+
+                if (bpsAfter != null && bpsAfter.width > 0) {
+                    GeneralPath borderPath = new GeneralPath();
+                    borderPath.moveTo(0, borderRect.height);
+                    borderPath.lineTo(borderRect.width, borderRect.height);
+                    borderPath.lineTo(
+                            borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width),
+                            borderRect.height - bpsAfter.width);
+                    borderPath.lineTo(
+                            bpsStart == null ? 0 : bpsStart.width,
+                            borderRect.height - bpsAfter.width);
+                    Area border = new Area(borderPath);
+
+                    border.subtract(cornerRegion);
+
+                    g2d.setColor(bpsAfter.color);
+                    g2d.fill(border);
+                    g2d.fill(cornerBorder[AFTER]);
+                }
+
+                if (bpsStart != null && bpsStart.width > 0) {
+
+                    GeneralPath borderPath = new GeneralPath();
+                    borderPath.moveTo(bpsStart.width,
+                            bpsBefore == null ? 0 : bpsBefore.width);
+                    borderPath.lineTo(bpsStart.width,
+                            borderRect.height - (bpsAfter == null ? 0 : bpsAfter.width));
+                    borderPath.lineTo(0, borderRect.height);
+                    borderPath.lineTo(0, 0);
+
+                    Area border = new Area(borderPath);
+
+                    border.subtract(cornerRegion);
+
+                    g2d.setColor(bpsStart.color);
+                    g2d.fill(border);
+                    g2d.fill(cornerBorder[START]);
+                }
+            }
+
+            public Dimension getImageSize() {
+                return borderRect.getSize();
+            }
+        }
+
+        public static final String CORNER_MODE_PROPERTY = "fop.round-corners.afp";
+        public static final String MODE_SEPERATE = "seperate";
+        public static final String MODE_ALL_IN_ONE = "all-in-one";
+        public static final String MODE_DEFAULT = "all-in-one";
+
         private AFPBorderPainter delegate;
+        private final AFPPainter painter;
+        private final AFPDocumentHandler documentHandler;
 
-        public AFPBorderPainterAdapter(AFPBorderPainter borderPainter) {
+        public AFPBorderPainterAdapter(AFPBorderPainter borderPainter, AFPPainter painter,
+                AFPDocumentHandler documentHandler) {
             this.delegate = borderPainter;
+            this.painter = painter;
+            this.documentHandler = documentHandler;
+        }
+
+        public void drawBorders(final Rectangle borderRect,
+                final BorderProps bpsBefore, final BorderProps bpsAfter,
+                final BorderProps bpsStart, final BorderProps bpsEnd, Color innerBackgroundColor)
+                throws IFException {
+
+            if (isRoundedCornersSupported()) {
+                if (MODE_SEPERATE.equals(System.getProperty(CORNER_MODE_PROPERTY))) {
+                    try {
+                        drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+                    } catch (IOException ioe) {
+                        throw new IFException("Error drawing border", ioe);
+                    }
+                    drawSeperateRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
+                            innerBackgroundColor);
+                } else {
+                    drawRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
+                            innerBackgroundColor);
+                }
+            } else {
+                try {
+                    drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+                } catch (IOException ioe) {
+                    throw new IFException("Error drawing border", ioe);
+                }
+            }
+        }
+
+        private boolean isModeSeperate() {
+            return (MODE_SEPERATE.equals(System.getProperty(CORNER_MODE_PROPERTY, MODE_DEFAULT)));
+        }
+
+        private boolean isModeAllInOne() {
+            return (MODE_ALL_IN_ONE.equals(System.getProperty(CORNER_MODE_PROPERTY, MODE_DEFAULT)));
+        }
+
+        private boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+                BorderProps bpsStart, BorderProps bpsEnd) {
+
+            boolean rtn = !(isRoundedCornersSupported() && isModeAllInOne()
+                    && hasRoundedCorners(bpsBefore,  bpsAfter,
+                     bpsStart,  bpsEnd));
+            return rtn;
+        }
+
+        private boolean hasRoundedCorners( final BorderProps bpsBefore, final BorderProps bpsAfter,
+                final BorderProps bpsStart, final BorderProps bpsEnd) {
+
+            return ((bpsStart == null ? false : bpsStart.getRadiusStart() > 0)
+                        && (bpsBefore == null ? false : bpsBefore.getRadiusStart() > 0))
+                || ((bpsBefore == null ? false : bpsBefore.getRadiusEnd() > 0)
+                        && (bpsEnd == null ? false : bpsEnd.getRadiusStart() > 0))
+                || ((bpsEnd == null ? false : bpsEnd.getRadiusEnd() > 0)
+                        && (bpsAfter == null ? false : bpsAfter.getRadiusEnd() > 0))
+                        || ((bpsAfter == null ? false : bpsAfter.getRadiusStart() > 0)
+                        && (bpsStart == null ? false : bpsStart.getRadiusEnd() > 0));
+        }
+
+        private void drawSeperateRoundedCorners(final Rectangle borderRect,
+                final BorderProps bpsBefore, final BorderProps bpsAfter,
+                final BorderProps bpsStart, final BorderProps bpsEnd,
+                final Color innerBackgroundColor)
+                    throws IFException {
+
+            double esf = cornerScaleFactor(borderRect.width, borderRect.height,
+                    bpsBefore,  bpsAfter,
+                    bpsStart,  bpsEnd);
+
+            if (bpsBefore != null && bpsStart != null) {
+                int beforeRadiusStart = (int)(esf *  bpsBefore.getRadiusStart());
+                int startRadiusStart = (int)(esf *  bpsStart.getRadiusStart());
+
+                Rectangle area = new Rectangle(borderRect.x, borderRect.y,
+                        startRadiusStart,  beforeRadiusStart);
+
+                drawCorner(BEFORE_START, area, bpsBefore, bpsStart,
+                        beforeRadiusStart, startRadiusStart, innerBackgroundColor);
+            }
+
+            if (bpsEnd != null && bpsBefore != null) {
+                int endRadiusStart = (int)(esf *  bpsEnd.getRadiusStart());
+                int beforeRadiusEnd = (int)(esf *  bpsBefore.getRadiusEnd());
+                Rectangle area = new Rectangle(borderRect.x + borderRect.width - endRadiusStart,
+                        borderRect.y, endRadiusStart,  beforeRadiusEnd);
+
+                drawCorner(BEFORE_END, area, bpsBefore, bpsEnd,
+                        beforeRadiusEnd, endRadiusStart, innerBackgroundColor);
+            }
+
+
+            if (bpsEnd != null && bpsAfter != null) {
+                int endRadiusEnd = (int)(esf *  bpsEnd.getRadiusEnd());
+                int afterRadiusEnd = (int)(esf *  bpsAfter.getRadiusEnd());
+
+                Rectangle area = new Rectangle(borderRect.x + borderRect.width - endRadiusEnd,
+                        borderRect.y + borderRect.height - afterRadiusEnd,
+                        endRadiusEnd,   afterRadiusEnd);
+
+                drawCorner(AFTER_END, area, bpsAfter, bpsEnd,
+                        afterRadiusEnd, endRadiusEnd, innerBackgroundColor);
+
+            }
+
+            if (bpsStart != null && bpsAfter != null) {
+                int startRadiusEnd = (int)(esf *  bpsStart.getRadiusEnd());
+                int afterRadiusStart = (int)(esf *  bpsAfter.getRadiusStart());
+                Rectangle area = new Rectangle(borderRect.x ,
+                        borderRect.y + borderRect.height - afterRadiusStart,
+                        startRadiusEnd, afterRadiusStart);
+
+                drawCorner(AFTER_START, area, bpsAfter, bpsStart,
+                        afterRadiusStart, startRadiusEnd, innerBackgroundColor);
+            }
+        }
+
+        private void drawRoundedCorners(final Rectangle borderRect,
+                final BorderProps bpsBefore, final BorderProps bpsAfter,
+                final BorderProps bpsStart, final BorderProps bpsEnd,
+                final Color innerBackgroundColor)
+        throws IFException {
+
+
+            final double esf = cornerScaleFactor(borderRect.width, borderRect.height,
+                    bpsBefore,  bpsAfter,
+                    bpsStart,  bpsEnd);
+
+            final boolean[] roundCorner = new boolean[]{
+                    bpsBefore != null  && bpsStart != null
+                        && bpsBefore.getRadiusStart() > 0
+                        && bpsStart.getRadiusStart() > 0
+                        && bpsBefore.mode != BorderProps.COLLAPSE_OUTER
+                        && bpsStart.mode != BorderProps.COLLAPSE_OUTER,
+                    bpsEnd != null && bpsBefore != null
+                        && bpsEnd.getRadiusStart() > 0
+                        && bpsBefore.getRadiusEnd() > 0
+                        && bpsEnd.mode != BorderProps.COLLAPSE_OUTER
+                        && bpsBefore.mode != BorderProps.COLLAPSE_OUTER,
+                    bpsEnd != null && bpsAfter != null
+                        && bpsEnd.getRadiusEnd() > 0
+                        && bpsAfter.getRadiusEnd() > 0
+                        && bpsEnd.mode != BorderProps.COLLAPSE_OUTER
+                        && bpsAfter.mode != BorderProps.COLLAPSE_OUTER,
+                    bpsStart != null && bpsAfter != null
+                        && bpsStart.getRadiusEnd() > 0
+                        && bpsAfter.getRadiusStart() > 0
+                        && bpsStart.mode != BorderProps.COLLAPSE_OUTER
+                        && bpsAfter.mode != BorderProps.COLLAPSE_OUTER
+                   };
+
+
+            if (!roundCorner[BEFORE_START] && !roundCorner[BEFORE_END]
+                        && !roundCorner[AFTER_END] && !roundCorner[AFTER_START]) {
+                try {
+                    drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+                } catch (IOException ioe) {
+                    throw new IFException("IO error drawing borders", ioe);
+                }
+                return;
+            }
+
+            String areaKey = makeKey(borderRect,
+                    bpsBefore, bpsEnd, bpsAfter,
+                    bpsStart, innerBackgroundColor);
+
+            Graphics2DImagePainter painter = null;
+            String name = documentHandler.getCachedRoundedCorner(areaKey);
+
+            if (name == null) {
+
+                name = documentHandler.cacheRoundedCorner(areaKey);
+
+                painter = new BorderImagePainter(esf, borderRect,
+                        bpsStart, bpsEnd, bpsBefore, bpsAfter,
+                        roundCorner, innerBackgroundColor);
+            }
+            paintCornersAsBitmap(painter, borderRect, name);
+        }
+
+
+        private Area makeCornerClip(final int beforeRadius, final int startRadius,
+                 final AffineTransform transform) {
+
+            Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);
+
+            Area clip = new Area(clipR);
+
+            Ellipse2D.Double e = new  Ellipse2D.Double();
+            e.x = 0;
+            e.y = 0;
+            e.width = 2 * startRadius;
+            e.height = 2 * beforeRadius;
+
+            clip.subtract(new Area(e));
+
+            clip.transform(transform);
+            return clip;
+        }
+
+
+        private Area makeCornerBorderBPD(final int beforeRadius, final int startRadius,
+                final int beforeWidth, final int startWidth, final AffineTransform transform) {
+
+            Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);
+
+            Ellipse2D.Double e = new  Ellipse2D.Double();
+            e.x = 0;
+            e.y = 0;
+            e.width = 2 * startRadius;
+            e.height = 2 * beforeRadius;
+
+            Ellipse2D.Double i = new  Ellipse2D.Double();
+            i.x = startWidth;
+            i.y = beforeWidth;
+            i.width = 2 * (startRadius - startWidth);
+            i.height = 2 * (beforeRadius - beforeWidth);
+
+            Area clip = new Area(e);
+            clip.subtract(new Area(i));
+            clip.intersect(new Area(clipR));
+
+            GeneralPath cut = new GeneralPath();
+            cut.moveTo(0, 0);
+            float borderWidthRatio = ((float)beforeWidth) / startWidth;
+            if (beforeWidth * startRadius > startWidth * beforeRadius) {
+                cut.lineTo(startRadius, borderWidthRatio * startRadius);
+                cut.lineTo(startRadius, 0);
+
+            } else {
+                cut.lineTo(startRadius, borderWidthRatio * startRadius);
+                cut.lineTo(startRadius, 0);
+            }
+
+            clip.intersect(new Area(cut));
+            clip.transform(transform);
+            return clip;
+        }
+
+
+        private Area makeCornerBorderIPD(final int beforeRadius, final int startRadius,
+                final int beforeWidth, final int startWidth, final AffineTransform transform) {
+
+            Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);
+
+
+            Ellipse2D.Double e = new  Ellipse2D.Double();
+            e.x = 0;
+            e.y = 0;
+            e.width = 2 * startRadius;
+            e.height = 2 * beforeRadius;
+
+            Ellipse2D.Double i = new  Ellipse2D.Double();
+            i.x = startWidth;
+            i.y = beforeWidth;
+            i.width = 2 * (startRadius - startWidth);
+            i.height = 2 * (beforeRadius - beforeWidth);
+
+            Area clip = new Area(e);
+            clip.subtract(new Area(i));
+            clip.intersect(new Area(clipR));
+
+            GeneralPath cut = new GeneralPath();
+            cut.moveTo(0, 0);
+            float borderWidthRatio = ((float)beforeWidth) / startWidth;
+            if (beforeWidth * startRadius > startWidth * beforeRadius) {
+                cut.lineTo(startRadius, borderWidthRatio * startRadius);
+                cut.lineTo(startRadius, 0);
+
+            } else {
+                cut.lineTo(startRadius, borderWidthRatio * startRadius);
+                cut.lineTo(startRadius, 0);
+            }
+
+            clip.subtract(new Area(cut));
+            clip.transform(transform);
+            return clip;
+        }
+
+
+        private void paintCorner(final Graphics2D g2d, final int beforeWidth,
+                final int startWidth, final  int beforeRadius,
+                final int startRadius, final Color innerBackgroundColor,
+                final Color beforeColor, final Color startColor) {
+
+            //Draw the before-srart corner
+            Ellipse2D.Double inner = new  Ellipse2D.Double();
+            inner.x = startWidth;
+            inner.y = beforeWidth;
+            inner.width =  2 * (startRadius - startWidth);
+            inner.height =  2 * (beforeRadius - beforeWidth);
+
+            Ellipse2D.Double outer = new  Ellipse2D.Double();
+            outer.x = 0;
+            outer.y = 0;
+            outer.width =   2 * (startRadius);
+            outer.height =  2 * (beforeRadius);
+
+            Area border = new Area(outer);
+            border.subtract(new Area(inner));
+
+            GeneralPath afterCut = new GeneralPath();
+            GeneralPath beforeCut = new GeneralPath();
+            afterCut.moveTo(0, 0);
+            beforeCut.moveTo(0, 0);
+            float borderWidthRatio = ((float)beforeWidth) / startWidth;
+            if (beforeWidth * startRadius > startWidth * beforeRadius) {
+                afterCut.lineTo(startRadius, borderWidthRatio * startRadius);
+                beforeCut.lineTo(1f / borderWidthRatio * beforeRadius, beforeRadius);
+
+                afterCut.lineTo(startRadius, 0);
+                beforeCut.lineTo(0, beforeRadius);
+            } else {
+                afterCut.lineTo(startRadius, (float)(borderWidthRatio * startRadius));
+                beforeCut.lineTo(1f / borderWidthRatio * beforeRadius, beforeRadius);
+
+                afterCut.lineTo(startRadius, 0);
+                beforeCut.lineTo(0, beforeRadius);
+
+            }
+
+            //start
+            g2d.setColor(startColor);
+            g2d.fill(border);
+
+            //before
+            border = new Area(outer);
+            border.subtract(new Area(inner));
+            border.subtract(new Area(beforeCut));
+
+            //start
+            g2d.setColor(beforeColor);
+            g2d.fill(border);
+
+            //paint background
+            if (innerBackgroundColor == null) {
+                g2d.setColor(Color.white);
+              //  log.warn("No background color set");
+
+            } else {
+                g2d.setColor(innerBackgroundColor);
+            }
+
+            g2d.fill(inner);
+        }
+
+
+        private void drawCorner(final int corner, final Rectangle area,
+                final BorderProps before, final BorderProps start,
+                final int beforeRadius, final int startRadius, final Color innerBackground)
+                throws IFException {
+
+            if (beforeRadius > 0 && startRadius > 0) {
+                String cornerKey = makeCornerKey(corner, before, start,
+                        beforeRadius, startRadius, innerBackground);
+
+                Graphics2DImagePainter painter = null;
+
+                String name = documentHandler.getCachedRoundedCorner(cornerKey);
+
+                //  If the corner is not in the cache we construct a Graphics2DImagePainter
+                //  that paints the corner
+                if (name == null) {
+                    //Cache the name
+                    name = documentHandler.cacheRoundedCorner(cornerKey);
+
+                    // create the Graphics2DImagePainter
+                    painter = new Graphics2DImagePainter() {
+                        public void paint(Graphics2D g2d, Rectangle2D area) {
+
+                            int beforeWidth = before.width;
+                            int startWidth = start.width;
+
+                            Color beforeColor = before.color;
+                            Color startColor = start.color;
+
+                            //No transformation
+                            AffineTransform t;
+                            switch(corner) {
+                            case BEFORE_START:
+                                //No transform required
+                                break;
+                            case BEFORE_END:
+                                t = new AffineTransform(-1, 0, 0, 1, startRadius, 0);
+                                g2d.transform(t);
+                                break;
+                            case AFTER_END:
+                                t = new AffineTransform(-1, 0, 0, -1, startRadius, beforeRadius);
+                                g2d.transform(t);
+                                break;
+                            case AFTER_START:
+                                t = new AffineTransform(1, 0, 0, -1, 0, beforeRadius);
+                                g2d.transform(t);
+                                break;
+                            default: break;
+                            }
+
+                            paintCorner(g2d, beforeWidth, startWidth,
+                                    beforeRadius, startRadius, innerBackground,
+                                    beforeColor,  startColor);
+                        }
+
+                        public Dimension getImageSize() {
+                            return area.getSize();
+                        }
+                    };
+                }
+                paintCornersAsBitmap(painter, area, name);
+            }
+        }
+
+
+        private String makeCornerKey(int corner, BorderProps beforeProps, BorderProps startProps,
+                int beforeRadius, int startRadius, Color innerBackgroundColor) {
+
+           return hash(new StringBuffer()
+                   .append(corner)
+                   .append(":")
+                   .append(beforeRadius)
+                   .append(":")
+                   .append(startRadius)
+                   .append(":")
+                   .append(beforeProps.width)
+                   .append(":")
+                   .append(startProps.width)
+                   .append(":")
+                   .append(beforeProps.color)
+                   .append(":")
+                   .append(startProps.color)
+                   .append(":")
+                   .append(innerBackgroundColor)
+                   .toString());
+        }
+
+
+        private String makeKey(Rectangle area, BorderProps beforeProps,
+                BorderProps endProps, BorderProps afterProps, BorderProps startProps,
+                 Color innerBackgroundColor) {
+
+            return hash(new StringBuffer()
+                    .append(area.width)
+                    .append(":")
+                    .append(area.height)
+                    .append(":")
+                    .append(beforeProps)
+                    .append(":")
+                    .append(endProps)
+                    .append(":")
+                    .append(afterProps)
+                    .append(":")
+                    .append(startProps)
+                    .append(":")
+                    .append(innerBackgroundColor)
+                    .toString());
+        }
+
+
+        private String hash(String text) {
+
+            MessageDigest md;
+            try {
+                md = MessageDigest.getInstance("MD5");
+            } catch (Exception e) {
+                throw new RuntimeException("Internal error", e);
+            }
+
+            byte[] result = md.digest(text.getBytes());
+
+            StringBuffer sb = new StringBuffer();
+            char[] digits = {'0', '1', '2', '3', '4', '5', '6',
+                    '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+            for (int idx = 0; idx < 6; ++idx) {
+              byte b = result[idx];
+              sb.append(digits[(b & 0xf0) >> 4]);
+              sb.append(digits[b & 0x0f]);
+            }
+            return sb.toString();
+        }
+
+        private void paintCornersAsBitmap(Graphics2DImagePainter painter,
+                Rectangle boundingBox, String name) throws IFException {
+            //TODO parameters ok?
+            ImageInfo info = new ImageInfo(name, null);
+
+            ImageSize size = new ImageSize();
+            size.setSizeInMillipoints(boundingBox.width, boundingBox.height);
+
+            //Use the foreign attributes map to set image handling hints
+            Map map = new java.util.HashMap(2);
+            map.put(AFPForeignAttributeReader.RESOURCE_NAME, name);
+            map.put(AFPForeignAttributeReader.RESOURCE_LEVEL, "print-file");
+
+            AFPRenderingContext context = (AFPRenderingContext)
+                    this.painter.createRenderingContext(/*map*/);
+
+            size.setResolution(context.getPaintingState().getResolution());
+            size.calcPixelsFromSize();
+            info.setSize(size);
+            ImageGraphics2D img = new ImageGraphics2D(info, painter);
+
+            Map hints = new java.util.HashMap();
+
+            hints.put(ImageHandlerUtil.CONVERSION_MODE, ImageHandlerUtil.CONVERSION_MODE_BITMAP);
+            hints.put("TARGET_RESOLUTION",
+                    new Integer(context.getPaintingState().getResolution()));
+
+
+            try {
+                this.painter.drawImage(img, boundingBox, context, true, hints);
+            } catch (IOException ioe) {
+                throw new IFException(
+                        "I/O error while painting corner using a bitmap", ioe);
+            } catch (ImageException ie) {
+                throw new IFException(
+                        "Image error while painting corner using a bitmap", ie);
+            }
         }
 
         protected void clip() throws IOException {
@@ -291,11 +1084,11 @@ public class AFPPainter extends AbstractIFPainter {
         }
 
         public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
-                throws IOException {
+        throws IOException {
             if (start.y != end.y) {
                 //TODO Support arbitrary lines if necessary
                 throw new UnsupportedOperationException(
-                        "Can only deal with horizontal lines right now");
+                "Can only deal with horizontal lines right now");
             }
 
             //Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated.
@@ -304,11 +1097,45 @@ public class AFPPainter extends AbstractIFPainter {
                     true, true, style.getEnumValue(), color);
         }
 
+
+        protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width,
+                int height) throws IOException {
+            throw new UnsupportedOperationException(
+            "Can only deal with horizontal lines right now");
+
+        }
+
+
+        protected void changeCoords(double a, double b, double c, double d, double e, double f) {
+            throw new UnsupportedOperationException(
+            "Can only deal with horizontal lines right now");
+        }
+
+        protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
+        throws IOException {
+            throw new UnsupportedOperationException(
+            "Cannot handle cubic Bezier");
+        }
+
+        protected void rotateCoordinates(double angle) throws IOException {
+            throw new UnsupportedOperationException(
+            "Cannot handle coordinate rotation");
+        }
+
+        protected void scaleCoordinates(float xScale, float yScale) throws IOException {
+            throw new UnsupportedOperationException(
+            "Cannot handle coordinate scaling");
+        }
+
+        protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
+            throw new UnsupportedOperationException(
+            "Cannot handle coordinate  translation");
+        }
     }
 
     /** {@inheritDoc} */
     public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
-                throws IFException {
+    throws IFException {
         try {
             this.borderPainter.drawLine(start, end, width, color, style);
         } catch (IOException ioe) {
@@ -499,4 +1326,19 @@ public class AFPPainter extends AbstractIFPainter {
         getPaintingState().restore();
     }
 
+
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+
+      //not supported by AFP
+    }
+
+    /** {@inheritDoc} */
+    public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        return borderPainter.isBackgroundRequired( bpsBefore,  bpsAfter,
+                 bpsStart,  bpsEnd);
+    }
+
 }
index 5374c7051a4a3c181e017ded20ef5b5c5e6dae6b..9c4cb62413027b8fd719d11a775ea217f1bdbbd8 100644 (file)
@@ -91,9 +91,11 @@ public abstract class AbstractAFPImageHandlerRawStream extends AFPImageHandler
         AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();
 
         // set resource information
-        setResourceInformation(dataObjectInfo,
+
+        dataObjectInfo.setResourceInfo(createResourceInformation(
                 image.getInfo().getOriginalURI(),
-                afpContext.getForeignAttributes());
+                afpContext.getForeignAttributes()));
+
 
         // Positioning
         dataObjectInfo.setObjectAreaInfo(createObjectAreaInfo(afpContext.getPaintingState(), pos));
index 1b870a7c513d6671dea8ffdc3e59cacf790df494..ca907eb0d76301c5f3734aa34b9f767a2f83eacc 100644 (file)
@@ -315,7 +315,7 @@ public abstract class AbstractIFPainter implements IFPainter {
 
     /** {@inheritDoc} */
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException {
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
         if (before != null) {
             Rectangle b = new Rectangle(
                     rect.x, rect.y,
@@ -411,4 +411,10 @@ public abstract class AbstractIFPainter implements IFPainter {
         return new AffineTransform(matrix);
     }
 
+    /** {@inheritDoc} */
+    public boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        return true;
+    }
+
 }
index 20402369ad3e974a1ca4fa4b97f00e6676cbe18c..31218af6bce9cc9f3a05a11595766ed7e5f56ba2 100644 (file)
@@ -32,6 +32,12 @@ import org.apache.fop.traits.RuleStyle;
  */
 public abstract class BorderPainter {
 
+    public static final String ROUNDED_CORNERS = "fop.round-corners";
+
+    protected static final int BEFORE = 0, END = 1, AFTER = 2, START = 3;
+    protected static final int BEFORE_START = 0, BEFORE_END = 1, AFTER_END = 2, AFTER_START = 3;
+
+
     /**
      * Draws borders.
      * @param borderRect the border rectangle
@@ -39,59 +45,94 @@ 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
+     * @throws IFException if an error occurs while drawing the borders
      */
     public void drawBorders(Rectangle borderRect,               // CSOK: MethodLength
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor)
+                throws IFException {
+
+        try {
+            if (isRoundedCornersSupported()) {
+                drawRoundedBorders(borderRect, bpsBefore, bpsAfter,
+                        bpsStart, bpsEnd);
+
+            } else {
+                drawRectangularBorders(borderRect, bpsBefore, bpsAfter,
+                        bpsStart, bpsEnd);
+            }
+
+        } catch (IOException ioe) {
+            throw new IFException("IO error drawing borders", ioe);
+        }
+    }
+
+    private BorderProps sanitizeBorderProps(BorderProps bps) {
+        return bps == null ? bps : bps.width == 0 ? (BorderProps)null : bps;
+    }
+
+    protected void drawRectangularBorders(Rectangle borderRect,
             BorderProps bpsBefore, BorderProps bpsAfter,
             BorderProps bpsStart, BorderProps bpsEnd) throws IOException {
+
+        bpsBefore = sanitizeBorderProps(bpsBefore);
+        bpsAfter = sanitizeBorderProps(bpsAfter);
+        bpsStart = sanitizeBorderProps(bpsStart);
+        bpsEnd = sanitizeBorderProps(bpsEnd);
+
         int startx = borderRect.x;
         int starty = borderRect.y;
         int width = borderRect.width;
         int height = borderRect.height;
         boolean[] b = new boolean[] {
-            (bpsBefore != null), (bpsEnd != null),
-            (bpsAfter != null), (bpsStart != null)};
+                (bpsBefore != null), (bpsEnd != null),
+                (bpsAfter != null), (bpsStart != null)};
         if (!b[0] && !b[1] && !b[2] && !b[3]) {
             return;
         }
         int[] bw = new int[] {
-            (b[0] ? bpsBefore.width : 0),
-            (b[1] ? bpsEnd.width : 0),
-            (b[2] ? bpsAfter.width : 0),
-            (b[3] ? bpsStart.width : 0)};
+                (b[BEFORE] ? bpsBefore.width : 0),
+                (b[END] ? bpsEnd.width : 0),
+                (b[AFTER] ? bpsAfter.width : 0),
+                (b[3] ? bpsStart.width : 0)};
         int[] clipw = new int[] {
-            BorderProps.getClippedWidth(bpsBefore),
-            BorderProps.getClippedWidth(bpsEnd),
-            BorderProps.getClippedWidth(bpsAfter),
-            BorderProps.getClippedWidth(bpsStart)};
-        starty += clipw[0];
-        height -= clipw[0];
-        height -= clipw[2];
-        startx += clipw[3];
-        width -= clipw[3];
-        width -= clipw[1];
+                BorderProps.getClippedWidth(bpsBefore),
+                BorderProps.getClippedWidth(bpsEnd),
+                BorderProps.getClippedWidth(bpsAfter),
+                BorderProps.getClippedWidth(bpsStart)};
+        starty += clipw[BEFORE];
+        height -= clipw[BEFORE];
+        height -= clipw[AFTER];
+        startx += clipw[START];
+        width -= clipw[START];
+        width -= clipw[END];
 
         boolean[] slant = new boolean[] {
-            (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
+                (b[START] && b[BEFORE]),
+                (b[BEFORE] && b[END]),
+                (b[END] && b[AFTER]),
+                (b[AFTER] && b[START])};
         if (bpsBefore != null) {
             int sx1 = startx;
-            int sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1);
+            int sx2 = (slant[BEFORE_START] ? sx1 + bw[START] - clipw[START] : sx1);
             int ex1 = startx + width;
-            int ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1);
-            int outery = starty - clipw[0];
-            int clipy = outery + clipw[0];
-            int innery = outery + bw[0];
+            int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1);
+            int outery = starty - clipw[BEFORE];
+            int clipy = outery + clipw[BEFORE];
+            int innery = outery + bw[BEFORE];
 
             saveGraphicsState();
             moveTo(sx1, clipy);
+
+
             int sx1a = sx1;
             int ex1a = ex1;
             if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
                 if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
-                    sx1a -= clipw[3];
+                    sx1a -= clipw[START];
                 }
                 if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
-                    ex1a += clipw[1];
+                    ex1a += clipw[END];
                 }
                 lineTo(sx1a, outery);
                 lineTo(ex1a, outery);
@@ -107,12 +148,12 @@ public abstract class BorderPainter {
         }
         if (bpsEnd != null) {
             int sy1 = starty;
-            int sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1);
+            int sy2 = (slant[BEFORE_END] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1);
             int ey1 = starty + height;
-            int ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1);
-            int outerx = startx + width + clipw[1];
-            int clipx = outerx - clipw[1];
-            int innerx = outerx - bw[1];
+            int ey2 = (slant[AFTER_END] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1);
+            int outerx = startx + width + clipw[END];
+            int clipx = outerx - clipw[END];
+            int innerx = outerx - bw[END];
 
             saveGraphicsState();
             moveTo(clipx, sy1);
@@ -120,10 +161,10 @@ public abstract class BorderPainter {
             int ey1a = ey1;
             if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
                 if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
-                    sy1a -= clipw[0];
+                    sy1a -= clipw[BEFORE];
                 }
                 if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
-                    ey1a += clipw[2];
+                    ey1a += clipw[AFTER];
                 }
                 lineTo(outerx, sy1a);
                 lineTo(outerx, ey1a);
@@ -138,12 +179,12 @@ public abstract class BorderPainter {
         }
         if (bpsAfter != null) {
             int sx1 = startx;
-            int sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1);
+            int sx2 = (slant[AFTER_START] ? sx1 + bw[START] - clipw[START] : sx1);
             int ex1 = startx + width;
-            int ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1);
-            int outery = starty + height + clipw[2];
-            int clipy = outery - clipw[2];
-            int innery = outery - bw[2];
+            int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1);
+            int outery = starty + height + clipw[AFTER];
+            int clipy = outery - clipw[AFTER];
+            int innery = outery - bw[AFTER];
 
             saveGraphicsState();
             moveTo(ex1, clipy);
@@ -151,10 +192,10 @@ public abstract class BorderPainter {
             int ex1a = ex1;
             if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
                 if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
-                    sx1a -= clipw[3];
+                    sx1a -= clipw[START];
                 }
                 if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
-                    ex1a += clipw[1];
+                    ex1a += clipw[END];
                 }
                 lineTo(ex1a, outery);
                 lineTo(sx1a, outery);
@@ -169,23 +210,25 @@ public abstract class BorderPainter {
         }
         if (bpsStart != null) {
             int sy1 = starty;
-            int sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
+            int sy2 = (slant[BEFORE_START] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1);
             int ey1 = sy1 + height;
-            int ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
-            int outerx = startx - clipw[3];
-            int clipx = outerx + clipw[3];
-            int innerx = outerx + bw[3];
+            int ey2 = (slant[AFTER_START] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1);
+            int outerx = startx - clipw[START];
+            int clipx = outerx + clipw[START];
+            int innerx = outerx + bw[START];
 
             saveGraphicsState();
+
             moveTo(clipx, ey1);
+
             int sy1a = sy1;
             int ey1a = ey1;
             if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
                 if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
-                    sy1a -= clipw[0];
+                    sy1a -= clipw[BEFORE];
                 }
                 if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
-                    ey1a += clipw[2];
+                    ey1a += clipw[AFTER];
                 }
                 lineTo(outerx, ey1a);
                 lineTo(outerx, sy1a);
@@ -200,6 +243,459 @@ public abstract class BorderPainter {
         }
     }
 
+    protected void drawRoundedBorders(Rectangle borderRect,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IOException {
+
+        bpsBefore = sanitizeBorderProps(bpsBefore);
+        bpsAfter = sanitizeBorderProps(bpsAfter);
+        bpsStart = sanitizeBorderProps(bpsStart);
+        bpsEnd = sanitizeBorderProps(bpsEnd);
+
+        boolean[] b = new boolean[] {
+                (bpsBefore != null), (bpsEnd != null),
+                (bpsAfter != null), (bpsStart != null)};
+        if (!b[BEFORE] && !b[END] && !b[AFTER] && !b[START]) {
+            return;
+        }
+        int[] bw = new int[] {
+                (b[BEFORE] ? bpsBefore.width : 0),
+                (b[END] ? bpsEnd.width : 0),
+                (b[AFTER] ? bpsAfter.width : 0),
+                (b[START] ? bpsStart.width : 0)};
+
+        int[] clipw = new int[] {
+                BorderProps.getClippedWidth(bpsBefore),
+                BorderProps.getClippedWidth(bpsEnd),
+                BorderProps.getClippedWidth(bpsAfter),
+                BorderProps.getClippedWidth(bpsStart)};
+
+        final int startx = borderRect.x + clipw[START];
+        final int starty = borderRect.y + clipw[BEFORE];
+        final int width = borderRect.width - clipw[START] - clipw[END];
+        final int height = borderRect.height - clipw[BEFORE] - clipw[AFTER];
+
+        boolean[] slant = new boolean[] {
+                (b[START] && b[BEFORE]), (b[BEFORE] && b[END]),
+                (b[END] && b[AFTER]), (b[START] && b[AFTER])};
+
+        //Determine scale factor if any adjacent elliptic corners overlap
+        double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+
+        if (bpsBefore != null) {
+            //Let x increase in the START->END direction
+            final int sx2 = (slant[BEFORE_START] ? bw[START] - clipw[START] : 0);
+            final int ex1 =  width;
+            final int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1);
+            final int outery = -clipw[BEFORE];
+            final int innery = outery + bw[BEFORE];
+            final int clipy = outery + clipw[BEFORE];
+            final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusStart());
+            final int ellipseSBH = (int)(esf * bpsBefore.getRadiusStart());
+            final int ellipseSBX = ellipseSBW;
+            final int ellipseSBY = clipy + ellipseSBH;
+            final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusStart());
+            final int ellipseBEH = (int)(esf * bpsBefore.getRadiusEnd());
+            final int ellipseBEX = ex1 - ellipseBEW;
+            final int ellipseBEY = clipy + ellipseBEH;
+
+            saveGraphicsState();
+            translateCoordinates(startx, starty);
+            drawBorderSegment( sx2, ex1, ex2,  outery, innery,
+                    clipw[START], clipw[END],
+                    ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
+                    ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
+                    bpsBefore, bpsStart, bpsEnd
+            );
+            restoreGraphicsState();
+        }
+
+        if (bpsStart != null) {
+            //Let x increase in the AFTER->BEFORE direction
+            final int sx2 = (slant[AFTER_START] ?  bw[AFTER] - clipw[AFTER] : 0);
+            final int ex1 = height;
+            final int ex2 = (slant[BEFORE_START] ? ex1 - bw[BEFORE] + clipw[BEFORE] : ex1);
+            final int outery = -clipw[START];
+            final int innery = outery + bw[START];
+            final int clipy = outery + clipw[START];
+            final int ellipseSBW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusStart());
+            final int ellipseSBH = (int)(esf * bpsStart.getRadiusEnd());
+            final int ellipseSBX = ellipseSBW;
+            final int ellipseSBY = clipy + ellipseSBH;
+            final int ellipseBEW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusStart());
+            final int ellipseBEH = (int)(esf * bpsStart.getRadiusStart());
+            final int ellipseBEX = ex1 - ellipseBEW;
+            final int ellipseBEY = clipy + ellipseBEH;
+
+            saveGraphicsState();
+            translateCoordinates(startx, starty + height);
+            rotateCoordinates(Math.PI * 3d / 2d);
+            drawBorderSegment( sx2, ex1, ex2, outery, innery,
+                    clipw[AFTER],  clipw[BEFORE],
+                    ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
+                    ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
+                    bpsStart, bpsAfter, bpsBefore
+            );
+            restoreGraphicsState();
+
+        }
+
+
+        if (bpsAfter != null) {
+            //Let x increase in the START->END direction
+            final int sx2 = (slant[AFTER_START] ?  bw[START] - clipw[START] : 0);
+            final int ex1 = width;
+            final int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1);
+            final int outery = -clipw[AFTER];
+            final int innery = outery + bw[AFTER];
+            final int clipy = outery + clipw[AFTER];
+            final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusEnd());
+            final int ellipseSBH =  (int)(esf * bpsAfter.getRadiusStart());
+            final int ellipseSBX = ellipseSBW;
+            final int ellipseSBY =  clipy + ellipseSBH;
+            final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusEnd());
+            final int ellipseBEH = (int)(esf * bpsAfter.getRadiusEnd());
+            final int ellipseBEX = ex1 - ellipseBEW;
+            final int ellipseBEY = clipy + ellipseBEH;
+
+            saveGraphicsState();
+            translateCoordinates(startx, starty + height);
+            scaleCoordinates(1, -1);
+            drawBorderSegment( sx2, ex1, ex2, outery, innery,
+                    clipw[START], clipw[END],
+                    ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
+                    ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
+                    bpsAfter, bpsStart, bpsEnd
+            );
+            restoreGraphicsState();
+        }
+
+        if (bpsEnd != null) {
+            //Let x increase in the BEFORE-> AFTER direction
+            final int sx2 = (slant[BEFORE_END] ?  bw[BEFORE] - clipw[BEFORE] : 0);
+            final int ex1 = height;
+            final int ex2 = (slant[AFTER_END] ? ex1 - bw[AFTER] + clipw[AFTER] : ex1);
+            final int outery = -clipw[END];
+            final int innery = outery + bw[END];
+            final int clipy = outery + clipw[END];
+            final int ellipseSBW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusEnd());
+            final int ellipseSBH = (int)(esf * bpsEnd.getRadiusStart());
+            final int ellipseSBX = ellipseSBW;
+            final int ellipseSBY = clipy + ellipseSBH;
+            final int ellipseBEW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusEnd());
+            final int ellipseBEH = (int)(esf * bpsEnd.getRadiusEnd());
+            final int ellipseBEX = ex1 - ellipseBEW;
+            final int ellipseBEY = clipy + ellipseBEH;
+
+            saveGraphicsState();
+            translateCoordinates(startx + width, starty);
+            rotateCoordinates(Math.PI / 2d);
+            drawBorderSegment( sx2, ex1, ex2, outery, innery,
+                    clipw[BEFORE], clipw[AFTER],
+                    ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
+                    ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
+                    bpsEnd,  bpsBefore, bpsAfter
+            );
+            restoreGraphicsState();
+        }
+    }
+
+    private void drawBorderSegment(final int sx2, final int ex1, final int ex2,
+            final int outery, final int innery,
+            final int clipWidthStart, final int clipWidthEnd,
+            final int ellipseSBX, final int ellipseSBY,
+            final int ellipseSBRadiusX, final int ellipseSBRadiusY,
+            final int ellipseBEX,  final int ellipseBEY,
+            final int ellipseBERadiusX, final int ellipseBERadiusY,
+            final BorderProps bpsThis, final BorderProps bpsStart, final BorderProps bpsEnd )
+    throws IOException {
+
+        int sx1a = 0;
+        int ex1a = ex1;
+
+
+        if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0 ) {
+
+            final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX,
+                    ellipseSBRadiusY, (double)innery / sx2);
+
+            final double outerJoinPointX = joinMetrics[0];
+            final double outerJoinPointY = joinMetrics[1];
+            final double sbJoinAngle = joinMetrics[2];
+
+            moveTo((int)outerJoinPointX, (int)outerJoinPointY);
+            arcTo(Math.PI + sbJoinAngle, Math.PI * 3 / 2,
+                    ellipseSBX, ellipseSBY, ellipseSBRadiusX, ellipseSBRadiusY);
+        }  else {
+
+            moveTo(0, 0);
+
+            if (bpsThis.mode == BorderProps.COLLAPSE_OUTER) {
+
+                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+                    sx1a -= clipWidthStart;
+                }
+                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
+                    ex1a += clipWidthEnd;
+                }
+
+                lineTo(sx1a, outery);
+                lineTo(ex1a, outery);
+            }
+        }
+
+        if (ellipseBERadiusX != 0) {
+
+            final double[] outerJoinMetrics = getCornerBorderJoinMetrics(
+                    ellipseBERadiusX, ellipseBERadiusY, (double)innery / (ex1 - ex2));
+            final double beJoinAngle = Math.PI / 2 - outerJoinMetrics[2];
+
+            lineTo(ellipseBEX, 0);
+            arcTo( Math.PI * 3 / 2 , Math.PI * 3 / 2 + beJoinAngle,
+                    ellipseBEX, ellipseBEY, ellipseBERadiusX, ellipseBERadiusY);
+
+            if (ellipseBEX < ex2 && ellipseBEY > innery) {
+
+                final double[] innerJoinMetrics = getCornerBorderJoinMetrics(
+                        (double)ex2 - ellipseBEX, (double)ellipseBEY - innery,
+                        (double)innery / (ex1 - ex2));
+                final double innerJoinPointX = innerJoinMetrics[0];
+                final double innerJoinPointY = innerJoinMetrics[1];
+                final double beInnerJoinAngle = Math.PI / 2 - innerJoinMetrics[2];
+
+                lineTo((int) (ex2 - innerJoinPointX), (int)(innerJoinPointY + innery));
+                arcTo(beInnerJoinAngle + Math.PI * 3 / 2, Math.PI * 3 / 2,
+                        ellipseBEX, ellipseBEY, ex2 - ellipseBEX, ellipseBEY - innery);
+            } else {
+                lineTo(ex2, innery);
+            }
+
+        } else {
+            lineTo(ex1, 0);
+            lineTo(ex2, innery);
+        }
+
+        if (ellipseSBRadiusX == 0) {
+            lineTo(sx2, innery);
+        } else {
+            if (ellipseSBX > sx2 &&  ellipseSBY > innery) {
+
+
+                final double[] innerJoinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX - sx2,
+                        ellipseSBRadiusY - innery, (double)innery / sx2);
+
+                final double sbInnerJoinAngle = innerJoinMetrics[2];
+
+                lineTo(ellipseSBX, innery);
+                arcTo(Math.PI * 3 / 2, sbInnerJoinAngle + Math.PI,
+                        ellipseSBX, ellipseSBY, ellipseSBX - sx2, ellipseSBY - innery);
+            } else {
+                lineTo(sx2, innery);
+            }
+        }
+
+        closePath();
+        clip();
+
+        if (ellipseBERadiusY == 0 && ellipseSBRadiusY == 0) {
+            drawBorderLine(sx1a, outery, ex1a, innery, true, true,
+                    bpsThis.style, bpsThis.color);
+
+        } else {
+            int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery);
+            drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true,
+                    bpsThis.style, bpsThis.color);
+        }
+    }
+
+    private double[] getCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
+            double borderWidthRatio) {
+
+        //TODO decide on implementation
+        boolean invert = System.getProperty("fop.round-corners.border-invert") != null;
+        if (invert) {
+            borderWidthRatio = 1d / borderWidthRatio;
+        }
+        String cornerJoinStyle = System.getProperty("fop.round-corners.corner-join-style");
+        if ("css".equals(cornerJoinStyle)) {
+            return getCSSCornerBorderJoinMetrics(ellipseCenterX, ellipseCenterY, borderWidthRatio);
+        } else {
+            if (invert) { throw new RuntimeException("non css AND bw inverted!"); }
+            return getDefaultCornerBorderJoinMetrics(
+                    ellipseCenterX, ellipseCenterY, borderWidthRatio);
+        }
+
+    }
+
+    private double[] getCSSCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
+            double borderWidthRatio) {
+
+        double angle = Math.atan(borderWidthRatio);
+        double x = ellipseCenterX * Math.cos(Math.atan(ellipseCenterX
+                / ellipseCenterY * borderWidthRatio));
+        double y = ellipseCenterY * Math.sqrt(1d - x * x / ellipseCenterX / ellipseCenterX);
+
+        return new double[]{ellipseCenterX - x, ellipseCenterY - y, angle};
+    }
+    private double[] getDefaultCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
+            double borderWidthRatio) {
+
+        double x = ellipseCenterY * ellipseCenterX * (
+                ellipseCenterY + ellipseCenterX * borderWidthRatio
+                - Math.sqrt(2d * ellipseCenterX * ellipseCenterY * borderWidthRatio)
+        )
+        / (ellipseCenterY * ellipseCenterY
+                + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio);
+        double y = borderWidthRatio * x;
+
+        return new double[]{x, y, Math.atan((ellipseCenterY - y) / (ellipseCenterX - x))};
+    }
+
+    /**
+     * Clip the background to the inner border
+     * @param rect
+     * @param bpsBefore
+     * @param bpsAfter
+     * @param bpsStart
+     * @param bpsEnd
+     * @throws IOException if an I/O error occurs
+     */
+    public void clipBackground(Rectangle rect,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IOException {
+        int startx = rect.x;
+        int starty = rect.y;
+        int width = rect.width;
+        int height = rect.height;
+
+
+        int fullWidth = width + ( bpsStart == null ? 0 : bpsStart.width )
+            + (bpsStart == null ? 0 : bpsStart.width);
+        int fullHeight = height + ( bpsBefore == null ? 0 : bpsBefore.width )
+            + (bpsAfter == null ? 0 : bpsAfter.width);
+
+        double esf = cornerScaleFactor( fullWidth,  fullHeight,   bpsBefore,  bpsAfter,
+                bpsStart,  bpsEnd);
+
+        int ellipseSS = 0;
+        int ellipseBS = 0;
+        int ellipseBE = 0;
+        int ellipseES = 0;
+        int ellipseEE = 0;
+        int ellipseAE = 0;
+        int ellipseAS = 0;
+        int ellipseSE = 0;
+
+        if (bpsBefore != null && bpsBefore.getRadiusStart() > 0
+                && bpsStart != null && bpsStart.getRadiusStart() > 0) {
+            ellipseSS = Math.max((int)(bpsStart.getRadiusStart() * esf) - bpsStart.width, 0);
+            ellipseBS = Math.max((int)(bpsBefore.getRadiusStart() * esf) - bpsBefore.width, 0);
+        }
+
+        if (bpsBefore != null && bpsBefore.getRadiusEnd() > 0
+                && bpsEnd != null && bpsEnd.getRadiusStart() > 0) {
+            ellipseBE = Math.max((int)(bpsBefore.getRadiusEnd() * esf) - bpsBefore.width, 0);
+            ellipseES = Math.max((int)(bpsEnd.getRadiusStart() * esf) - bpsEnd.width, 0);
+        }
+
+        if (bpsEnd != null && bpsEnd.getRadiusEnd() > 0
+                && bpsAfter != null && bpsAfter.getRadiusEnd() > 0) {
+            ellipseEE = Math.max((int)(bpsEnd.getRadiusEnd() * esf) - bpsEnd.width, 0);
+            ellipseAE = Math.max((int)(bpsAfter.getRadiusEnd() * esf) - bpsAfter.width, 0);
+        }
+
+        if (bpsAfter != null && bpsAfter.getRadiusStart() > 0
+                && bpsStart != null && bpsStart.getRadiusEnd() > 0) {
+            ellipseAS = Math.max((int)(bpsAfter.getRadiusStart() * esf) - bpsAfter.width, 0);
+            ellipseSE = Math.max((int)(bpsStart.getRadiusEnd() * esf) - bpsStart.width, 0);
+        }
+
+        // Draw clipping region in the order: Before->End->After->Start
+        moveTo(startx + ellipseSS, starty);
+
+        lineTo(startx + width - ellipseES, starty);
+
+        if (ellipseBE > 0 && ellipseES > 0) {
+            arcTo(Math.PI * 3 / 2, Math.PI * 2,
+                    startx + width - ellipseES, starty + ellipseBE, ellipseES, ellipseBE);
+        }
+
+        lineTo(startx + width,  starty + height - ellipseAE);
+
+        if (ellipseEE > 0 && ellipseAE > 0) {
+            arcTo(0, Math.PI / 2, startx + width - ellipseEE,
+                    starty + height - ellipseAE, ellipseEE, ellipseAE);
+        }
+
+        lineTo(startx + ellipseSE, starty + height);
+
+        if (ellipseSE > 0 && ellipseSE > 0) {
+            arcTo( Math.PI / 2, Math.PI, startx + ellipseSE,
+                    starty + height - ellipseAS, ellipseSE, ellipseAS);
+        }
+
+        lineTo( startx, starty + ellipseBS);
+
+        if (ellipseSS > 0 && ellipseBS > 0) {
+            arcTo( Math.PI, Math.PI * 3 / 2,
+                    startx +  ellipseSS, starty + ellipseBS, ellipseSS, ellipseBS);
+        }
+
+        clip();
+
+    }
+
+    /*
+     * If the ellipse radii exceed the border edge length, the ellipses are rescaled.
+     */
+    protected double cornerScaleFactor(int width, int height,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        // Ellipse scale factor
+        double esf = 1d;
+
+        if (bpsBefore != null) {
+            if (bpsStart != null && bpsEnd != null
+                    && bpsStart.getRadiusStart() + bpsEnd.getRadiusStart() > 0) {
+
+                double f = (double)width / (bpsStart.getRadiusStart() + bpsEnd.getRadiusStart());
+                if (f < esf) {
+                    esf = f;
+                }
+            }
+        }
+        if (bpsStart != null) {
+            if (bpsAfter != null && bpsBefore != null
+                    && bpsAfter.getRadiusStart() + bpsBefore.getRadiusStart() > 0) {
+                double f = (double)height / (bpsAfter.getRadiusStart()
+                        + bpsBefore.getRadiusStart());
+                if ( f < esf) {
+                    esf = f;
+                }
+            }
+        }
+        if (bpsAfter != null) {
+            if (bpsStart != null && bpsEnd != null
+                    && bpsStart.getRadiusEnd() + bpsEnd.getRadiusEnd() > 0) {
+                double f = (double)width / (bpsStart.getRadiusEnd() + bpsEnd.getRadiusEnd());
+                if (f < esf) {
+                    esf = f;
+                }
+            }
+        }
+
+        if (bpsEnd != null) {
+            if (bpsAfter != null && bpsBefore != null
+                    && bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd() > 0) {
+                double f = (double)height / (bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd());
+                if (f < esf) {
+                    esf = f;
+                }
+            }
+        }
+
+        return esf;
+    }
 
     /**
      * Draws a border line.
@@ -249,6 +745,118 @@ public abstract class BorderPainter {
      */
     protected abstract void lineTo(int x, int y) throws IOException;
 
+    /**
+     * Draw a cubic bezier from current position to (p3x, p3y) using the control points
+     * (p1x, p1y) and (p2x, p2y)
+     * @param p1x x coordinate of the first control point
+     * @param p1y y coordinate of the first control point
+     * @param p2x x coordinate of the second control point
+     * @param p2y y coordinate of the second control point
+     * @param p3x x coordinate of the end point
+     * @param p3y y coordinate of the end point
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
+            throws IOException;
+
+    /**
+     * Draws an arc on the ellipse centered at (cx, cy) with width width and height height
+     * from start angle startAngle (with respect to the x-axis counter-clockwise)
+     * to the end angle endAngle.
+     * The ellipses major axis are assumed to coincide with the coordinate axis.
+     * The current position MUST coincide with the starting position on the ellipse.
+     * @param startAngle the start angle
+     * @param endAngle the end angle
+     * @param cx the x coordinate of the ellipse center
+     * @param cy the y coordinate of the ellipse center
+     * @param width the extent of the ellipse in the x direction
+     * @param height the extent of the ellipse in the y direction
+     * @throws IOException if an I/O error occurs
+     */
+    protected void arcTo(final double startAngle, final double endAngle, final int cx, final int cy,
+            final int width, final int height)
+                throws IOException {
+
+        //  Implementation follows http://www.spaceroots.org/documents/ellipse/ -
+        //      Drawing an elliptical arc using polylines, quadratic or cubic Bézier curves
+        //      L. Maisonobe, July 21, 2003
+
+        //  Scaling the coordinate system to represent the ellipse as a circle:
+        final double etaStart = Math.atan(Math.tan(startAngle) * width / height)
+                                    + quadrant(startAngle);
+        final double etaEnd = Math.atan(Math.tan(endAngle) * width / height)
+                                    + quadrant(endAngle);
+
+        final double sinStart = Math.sin(etaStart);
+        final double cosStart = Math.cos(etaStart);
+        final double sinEnd = Math.sin(etaEnd);
+        final double cosEnd = Math.cos(etaEnd);
+
+        final double p0x = cx + cosStart * width;
+        final double p0y = cy + sinStart * height;
+        final double p3x = cx + cosEnd * width;
+        final double p3y = cy + sinEnd * height;
+
+
+        double etaDiff = Math.abs(etaEnd - etaStart);
+        double tan = Math.tan((etaDiff) / 2d);
+        final double alpha = Math.sin(etaDiff) * (Math.sqrt(4d + 3d * tan * tan) - 1d) / 3d;
+
+
+        int order = etaEnd > etaStart ? 1 : -1;
+
+        // p1 = p0 + alpha*(-sin(startAngle), cos(startAngle))
+        final double p1x = p0x - alpha *  sinStart * width * order;
+        final double p1y = p0y + alpha *  cosStart * height * order;
+
+        // p1 = p3 + alpha*(sin(endAngle), -cos(endAngle))
+        final double p2x = p3x + alpha *  sinEnd * width * order;
+        final double p2y = p3y - alpha * cosEnd * height * order;
+
+        //Draw the curve in original coordinate system
+        cubicBezierTo((int)p1x, (int)p1y, (int)p2x, (int)p2y, (int)p3x, (int)p3y);
+    }
+
+    private double quadrant(double angle) {
+        if (angle <= Math.PI ) {
+            if (angle <=  Math.PI / 2d) {
+                return 0;
+            } else {
+                return Math.PI;
+            }
+        } else {
+            if (angle > Math.PI * 3d / 2d) {
+                return 2d * Math.PI;
+            } else {
+                return Math.PI;
+            }
+        }
+    }
+
+    /**
+     * Rotate the coordinate frame
+     * @param angle angle in radians to rotate the coordinate frame
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void rotateCoordinates(double angle) throws IOException;
+
+    /**
+     * Translate the coordinate frame
+     * @param xTranslate translation in the x direction
+     * @param yTranslate translation in the y direction
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void translateCoordinates(int xTranslate, int yTranslate) throws IOException;
+
+    /**
+     * Scale the coordinate frame
+     * @param xScale scale factor in the x direction
+     * @param yScale scale factor in the y direction
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void scaleCoordinates(float xScale, float yScale) throws IOException;
+
+
     /**
      * Closes the current path.
      * @throws IOException if an I/O error occurs
@@ -273,4 +881,8 @@ public abstract class BorderPainter {
      */
     protected abstract void restoreGraphicsState() throws IOException;
 
+    public static boolean isRoundedCornersSupported() {
+        return "true".equalsIgnoreCase(System.getProperty(ROUNDED_CORNERS, "true"));
+    }
+
 }
index 52c6507651c737e668f12aae3c570e697d524976..7e12c301bea2ecffe3dcd4cb9961e56c4ced1ba3 100644 (file)
@@ -43,4 +43,16 @@ public class IFException extends Exception {
         super(message, cause);
     }
 
+    /**
+     * Constructs a new exception with the  cause.
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     *         {@link #getCause()} method).  (A <code>null</code> value is
+     *         permitted, and indicates that the cause is nonexistent or
+     *         unknown.)
+     */
+    /*public IFException( Exception cause) {
+        super(cause);
+    }*/
+
 }
index 00fd74209e4297696a6aea4b9422aee2a0176f88..b0ddf462d527e7b2f4ac182ba1923baf813bf482 100644 (file)
@@ -166,6 +166,32 @@ public interface IFPainter {
     void clipRect(Rectangle rect) throws IFException;
     //TODO clipRect() shall be considered temporary until verified with SVG and PCL
 
+
+    /**
+     * Restricts the current clipping region to the inner border.
+     * @param rect the rectangle's coordinates and extent
+     * @param bpsBefore the border segment on the before-side (top)
+     * @param bpsAfter the border segment on the after-side (bottom)
+     * @param bpsStart the border segment on the start-side (left)
+     * @param bpsEnd the border segment on the end-side (right)
+     * @throws IFException if an error occurs while handling this event
+     */
+    void clipBackground (Rectangle rect,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException;
+
+
+    /**
+     *
+     * @param bpsBefore
+     * @param bpsAfter
+     * @param bpsStart
+     * @param bpsEnd
+     * @return true if the background needs to be painted
+     */
+    boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd);
+
     /**
      * Fills a rectangular area.
      * @param rect the rectangle's coordinates and extent
@@ -182,11 +208,12 @@ public interface IFPainter {
      * @param after the border segment on the after-side (bottom)
      * @param start the border segment on the start-side (left)
      * @param end the border segment on the end-side (right)
+     * @param innerBackgroundColor the color of the inner background
      * @throws IFException if an error occurs while handling this event
      */
     void drawBorderRect(Rectangle rect,
             BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException;
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException;
 
     /**
      * Draws a line. NOTE: Currently, only horizontal lines are implemented!
index da5d8a623ec8127c39358700fc7c497bfd6bb20a..0abf3839a4bba2231c17e7a8bec44e023e1679dd 100644 (file)
@@ -642,9 +642,16 @@ public class IFParser implements IFConstants {
                         borders[i] = BorderProps.valueOf(userAgent, b);
                     }
                 }
+                Color backgroundColor;
+
+                try {
+                    backgroundColor = getAttributeAsColor(attributes, "inner-background-color");
+                } catch (PropertyException pe) {
+                    throw new IFException("Error parsing the color attribute", pe);
+                }
 
                 painter.drawBorderRect(new Rectangle(x, y, width, height),
-                        borders[0], borders[1], borders[2], borders[3]);
+                        borders[0], borders[1], borders[2], borders[3], backgroundColor);
             }
 
         }
index bdeb1e9a1a7667e202e35d98840591de1d5406a1..1d20da51979b2d4ffb46550c138e2b043a6c5230 100644 (file)
@@ -1231,21 +1231,49 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             handleIFException(ife);
         }
     }
-
+    /** {@inheritDoc} */
+    protected void clipBackground(float startx, float starty,
+            float width, float height,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        pushGroup(new IFGraphicContext.Group());
+        Rectangle rect = toMillipointRectangle(startx, starty, width, height);
+        try {
+            painter.clipBackground( rect,
+                 bpsBefore,  bpsAfter, bpsStart,  bpsEnd);
+        } catch (IFException ife) {
+            handleIFException(ife);
+        }
+    }
     /** {@inheritDoc} */
     protected void closePath() {
         throw new IllegalStateException("Not used");
     }
 
+    /** {@inheritDoc} */
+    protected void drawBackground(float startx, float starty,
+            float width, float height,
+            Trait.Background back,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        if (painter.isBackgroundRequired(bpsBefore, bpsAfter, bpsStart, bpsEnd)) {
+            super.drawBackground(startx, starty, width, height,
+                     back,  bpsBefore,  bpsAfter,
+                     bpsStart,  bpsEnd);
+        }
+    }
+
     /** {@inheritDoc} */
     protected void drawBorders(                                  // CSOK: ParameterNumber
             float startx, float starty,
             float width, float height,
             BorderProps bpsBefore, BorderProps bpsAfter,
-            BorderProps bpsStart, BorderProps bpsEnd) {
+            BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) {
+      //TODO lose scale?
         Rectangle rect = toMillipointRectangle(startx, starty, width, height);
         try {
-            painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
+            painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
+                    innerBackgroundColor);
         } catch (IFException ife) {
             handleIFException(ife);
         }
index edeef976655d8de5d4fc8f9873c516ec3665d3cd..0b3b241102228f46139c3a4cede57c2b82109e5c 100644 (file)
@@ -33,6 +33,7 @@ import java.util.Map;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
+
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
@@ -470,6 +471,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
         }
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+        // TODO Auto-generated method stub
+
+    }
+
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -490,7 +499,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
 
     /** {@inheritDoc} */
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException {
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
         if (before == null && after == null && start == null && end == null) {
             return;
         }
@@ -512,6 +521,12 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
             if (end != null) {
                 addAttribute(atts, "end", end.toString());
             }
+
+            if (innerBackgroundColor != null) {
+                addAttribute(atts, "inner-background-color",
+                        ColorUtil.colorToString(innerBackgroundColor));
+            }
+
             handler.element(EL_BORDER_RECT, atts);
         } catch (SAXException e) {
             throw new IFException("SAX error in drawBorderRect()", e);
@@ -775,4 +790,13 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
         }
     }
 
+    /** {@inheritDoc} */
+    public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) {
+        return true;
+    }
+
+
+
+
 }
index b2b29188b384fe1be59dd135a9b8c740e17d4f0f..b3ad5ff7ad0215715b1da15bada3ac1ff22c9db6 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.geom.GeneralPath;
 import java.awt.geom.Line2D;
+import java.io.IOException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -315,4 +316,42 @@ public class Java2DBorderPainter extends BorderPainter {
         this.currentPath = null;
     }
 
+    /** {@inheritDoc} */
+    protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width, int height)
+            throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    protected void changeCoords(double a, double b, double c, double d, double e, double f) {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
+            throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    protected void rotateCoordinates(double angle) throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    protected void scaleCoordinates(float xScale, float yScale) throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
 }
index 39664576829689a5f2faa7f2f92eb5eefa87443a..461b6e5bf380ba3e7ce730a71b3be311d2cd254b 100644 (file)
@@ -172,6 +172,13 @@ public class Java2DPainter extends AbstractIFPainter {
         getState().updateClip(rect);
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+        // TODO Auto-generated method stub
+
+    }
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -188,10 +195,10 @@ public class Java2DPainter extends AbstractIFPainter {
             BorderProps start, BorderProps end) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             try {
-                this.borderPainter.drawBorders(rect, before, after, start, end);
-            } catch (IOException e) {
+                this.borderPainter.drawBorders(rect, before, after, start, end, null);
+            } catch (IFException e) {
                 //Won't happen with Java2D
-                throw new IllegalStateException("Unexpected I/O error");
+                throw new IllegalStateException("Unexpected IF error");
             }
         }
     }
@@ -261,4 +268,6 @@ public class Java2DPainter extends AbstractIFPainter {
         g2dState.transform(transform);
     }
 
+
+
 }
index afae8ac27f286f8d488ffe4d9145657e5d97ed7f..8adb0f12d17946e2f084fef634350665d0be1947 100644 (file)
@@ -99,7 +99,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
 
     /** @return the target resolution */
     protected int getResolution() {
-        int resolution = (int)Math.round(getUserAgent().getTargetResolution());
+        int resolution = Math.round(getUserAgent().getTargetResolution());
         if (resolution <= 300) {
             return 300;
         } else {
@@ -181,6 +181,14 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
         //If you need clipping support, switch to RenderingMode.BITMAP.
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+        //PCL cannot clip (only HP GL/2 can)
+        //If you need clipping support, switch to RenderingMode.BITMAP.
+
+    }
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -209,7 +217,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
             final BorderProps before, final BorderProps after,
             final BorderProps start, final BorderProps end) throws IFException {
         if (isSpeedOptimized()) {
-            super.drawBorderRect(rect, before, after, start, end);
+            super.drawBorderRect(rect, before, after, start, end, null);
             return;
         }
         if (before != null || after != null || start != null || end != null) {
@@ -529,4 +537,6 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
         gen.setCursorPos(transPoint.getX(), transPoint.getY());
     }
 
+
+
 }
index f8090fdeb1d6ec38e0e9fa5e1a88e61a65d4f695..4bfba9df39746aafec1a665205a5f927d557f493 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.render.pdf;
 import java.awt.Color;
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.io.IOException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -53,6 +54,7 @@ public class PDFBorderPainter extends BorderPainter {
     protected void drawBorderLine(                               // CSOK: ParameterNumber
             int x1, int y1, int x2, int y2, boolean horz,
             boolean startOrBefore, int style, Color col) {
+      //TODO lose scale?
        drawBorderLine(generator, x1 / 1000f, y1 / 1000f, x2 / 1000f, y2 / 1000f,
                horz, startOrBefore, style, col);
     }
@@ -68,11 +70,12 @@ public class PDFBorderPainter extends BorderPainter {
         float colFactor;
         float w = x2 - x1;
         float h = y2 - y1;
+        /*
         if ((w < 0) || (h < 0)) {
             LOG.error("Negative extent received (w=" + w + ", h=" + h
                     + "). Border won't be painted.");
             return;
-        }
+        }*/
         switch (style) {
             case Constants.EN_DASHED:
                 generator.setColor(col, false);
@@ -291,6 +294,7 @@ public class PDFBorderPainter extends BorderPainter {
     }
 
     static final String format(int coordinate) {
+      //TODO lose scale?
         return format(coordinate / 1000f);
     }
 
@@ -328,4 +332,38 @@ public class PDFBorderPainter extends BorderPainter {
         generator.add("Q\n");
     }
 
+    /** {@inheritDoc} */
+    protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) {
+        generator.add(format(p1x) + " " + format(p1y) +  " " + format(p2x) + " " + format(p2y)
+                +  " " + format(p3x) + " " + format(p3y) + " c ");
+    }
+
+
+    private void transformCoordinates(int a, int b, int c, int d, int e, int f) {
+        generator.add( "" + format(a)  + " " + format(b)  + " " + format(c)  + " " + format(d)
+                + " " + format(e)  + " " + format(f)  + " cm ");
+    }
+
+    private void transformCoordinates2(float a, float b, float c, float d, float e, float f) {
+        generator.add( "" + format(a)  + " " + format(b)  + " " + format(c)  + " " + format(d)
+                + " " + format(e)  + " " + format(f)  + " cm ");
+    }
+
+    /** {@inheritDoc} */
+    protected void rotateCoordinates(double angle) throws IOException {
+        float s = (float)Math.sin(angle);
+        float c = (float)Math.cos(angle);
+        transformCoordinates2(c, s, -s, c, 0, 0);
+    }
+
+    /** {@inheritDoc} */
+    protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
+        transformCoordinates(1000, 0, 0, 1000, xTranslate, yTranslate);
+    }
+
+    /** {@inheritDoc} */
+    protected void scaleCoordinates(float xScale, float yScale) throws IOException {
+        transformCoordinates2(xScale, 0, 0, yScale, 0, 0);
+    }
+
 }
index 161b466172962e58c54acf174d1b9d1f04947e72..273426c0bf23cd2d7ed1336e4cd5bff0706da7a5 100644 (file)
@@ -223,6 +223,20 @@ public class PDFPainter extends AbstractIFPainter {
         generator.clipRect(rect);
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+
+        try {
+            borderPainter.clipBackground(rect,
+                    bpsBefore,  bpsAfter, bpsStart,  bpsEnd);
+        } catch (IOException ioe) {
+            throw new IFException("I/O error while clipping background", ioe);
+        }
+
+    }
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -256,17 +270,21 @@ public class PDFPainter extends AbstractIFPainter {
 
     /** {@inheritDoc} */
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException {
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             generator.endTextObject();
             try {
-                this.borderPainter.drawBorders(rect, before, after, start, end);
-            } catch (IOException ioe) {
-                throw new IFException("I/O error while drawing borders", ioe);
+                this.borderPainter.drawBorders(rect, before, after, start, end,
+                        innerBackgroundColor);
+            } catch (IFException ioe) {
+                throw new IFException("IF error while drawing borders", ioe);
             }
         }
     }
 
+
+
+
     /** {@inheritDoc} */
     public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
         throws IFException {
index 1e2964eedadf2008217e3522de4f453c4e02017e..916289eda96448806db7f8459ad88e5abe60a69f 100644 (file)
@@ -327,4 +327,58 @@ public class PSBorderPainter extends BorderPainter {
         generator.restoreGraphicsState();
     }
 
+
+
+
+    /** {@inheritDoc} */
+    protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
+            throws IOException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(generator.formatDouble(toPoints(p1x)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(p1y)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(p2x)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(p2y)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(p3x)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(p3y)));
+        sb.append(" curveto ");
+        generator.writeln(sb.toString());
+
+    }
+
+    /** {@inheritDoc} */
+    protected void rotateCoordinates(double angle) throws IOException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(generator.formatDouble(angle * 180d / Math.PI));
+        sb.append(" ");
+        sb.append(" rotate ");
+        generator.writeln(sb.toString());
+    }
+
+    /** {@inheritDoc} */
+    protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(generator.formatDouble(toPoints(xTranslate)));
+        sb.append(" ");
+        sb.append(generator.formatDouble(toPoints(yTranslate)));
+        sb.append(" ");
+        sb.append(" translate ");
+        generator.writeln(sb.toString());
+    }
+
+    /** {@inheritDoc} */
+    protected void scaleCoordinates(float xScale, float yScale) throws IOException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(generator.formatDouble(xScale));
+        sb.append(" ");
+        sb.append(generator.formatDouble(yScale));
+        sb.append(" ");
+        sb.append(" scale ");
+        generator.writeln(sb.toString());
+    }
+
 }
index b8a6d40f659c95c93bc4e6615df8bcbfbf66e6d4..72501528e17cf9581d3a669a1b1c84b857a628ce 100644 (file)
@@ -208,6 +208,20 @@ public class PSPainter extends AbstractIFPainter {
         }
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect,
+            BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+
+        try {
+            borderPainter.clipBackground(rect,
+                    bpsBefore,  bpsAfter, bpsStart,  bpsEnd);
+        } catch (IOException ioe) {
+            throw new IFException("I/O error while clipping background", ioe);
+        }
+
+    }
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -235,11 +249,12 @@ public class PSPainter extends AbstractIFPainter {
 
     /** {@inheritDoc} */
     public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
-            BorderProps start, BorderProps end) throws IFException {
+            BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
         if (before != null || after != null || start != null || end != null) {
             try {
                 endTextObject();
-                this.borderPainter.drawBorders(rect, before, after, start, end);
+                this.borderPainter.drawBorders(rect, before, after, start, end,
+                        innerBackgroundColor);
             } catch (IOException ioe) {
                 throw new IFException("I/O error in drawBorderRect()", ioe);
             }
index 25ac86cedcea65e5a7316f178be2735ef81a22b6..dc60bb0ad189256caa5cbee343b8eec9b410ae1d 100644 (file)
@@ -47,6 +47,12 @@ public class BorderProps implements Serializable {
     public Color color;                                         // CSOK: VisibilityModifier
     /** Border width */
     public int width;                                           // CSOK: VisibilityModifier
+
+    private int radiusStart = 0;
+    private int radiusEnd = 0;
+
+
+
     /** Border mode (one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */
     public int mode;                                            // CSOK: VisibilityModifier
 
@@ -75,6 +81,37 @@ public class BorderProps implements Serializable {
         this(getConstantForStyle(style), width, color, mode);
     }
 
+    /**
+     *
+     * @return the radius of the corner adjacent to the before or start border
+     */
+    public int getRadiusStart() {
+        return radiusStart;
+    }
+
+    /**
+     *
+     * @param radiusStart the radius of the corner adjacent to the before or start border
+     */
+    public void setRadiusStart(int radiusStart) {
+        this.radiusStart = radiusStart;
+    }
+
+    /**
+     * @return the radius of the corner adjacent to the after or end border
+     */
+    public int getRadiusEnd() {
+        return radiusEnd;
+    }
+
+    /**
+     *
+     * @param radiusEnd the radius of the corner adjacent to the  after or end border
+     */
+    public void setRadiusEnd(int radiusEnd) {
+        this.radiusEnd = radiusEnd;
+    }
+
     /**
      * @param bp the border properties or null
      * @return the effective width of the clipped part of the border
@@ -112,7 +149,9 @@ public class BorderProps implements Serializable {
                 return (style == other.style)
                         && color.equals(other.color)
                         && width == other.width
-                        && mode == other.mode;
+                        && mode == other.mode
+                        && radiusStart == other.radiusStart
+                        && radiusEnd == other.radiusEnd;
             }
         }
         return false;
@@ -154,7 +193,18 @@ public class BorderProps implements Serializable {
                 throw new IllegalArgumentException(e.getMessage());
             }
 
-            return new BorderProps(style, width, c, mode);
+            BorderProps bp = new BorderProps(style, width, c, mode);
+
+            found = m.find();
+            if (found) {
+                int startRadius = Integer.parseInt(m.group());
+                m.find();
+                int endRadius = Integer.parseInt(m.group());
+                bp.setRadiusStart(startRadius);
+                bp.setRadiusEnd(endRadius);
+            }
+
+            return bp;
         } else {
             throw new IllegalArgumentException("BorderProps must be surrounded by parentheses");
         }
@@ -170,12 +220,24 @@ public class BorderProps implements Serializable {
         sbuf.append(',');
         sbuf.append(width);
         if (mode != SEPARATE) {
-            sbuf.append(',');
             if (mode == COLLAPSE_INNER) {
-                sbuf.append("collapse-inner");
+                sbuf.append(",collapse-inner");
             } else {
-                sbuf.append("collapse-outer");
+                sbuf.append(",collapse-outer");
+            }
+        }
+
+        if (radiusStart != 0 || radiusEnd != 0) {
+            if (mode == SEPARATE) {
+                // Because of the corner radii properties the mode must be set
+                // so that the parameter index is consistent
+                sbuf.append(",separate");
             }
+            sbuf.append(',');
+            sbuf.append(radiusStart);
+
+            sbuf.append(',');
+            sbuf.append(radiusEnd);
         }
         sbuf.append(')');
         return sbuf.toString();
index bcc3a6913e361d07f5ec076683a6ebccb5e66cd7..43590da1fae2c39af310d732c07f4b9765e3b935 100644 (file)
@@ -30,7 +30,6 @@ import java.io.IOException;
 import java.util.Map;
 
 import org.w3c.dom.Document;
-
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
@@ -267,6 +266,13 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
         //TODO Implement me!!!
     }
 
+    /** {@inheritDoc} */
+    public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+            BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+        //TODO Implement me!!!
+
+    }
+
     /** {@inheritDoc} */
     public void fillRect(Rectangle rect, Paint fill) throws IFException {
         if (fill == null) {
@@ -406,4 +412,6 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
         }
     }
 
+
+
 }