From ba7f42b128a5265f443c401613378aaa504d727d Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Thu, 30 Sep 2010 11:51:08 +0000 Subject: Bugzilla #49913: Initial code drop for support of rounded corners. Submitted by: Peter Hancock 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 --- .../org/apache/fop/afp/AFPResourceManager.java | 143 +++- src/java/org/apache/fop/afp/AFPStreamer.java | 2 +- src/java/org/apache/fop/area/Trait.java | 5 +- src/java/org/apache/fop/fo/Constants.java | 24 +- src/java/org/apache/fop/fo/FOPropertyMapping.java | 71 ++ .../fop/fo/extensions/ExtensionElementMapping.java | 11 + .../properties/BoxCornerPropShorthandParser.java | 66 ++ .../properties/CommonBorderPaddingBackground.java | 178 +++-- src/java/org/apache/fop/layoutmgr/TraitSetter.java | 57 +- .../fop/render/AbstractPathOrientedRenderer.java | 57 +- .../apache/fop/render/afp/AFPDocumentHandler.java | 51 ++ .../org/apache/fop/render/afp/AFPImageHandler.java | 52 +- .../fop/render/afp/AFPImageHandlerGraphics2D.java | 7 +- .../render/afp/AFPImageHandlerRenderedImage.java | 4 +- src/java/org/apache/fop/render/afp/AFPPainter.java | 868 ++++++++++++++++++++- .../afp/AbstractAFPImageHandlerRawStream.java | 6 +- .../fop/render/intermediate/AbstractIFPainter.java | 8 +- .../fop/render/intermediate/BorderPainter.java | 704 +++++++++++++++-- .../fop/render/intermediate/IFException.java | 12 + .../apache/fop/render/intermediate/IFPainter.java | 29 +- .../apache/fop/render/intermediate/IFParser.java | 9 +- .../apache/fop/render/intermediate/IFRenderer.java | 34 +- .../fop/render/intermediate/IFSerializer.java | 26 +- .../fop/render/java2d/Java2DBorderPainter.java | 39 + .../apache/fop/render/java2d/Java2DPainter.java | 15 +- src/java/org/apache/fop/render/pcl/PCLPainter.java | 14 +- .../apache/fop/render/pdf/PDFBorderPainter.java | 40 +- src/java/org/apache/fop/render/pdf/PDFPainter.java | 26 +- .../org/apache/fop/render/ps/PSBorderPainter.java | 54 ++ src/java/org/apache/fop/render/ps/PSPainter.java | 19 +- src/java/org/apache/fop/traits/BorderProps.java | 72 +- 31 files changed, 2501 insertions(+), 202 deletions(-) create mode 100644 src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index b7e1abc01..6aad0ca50 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -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/**/ includeNameMap - = new java.util.HashMap()/**/; - - private Map pageSegmentMap = new java.util.HashMap(); + /** Mapping of resourceInfo to AbstractCachedObject */ + private final Map/**/ includeObjectCache + = new java.util.HashMap()/**/; 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 } diff --git a/src/java/org/apache/fop/afp/AFPStreamer.java b/src/java/org/apache/fop/afp/AFPStreamer.java index 33d1dbf90..154ca4cc9 100644 --- a/src/java/org/apache/fop/afp/AFPStreamer.java +++ b/src/java/org/apache/fop/afp/AFPStreamer.java @@ -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(); diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index 42c5462e4..9e9d576c3 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -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]; diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 5f23502f3..d9b0f2527 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -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 diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index c711a76c0..e79ed25b6 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -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); diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index a12bed0fa..1ff44700d 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -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 index 000000000..d3e37a7c9 --- /dev/null +++ b/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java @@ -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; + } + +} diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index d39dc24f0..3b4b818b5 100644 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -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; diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 6039ad941..a3d44f0a5 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -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 diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index cfce3a875..ea78b958c 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -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), diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 21d4faf56..e90f0b29a 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -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/**/pageSegmentMap = new java.util.HashMap/**/(); + + // Rounded corners are cached at the document level + private Map/**/ roundedCornerNameCache + = new HashMap()/**/; + + 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} */ diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandler.java b/src/java/org/apache/fop/render/afp/AFPImageHandler.java index 244263213..0e4e7a63a 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandler.java @@ -19,7 +19,10 @@ 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; } /** diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java index 07b712ceb..5617c4668 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java @@ -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( diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java index ca0544542..48eeb79bf 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java @@ -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 diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 0bbfef3e8..895316e6b 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -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); + } + } diff --git a/src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java b/src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java index 5374c7051..9c4cb6241 100644 --- a/src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java +++ b/src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java @@ -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)); diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 1b870a7c5..ca907eb0d 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -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; + } + } diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 20402369a..31218af6b 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -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")); + } + } diff --git a/src/java/org/apache/fop/render/intermediate/IFException.java b/src/java/org/apache/fop/render/intermediate/IFException.java index 52c650765..7e12c301b 100644 --- a/src/java/org/apache/fop/render/intermediate/IFException.java +++ b/src/java/org/apache/fop/render/intermediate/IFException.java @@ -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 null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + /*public IFException( Exception cause) { + super(cause); + }*/ + } diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index 00fd74209..b0ddf462d 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -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! diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index da5d8a623..0abf3839a 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -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); } } diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index bdeb1e9a1..1d20da519 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -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); } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index edeef9766..0b3b24110 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -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; + } + + + + } diff --git a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java b/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java index b2b29188b..b3ad5ff7a 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java @@ -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 + + } + } diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 396645768..461b6e5bf 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -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); } + + } diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index afae8ac27..8adb0f12d 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -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()); } + + } diff --git a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java b/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java index f8090fdeb..4bfba9df3 100644 --- a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java @@ -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); + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 161b46617..273426c0b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -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 { diff --git a/src/java/org/apache/fop/render/ps/PSBorderPainter.java b/src/java/org/apache/fop/render/ps/PSBorderPainter.java index 1e2964eed..916289eda 100644 --- a/src/java/org/apache/fop/render/ps/PSBorderPainter.java +++ b/src/java/org/apache/fop/render/ps/PSBorderPainter.java @@ -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()); + } + } diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index b8a6d40f6..72501528e 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -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); } diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 25ac86ced..dc60bb0ad 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -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(); -- cgit v1.2.3 From ae8a5035235e1262e412e1eacd0eb3adfaef83ef Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Wed, 8 Jun 2011 12:33:47 +0000 Subject: Bugzilla #49913 Minor bug fixes and some cleanup git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1133370 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop-intermediate-format-ng-content.xsd | 12 ++-- .../org/apache/fop/afp/AFPResourceManager.java | 2 +- .../fop/render/AbstractPathOrientedRenderer.java | 10 ++- src/java/org/apache/fop/render/afp/AFPPainter.java | 10 ++- .../fop/render/intermediate/BorderPainter.java | 81 ++++++++++++++++------ .../apache/fop/render/intermediate/IFPainter.java | 18 ++--- .../apache/fop/render/intermediate/IFParser.java | 34 ++++++++- .../fop/render/intermediate/IFSerializer.java | 54 ++++++++++++++- .../org/apache/fop/render/svg/SVGPainter.java | 7 ++ 9 files changed, 186 insertions(+), 42 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd index 904eeec21..85748ac33 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd @@ -79,6 +79,7 @@ + @@ -95,11 +96,8 @@ - - - - + @@ -126,6 +124,12 @@ + + + + + + diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index 6aad0ca50..de5543084 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -132,7 +132,7 @@ public class AFPResourceManager { AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo(); updateResourceInfoUri(resourceInfo); - if (includeCachedObject(resourceInfo, null)) { + if (includeCachedObject(resourceInfo, dataObjectInfo.getObjectAreaInfo())) { return; } diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index ea78b958c..aa4b6325a 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -159,8 +159,9 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { (Trait.Background) backgroundArea.getTrait(Trait.BACKGROUND), bpsBefore, bpsAfter, bpsStart, bpsEnd); - Color bg = null; - if (backgroundTrait != null) { + // TODO what is the default bg color? Should we serialize it? + Color bg = Color.white; + if (backgroundTrait != null && backgroundTrait.getColor() != null) { bg = backgroundTrait.getColor(); } @@ -223,9 +224,9 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { updateColor(back.getColor(), true); fillRect(sx, sy, paddRectWidth, paddRectHeight); } + if (back.getImageInfo() != null) { ImageSize imageSize = back.getImageInfo().getSize(); - int horzCount = (int)((paddRectWidth * 1000 / imageSize.getWidthMpt()) + 1.0f); int vertCount = (int)((paddRectHeight @@ -267,6 +268,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { } /** + * TODO represent border related parameters in a class * Clip the background to the inner border. * This draws the border traits given the position and the traits. * @@ -300,6 +302,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { * @param bpsAfter the border-after traits * @param bpsStart the border-start traits * @param bpsEnd the border-end traits + * @param innerBackgroundColor the background color of the block */ protected void drawBorders( // CSOK: ParameterNumber float startx, float starty, float width, float height, @@ -321,6 +324,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { * @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 + * @param innerBackgroundColor the background color of the block */ protected void drawBorders( // CSOK: MethodLength Rectangle2D.Float borderRect, diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 895316e6b..c31a7ede3 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -309,6 +309,7 @@ public class AFPPainter extends AbstractIFPainter { private final boolean[] roundCorner; private final Color innerBackgroundColor; + /* TODO represent border related parameters in a class */ private BorderImagePainter(double esf, Rectangle borderRect, BorderProps bpsStart, BorderProps bpsEnd, BorderProps bpsBefore, BorderProps bpsAfter, @@ -807,7 +808,7 @@ public class AFPPainter extends AbstractIFPainter { return clip; } - + /* TODO collect parameters in a useful structure */ private void paintCorner(final Graphics2D g2d, final int beforeWidth, final int startWidth, final int beforeRadius, final int startRadius, final Color innerBackgroundColor, @@ -841,7 +842,7 @@ public class AFPPainter extends AbstractIFPainter { afterCut.lineTo(startRadius, 0); beforeCut.lineTo(0, beforeRadius); } else { - afterCut.lineTo(startRadius, (float)(borderWidthRatio * startRadius)); + afterCut.lineTo(startRadius, (borderWidthRatio * startRadius)); beforeCut.lineTo(1f / borderWidthRatio * beforeRadius, beforeRadius); afterCut.lineTo(startRadius, 0); @@ -1341,4 +1342,9 @@ public class AFPPainter extends AbstractIFPainter { bpsStart, bpsEnd); } + /** {@inheritDoc} */ + public void fillBackground(Rectangle rect, Paint fill, BorderProps bpsBefore, + BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IFException { + // not supported in AFP + } } diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 31218af6b..5cf50ecbb 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -32,9 +32,14 @@ import org.apache.fop.traits.RuleStyle; */ public abstract class BorderPainter { + /** TODO remove before integration*/ public static final String ROUNDED_CORNERS = "fop.round-corners"; + /** TODO Use a class to model border instead of an array + * convention index of before, end, after and start borders */ protected static final int BEFORE = 0, END = 1, AFTER = 2, START = 3; + /** TODO Use a class to model border corners instead of an array + convention index of before_start, before_end, after_end and after_start border corners*/ protected static final int BEFORE_START = 0, BEFORE_END = 1, AFTER_END = 2, AFTER_START = 3; @@ -45,6 +50,7 @@ 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 + * @param innerBackgroundColor the inner background color * @throws IFException if an error occurs while drawing the borders */ public void drawBorders(Rectangle borderRect, // CSOK: MethodLength @@ -71,6 +77,15 @@ public abstract class BorderPainter { return bps == null ? bps : bps.width == 0 ? (BorderProps)null : bps; } + /** + * TODO merge with drawRoundedBorders()? + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @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 + */ protected void drawRectangularBorders(Rectangle borderRect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IOException { @@ -243,6 +258,14 @@ public abstract class BorderPainter { } } + /** TODO merge with drawRectangularBorders? + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @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 on io exception + * */ protected void drawRoundedBorders(Rectangle borderRect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IOException { @@ -400,6 +423,7 @@ public abstract class BorderPainter { } } + /** TODO collect parameters into useful data structures*/ private void drawBorderSegment(final int sx2, final int ex1, final int ex2, final int outery, final int innery, final int clipWidthStart, final int clipWidthEnd, @@ -444,7 +468,7 @@ public abstract class BorderPainter { } } - if (ellipseBERadiusX != 0) { + if (ellipseBERadiusX != 0 && ellipseBERadiusY != 0) { final double[] outerJoinMetrics = getCornerBorderJoinMetrics( ellipseBERadiusX, ellipseBERadiusY, (double)innery / (ex1 - ex2)); @@ -553,11 +577,11 @@ public abstract class BorderPainter { /** * Clip the background to the inner border - * @param rect - * @param bpsBefore - * @param bpsAfter - * @param bpsStart - * @param bpsEnd + * @param rect clipping rectangle + * @param bpsBefore before border + * @param bpsAfter after border + * @param bpsStart start border + * @param bpsEnd end border * @throws IOException if an I/O error occurs */ public void clipBackground(Rectangle rect, @@ -629,7 +653,7 @@ public abstract class BorderPainter { lineTo(startx + ellipseSE, starty + height); - if (ellipseSE > 0 && ellipseSE > 0) { + if (ellipseSE > 0 && ellipseAS > 0) { arcTo( Math.PI / 2, Math.PI, startx + ellipseSE, starty + height - ellipseAS, ellipseSE, ellipseAS); } @@ -645,8 +669,9 @@ public abstract class BorderPainter { } - /* - * If the ellipse radii exceed the border edge length, the ellipses are rescaled. + /** + * TODO javadocs + * If an ellipse radii exceed the border edge length then all ellipses must be rescaled. */ protected double cornerScaleFactor(int width, int height, BorderProps bpsBefore, BorderProps bpsAfter, @@ -655,29 +680,35 @@ public abstract class BorderPainter { double esf = 1d; if (bpsBefore != null) { - if (bpsStart != null && bpsEnd != null - && bpsStart.getRadiusStart() + bpsEnd.getRadiusStart() > 0) { + double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusStart()) + + (bpsEnd == null ? 0 : bpsEnd.getRadiusStart()); - double f = (double)width / (bpsStart.getRadiusStart() + bpsEnd.getRadiusStart()); + if (ellipseExtent > 0) { + double f = width / ellipseExtent; 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()); + double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusStart()) + + (bpsBefore == null ? 0 : bpsBefore.getRadiusStart()); + + if (ellipseExtent > 0) { + double f = height / ellipseExtent; 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()); + double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusEnd()) + + (bpsEnd == null ? 0 : bpsEnd.getRadiusEnd()); + + if (ellipseExtent > 0) { + double f = width / ellipseExtent; if (f < esf) { esf = f; } @@ -685,9 +716,11 @@ public abstract class BorderPainter { } if (bpsEnd != null) { - if (bpsAfter != null && bpsBefore != null - && bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd() > 0) { - double f = (double)height / (bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd()); + double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusEnd()) + + (bpsBefore == null ? 0 : bpsBefore.getRadiusEnd()); + + if (ellipseExtent > 0) { + double f = height / ellipseExtent; if (f < esf) { esf = f; } @@ -881,6 +914,10 @@ public abstract class BorderPainter { */ protected abstract void restoreGraphicsState() throws IOException; + /** + * TODO remove the System.props when rounded corners code is stable + * @return true iff in rounded corners mode + */ public static boolean isRoundedCornersSupported() { return "true".equalsIgnoreCase(System.getProperty(ROUNDED_CORNERS, "true")); } diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index b0ddf462d..8d92d1ca9 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -182,14 +182,16 @@ public interface IFPainter { /** - * - * @param bpsBefore - * @param bpsAfter - * @param bpsStart - * @param bpsEnd - * @return true if the background needs to be painted - */ - boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter, + * TODO Painter-specific rounded borders logic required background drawing to be + * made optional. A future refactoring of the rounded borders code should aim to make + * the need for this abstraction obsolete + * @param bpsBefore the before border + * @param bpsAfter the after border + * @param bpsStart the start border + * @param bpsEnd the end border + * @return true if and only if background drawing is required + */ + boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd); /** diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 0abf3839a..0f143018d 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -583,6 +583,19 @@ public class IFParser implements IFConstants { int y = Integer.parseInt(attributes.getValue("y")); int width = Integer.parseInt(attributes.getValue("width")); int height = Integer.parseInt(attributes.getValue("height")); + BorderProps[] borders = new BorderProps[4]; + for (int i = 0; i < 4; i++) { + String b = attributes.getValue(SIDES[i]); + if (b != null) { + borders[i] = BorderProps.valueOf(userAgent, b); + } + } + + if (!(borders[0] == null && borders[1] == null + && borders[2] == null && borders[3] == null)) { + painter.clipBackground(new Rectangle(x, y, width, height), + borders[0], borders[1], borders[2], borders[3]); + } painter.clipRect(new Rectangle(x, y, width, height)); } @@ -601,7 +614,26 @@ public class IFParser implements IFConstants { } catch (PropertyException pe) { throw new IFException("Error parsing the fill attribute", pe); } - painter.fillRect(new Rectangle(x, y, width, height), fillColor); + + Rectangle rectangularArea = new Rectangle(x, y, width, height); + + //TODO should rect be overloaded to include rounded corners + // or should we introduce a new IF element? + BorderProps[] borders = new BorderProps[4]; + for (int i = 0; i < 4; i++) { + String b = attributes.getValue(SIDES[i]); + if (b != null) { + borders[i] = BorderProps.valueOf(userAgent, b); + } + } + + if (!(borders[0] == null && borders[1] == null + && borders[2] == null && borders[3] == null)) { + painter.clipBackground(rectangularArea, + borders[0], borders[1], borders[2], borders[3]); + } + + painter.fillRect(rectangularArea , fillColor); } } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 0b3b24110..4af90faed 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -474,8 +474,31 @@ 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 + try { + AttributesImpl atts = new AttributesImpl(); + addAttribute(atts, "x", Integer.toString(rect.x)); + addAttribute(atts, "y", Integer.toString(rect.y)); + addAttribute(atts, "width", Integer.toString(rect.width)); + addAttribute(atts, "height", Integer.toString(rect.height)); + if (hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd)) { + if (bpsBefore != null) { + addAttribute(atts, "before", bpsBefore.toString()); + } + if (bpsAfter != null) { + addAttribute(atts, "after", bpsAfter.toString()); + } + if (bpsStart != null) { + addAttribute(atts, "start", bpsStart.toString()); + } + if (bpsEnd != null) { + addAttribute(atts, "end", bpsEnd.toString()); + } + } + handler.element(EL_CLIP_RECT, atts); + } catch (SAXException e) { + throw new IFException("SAX error in clipRect()", e); + } } @@ -497,6 +520,35 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } + //TODO create a class representing all borders should exist + //with query methods like this + private boolean hasRoundedCorners(BorderProps bpsBefore, BorderProps bpsAfter, + BorderProps bpsStart, BorderProps bpsEnd) { + boolean rtn = false; + + if (bpsBefore != null && bpsBefore.getRadiusStart() > 0 + && bpsStart != null && bpsStart.getRadiusStart() > 0) { + rtn = true; + } + + if (bpsBefore != null && bpsBefore.getRadiusEnd() > 0 + && bpsEnd != null && bpsEnd.getRadiusStart() > 0) { + rtn = true; + } + + if (bpsEnd != null && bpsEnd.getRadiusEnd() > 0 + && bpsAfter != null && bpsAfter.getRadiusEnd() > 0) { + rtn = true; + } + + if (bpsAfter != null && bpsAfter.getRadiusStart() > 0 + && bpsStart != null && bpsStart.getRadiusEnd() > 0) { + rtn = true; + } + + return rtn; + } + /** {@inheritDoc} */ public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after, BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException { diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java index 43590da1f..2e48e35b3 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java @@ -412,6 +412,13 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants { } } + /** {@inheritDoc} */ + public void fillBackground(Rectangle rect, Paint fill, BorderProps bpsBefore, + BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IFException { + // Not supported in SVG + + } + } -- cgit v1.2.3 From 476528a0e1f7156b83ba08ba48ec8f24b1476e73 Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Thu, 5 Jul 2012 09:20:58 +0000 Subject: Minor cleanup git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1357514 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/afp/AFPResourceManager.java | 6 +-- src/java/org/apache/fop/fo/Constants.java | 1 - src/java/org/apache/fop/fo/FOPropertyMapping.java | 1 - .../fop/fo/extensions/ExtensionElementMapping.java | 1 - .../properties/CommonBorderPaddingBackground.java | 28 +++++-------- .../fop/render/AbstractPathOrientedRenderer.java | 2 +- .../apache/fop/render/afp/AFPDocumentHandler.java | 2 +- src/java/org/apache/fop/render/afp/AFPPainter.java | 1 - .../fop/render/intermediate/BorderPainter.java | 49 +++++++++++----------- .../fop/render/intermediate/IFSerializer.java | 1 + src/java/org/apache/fop/traits/BorderProps.java | 5 +-- 11 files changed, 41 insertions(+), 56 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index 43dc626a7..885e13e5b 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -70,7 +70,6 @@ public class AFPResourceManager { /** Mapping of resourceInfo to AbstractCachedObject */ private final Map includeObjectCache = new java.util.HashMap(); - private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults(); /** @@ -427,9 +426,7 @@ public class AFPResourceManager { resourceInfo.setName(resourceName); resourceInfo.setUri(uri.toASCIIString()); - AbstractCachedObject cachedObject = (AbstractCachedObject) - includeObjectCache.get(resourceInfo); - + AbstractCachedObject cachedObject = (AbstractCachedObject) includeObjectCache.get(resourceInfo); if (cachedObject == null) { ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel); @@ -457,7 +454,6 @@ public class AFPResourceManager { @Override protected void writeEnd(OutputStream os) throws IOException { } }; - resourceGroup.addObject(resourceObject); cachedObject = new CachedObject(resourceName, null); includeObjectCache.put(resourceInfo, cachedObject); diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 324a470f5..14c4b5890 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -779,7 +779,6 @@ public interface Constants { /** Property constant - FOP proprietary prototype (in XSL-FO 2.0 Requirements) */ int PR_X_XML_BASE = 275; - /** Property constant FOP proprietary*/ int PR_X_BORDER_BEFORE_RADIUS_START = 276; diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 8934a7594..eac510fb0 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -212,7 +212,6 @@ public final class FOPropertyMapping implements Constants { genericCondCornerRadius.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT); genericCondCornerRadius.addShorthand(generics[PR_X_BORDER_RADIUS]); - // GenericBreak genericBreak = new EnumProperty.Maker(0); genericBreak.setInherited(false); diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 1e0755233..008bca8a8 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -52,7 +52,6 @@ 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"); diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index f7053f8ee..70e74f6d6 100644 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -216,18 +216,15 @@ public class CommonBorderPaddingBackground { if (this == obj) { return true; } - if (obj instanceof BorderInfo) { - BorderInfo bi = (BorderInfo)obj; - return (this.mColor == bi.mColor - && this.mStyle == bi.mStyle - && this.mWidth == bi.mWidth - && this.radiusStart == bi.radiusStart - && this.radiusEnd == bi.radiusEnd); + if (!(obj instanceof BorderInfo)) { + return false; } - BorderInfo other = (BorderInfo) obj; - return CompareUtil.equal(mColor, other.mColor) - && mStyle == other.mStyle - && CompareUtil.equal(mWidth, other.mWidth); + BorderInfo bi = (BorderInfo)obj; + return (this.mColor == bi.mColor + && this.mStyle == bi.mStyle + && this.mWidth == bi.mWidth + && this.radiusStart == bi.radiusStart + && this.radiusEnd == bi.radiusEnd); } @Override @@ -252,12 +249,9 @@ public class CommonBorderPaddingBackground { * 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, - new ConditionalNullLength(), new ConditionalNullLength()); - - - + private static final BorderInfo DEFAULT_BORDER_INFO = BorderInfo.getInstance( + Constants.EN_NONE, new ConditionalNullLength(), null, new ConditionalNullLength(), + new ConditionalNullLength()); /** * A conditional length of value 0. Returned by the diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index 042efccc5..8b90d23bd 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -173,7 +173,6 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { drawBackground(startx, starty, width, height, (Trait.Background) backgroundArea.getTrait(Trait.BACKGROUND), bpsBefore, bpsAfter, bpsStart, bpsEnd, backgroundArea.getBidiLevel()); - // TODO what is the default bg color? Should we serialize it? Color bg = Color.white; if (backgroundTrait != null && backgroundTrait.getColor() != null) { @@ -328,6 +327,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { * @param bpsAfter the border-after traits * @param bpsStart the border-start traits * @param bpsEnd the border-end traits + * @param innerBackgroundColor the background color of the block */ protected void clipBackground (float startx, float starty, float width, float height, diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 9c786acf5..a82c27bac 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -23,8 +23,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.io.IOException; -import java.util.HashMap; import java.net.URI; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 47ff5dba1..1d7c41c12 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -354,7 +354,6 @@ public class AFPPainter extends AbstractIFPainter { = new Area[]{new Area(), new Area(), new Area(), new Area()}; if (roundCorner[TOP_LEFT]) { - AffineTransform transform = new AffineTransform(); int beforeRadius = (int)(esf * bpsBefore.getRadiusStart()); int startRadius = (int)(esf * bpsStart.getRadiusStart()); diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 0ff5c2036..776cd3c32 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -101,30 +101,33 @@ public abstract class BorderPainter { int width = borderRect.width; int height = borderRect.height; boolean[] b = new boolean[] { - (bpsTop != null), (bpsRight != null), - (bpsBottom != null), (bpsLeft != null)}; - if (!b[0] && !b[1] && !b[2] && !b[3]) { + (bpsTop != null), (bpsRight != null), + (bpsBottom != null), (bpsLeft != null)}; + if (!b[TOP] && !b[RIGHT] && !b[BOTTOM] && !b[LEFT]) { return; } int[] bw = new int[] { - (b[0] ? bpsTop.width : 0), - (b[1] ? bpsRight.width : 0), - (b[2] ? bpsBottom.width : 0), - (b[3] ? bpsLeft.width : 0)}; + (b[TOP] ? bpsTop.width : 0), + (b[RIGHT] ? bpsRight.width : 0), + (b[BOTTOM] ? bpsBottom.width : 0), + (b[LEFT] ? bpsLeft.width : 0)}; int[] clipw = new int[] { - BorderProps.getClippedWidth(bpsTop), - BorderProps.getClippedWidth(bpsRight), - BorderProps.getClippedWidth(bpsBottom), - BorderProps.getClippedWidth(bpsLeft)}; - starty += clipw[0]; - height -= clipw[0]; - height -= clipw[2]; - startx += clipw[3]; - width -= clipw[3]; - width -= clipw[1]; + BorderProps.getClippedWidth(bpsTop), + BorderProps.getClippedWidth(bpsRight), + BorderProps.getClippedWidth(bpsBottom), + BorderProps.getClippedWidth(bpsLeft)}; + starty += clipw[TOP]; + height -= clipw[TOP]; + height -= clipw[BOTTOM]; + startx += clipw[LEFT]; + width -= clipw[LEFT]; + width -= clipw[RIGHT]; boolean[] slant = new boolean[] { - (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])}; + (b[LEFT] && b[TOP]), + (b[TOP] && b[RIGHT]), + (b[RIGHT] && b[BOTTOM]), + (b[BOTTOM] && b[LEFT])}; if (bpsTop != null) { int sx1 = startx; int sx2 = (slant[TOP_LEFT] ? sx1 + bw[LEFT] - clipw[LEFT] : sx1); @@ -142,10 +145,10 @@ public abstract class BorderPainter { int ex1a = ex1; if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipw[3]; + sx1a -= clipw[LEFT]; } if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipw[1]; + ex1a += clipw[RIGHT]; } lineTo(sx1a, outery); lineTo(ex1a, outery); @@ -167,7 +170,6 @@ public abstract class BorderPainter { int outerx = startx + width + clipw[RIGHT]; int clipx = outerx - clipw[RIGHT]; int innerx = outerx - bw[RIGHT]; - saveGraphicsState(); moveTo(clipx, sy1); int sy1a = sy1; @@ -199,7 +201,6 @@ public abstract class BorderPainter { int outery = starty + height + clipw[BOTTOM]; int clipy = outery - clipw[BOTTOM]; int innery = outery - bw[BOTTOM]; - saveGraphicsState(); moveTo(ex1, clipy); int sx1a = sx1; @@ -231,7 +232,7 @@ public abstract class BorderPainter { int outerx = startx - clipw[LEFT]; int clipx = outerx + clipw[LEFT]; int innerx = outerx + bw[LEFT]; - + saveGraphicsState(); moveTo(clipx, ey1); @@ -286,7 +287,6 @@ public abstract class BorderPainter { (b[RIGHT] ? bpsEnd.width : 0), (b[BOTTOM] ? bpsAfter.width : 0), (b[LEFT] ? bpsStart.width : 0)}; - int[] clipw = new int[] { BorderProps.getClippedWidth(bpsBefore), BorderProps.getClippedWidth(bpsEnd), @@ -301,7 +301,6 @@ public abstract class BorderPainter { boolean[] slant = new boolean[] { (b[LEFT] && b[TOP]), (b[TOP] && b[RIGHT]), (b[RIGHT] && b[BOTTOM]), (b[LEFT] && b[BOTTOM])}; - //Determine scale factor if any adjacent elliptic corners overlap double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 749f68d78..28a22779a 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -31,6 +31,7 @@ import java.util.Locale; import java.util.Map; import org.w3c.dom.Document; + import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 051041c1d..3eca9c43f 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -51,10 +51,9 @@ public class BorderProps implements Serializable { 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 -- cgit v1.2.3 From 3e96fb119fcbd9e6e0644092b0e2376bdbbe6c10 Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Mon, 13 Aug 2012 12:22:47 +0000 Subject: Fixed an IF serialization bug and an AFP image regression git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1372388 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java | 1 + src/java/org/apache/fop/render/intermediate/IFSerializer.java | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java index 3e0780cbf..3adbd68f6 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java @@ -201,6 +201,7 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima ImageInfo imageInfo = imageRendered.getInfo(); this.intrinsicSize = imageInfo.getSize(); this.effIntrinsicSize = intrinsicSize; + effIntrinsicSize.setSizeInPixels(renderedImage.getWidth(), renderedImage.getHeight()); AFPResourceInfo resourceInfo = imageObjectInfo.getResourceInfo(); this.usePageSegments = useFS10 && !resourceInfo.getLevel().isInline(); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 28a22779a..24239e5f4 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -31,7 +31,6 @@ import java.util.Locale; import java.util.Map; import org.w3c.dom.Document; - import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -531,16 +530,16 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler { if (hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd)) { if (bpsBefore != null) { - addAttribute(atts, "before", bpsBefore.toString()); + addAttribute(atts, "top", bpsBefore.toString()); } if (bpsAfter != null) { - addAttribute(atts, "after", bpsAfter.toString()); + addAttribute(atts, "bottom", bpsAfter.toString()); } if (bpsStart != null) { - addAttribute(atts, "start", bpsStart.toString()); + addAttribute(atts, "left", bpsStart.toString()); } if (bpsEnd != null) { - addAttribute(atts, "end", bpsEnd.toString()); + addAttribute(atts, "right", bpsEnd.toString()); } } handler.element(EL_CLIP_RECT, atts); -- cgit v1.2.3 From 118f9d4f0f6aeed4971ea9cc61fd4d63198c9a84 Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Fri, 17 Aug 2012 08:57:56 +0000 Subject: Removed rounded corner-related backdoor (System property) config git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1374180 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/afp/AFPResourceManager.java | 1 - .../fop/render/AbstractPathOrientedRenderer.java | 9 +- src/java/org/apache/fop/render/afp/AFPPainter.java | 257 +-------------------- .../fop/render/intermediate/BorderPainter.java | 53 +---- 4 files changed, 6 insertions(+), 314 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index e9c8eda67..59dab55c3 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -428,7 +428,6 @@ public class AFPResourceManager { AbstractCachedObject cachedObject = (AbstractCachedObject) includeObjectCache.get(resourceInfo); if (cachedObject == null) { - ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel); //resourceObject delegates write commands to copyNamedResource() diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index 8b90d23bd..c2bb56dcf 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -48,7 +48,6 @@ import org.apache.fop.area.inline.InlineViewport; 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; import org.apache.fop.util.UnitConv; @@ -259,13 +258,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { saveGraphicsState(); - //TODO remove this choice - if (BorderPainter.isRoundedCornersSupported()) { - clipBackground(sx, sy, paddRectWidth, paddRectHeight, - bpsTop, bpsBottom, bpsLeft, bpsRight); - } else { - clipRect(sx, sy, paddRectWidth, paddRectHeight); - } + clipBackground(sx, sy, paddRectWidth, paddRectHeight, bpsTop, bpsBottom, bpsLeft, bpsRight); if (back.getColor() != null) { updateColor(back.getColor(), true); diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 1d7c41c12..cf97494fa 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -536,11 +536,6 @@ public class AFPPainter extends AbstractIFPainter { } } - 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; @@ -555,50 +550,17 @@ public class AFPPainter extends AbstractIFPainter { 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))); + throws IFException { + drawRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd, innerBackgroundColor); } private boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) { - - boolean rtn = !(isRoundedCornersSupported() && isModeAllInOne() - && hasRoundedCorners(bpsBefore, bpsAfter, - bpsStart, bpsEnd)); - return rtn; + return !hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd); } 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) @@ -609,63 +571,6 @@ public class AFPPainter extends AbstractIFPainter { && (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(TOP_LEFT, 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(TOP_RIGHT, 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(BOTTOM_RIGHT, 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(BOTTOM_LEFT, area, bpsAfter, bpsStart, - afterRadiusStart, startRadiusEnd, innerBackgroundColor); - } - } - private void drawRoundedCorners(final Rectangle borderRect, final BorderProps bpsBefore, final BorderProps bpsAfter, final BorderProps bpsStart, final BorderProps bpsEnd, @@ -828,162 +733,6 @@ public class AFPPainter extends AbstractIFPainter { return clip; } - /* TODO collect parameters in a useful structure */ - 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, (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 TOP_LEFT: - //No transform required - break; - case TOP_RIGHT: - t = new AffineTransform(-1, 0, 0, 1, startRadius, 0); - g2d.transform(t); - break; - case BOTTOM_RIGHT: - t = new AffineTransform(-1, 0, 0, -1, startRadius, beforeRadius); - g2d.transform(t); - break; - case BOTTOM_LEFT: - 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) { diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 776cd3c32..4104f924c 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -57,17 +57,8 @@ public abstract class BorderPainter { BorderProps bpsTop, BorderProps bpsBottom, BorderProps bpsLeft, BorderProps bpsRight, Color innerBackgroundColor) throws IFException { - try { - if (isRoundedCornersSupported()) { - drawRoundedBorders(borderRect, bpsTop, bpsBottom, - bpsLeft, bpsRight); - - } else { - drawRectangularBorders(borderRect, bpsTop, bpsBottom, - bpsLeft, bpsRight); - } - + drawRoundedBorders(borderRect, bpsTop, bpsBottom, bpsLeft, bpsRight); } catch (IOException ioe) { throw new IFException("IO error drawing borders", ioe); } @@ -533,44 +524,12 @@ public abstract class BorderPainter { 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 + ) / (ellipseCenterY * ellipseCenterY + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio); double y = borderWidthRatio * x; - return new double[]{x, y, Math.atan((ellipseCenterY - y) / (ellipseCenterX - x))}; } @@ -913,12 +872,4 @@ public abstract class BorderPainter { */ protected abstract void restoreGraphicsState() throws IOException; - /** - * TODO remove the System.props when rounded corners code is stable - * @return true iff in rounded corners mode - */ - public static boolean isRoundedCornersSupported() { - return "true".equalsIgnoreCase(System.getProperty(ROUNDED_CORNERS, "true")); - } - } -- cgit v1.2.3 From e079e8eb1900e3287f28a34a6053b62ae4b241be Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Mon, 8 Oct 2012 09:24:03 +0000 Subject: Increased unit test coverage for the Painters git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1395490 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/layoutmgr/TraitSetter.java | 34 +- .../layoutmgr/table/TableCellLayoutManager.java | 4 +- .../fop/render/AbstractPathOrientedRenderer.java | 29 +- src/java/org/apache/fop/render/afp/AFPPainter.java | 325 +++---- .../intermediate/ArcToBezierCurveTransformer.java | 102 +++ .../render/intermediate/BezierCurvePainter.java | 37 + .../fop/render/intermediate/BorderPainter.java | 998 +++++++++++---------- .../fop/render/intermediate/GraphicsPainter.java | 145 +++ .../fop/render/java2d/Java2DBorderPainter.java | 357 -------- .../fop/render/java2d/Java2DGraphicsPainter.java | 333 +++++++ .../apache/fop/render/java2d/Java2DPainter.java | 15 +- .../apache/fop/render/pdf/PDFBorderPainter.java | 369 -------- .../apache/fop/render/pdf/PDFGraphicsPainter.java | 499 +++++++++++ src/java/org/apache/fop/render/pdf/PDFPainter.java | 15 +- .../org/apache/fop/render/ps/PSBorderPainter.java | 387 -------- .../apache/fop/render/ps/PSGraphicsPainter.java | 387 ++++++++ src/java/org/apache/fop/render/ps/PSPainter.java | 11 +- src/java/org/apache/fop/traits/BorderProps.java | 213 ++--- .../apache/fop/render/afp/AFPPainterTestCase.java | 131 +++ .../intermediate/AbstractIFPainterTestCase.java | 10 +- .../ArcToBezierCurveTransformerTestCase.java | 79 ++ .../render/intermediate/BorderPainterTestCase.java | 565 ++++++++++++ .../fop/render/pdf/PDFGraphicsPainterTestCase.java | 170 ++++ .../apache/fop/render/pdf/PDFPainterTestCase.java | 87 ++ .../apache/fop/render/ps/PSPainterTestCase.java | 44 + .../org/apache/fop/traits/BorderPropsTestCase.java | 44 +- 26 files changed, 3439 insertions(+), 1951 deletions(-) create mode 100644 src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java create mode 100644 src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java create mode 100644 src/java/org/apache/fop/render/intermediate/GraphicsPainter.java delete mode 100644 src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java create mode 100644 src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java delete mode 100644 src/java/org/apache/fop/render/pdf/PDFBorderPainter.java create mode 100644 src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java delete mode 100644 src/java/org/apache/fop/render/ps/PSBorderPainter.java create mode 100644 src/java/org/apache/fop/render/ps/PSGraphicsPainter.java create mode 100644 test/java/org/apache/fop/render/afp/AFPPainterTestCase.java create mode 100644 test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java create mode 100644 test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java create mode 100644 test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java create mode 100644 test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 3648f7091..b96283a4e 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -80,19 +80,19 @@ public final class TraitSetter { addBorderTrait(area, bpProps, isNotFirst, CommonBorderPaddingBackground.START, - BorderProps.SEPARATE, Trait.BORDER_START, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_START, context); addBorderTrait(area, bpProps, isNotLast, CommonBorderPaddingBackground.END, - BorderProps.SEPARATE, Trait.BORDER_END, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_END, context); addBorderTrait(area, bpProps, false, CommonBorderPaddingBackground.BEFORE, - BorderProps.SEPARATE, Trait.BORDER_BEFORE, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_BEFORE, context); addBorderTrait(area, bpProps, false, CommonBorderPaddingBackground.AFTER, - BorderProps.SEPARATE, Trait.BORDER_AFTER, context); + BorderProps.Mode.SEPARATE, Trait.BORDER_AFTER, context); } /* @@ -104,18 +104,14 @@ public final class TraitSetter { */ private static void addBorderTrait(Area area, CommonBorderPaddingBackground bpProps, - boolean bDiscard, int iSide, int mode, - Integer oTrait, PercentBaseContext context) { + boolean bDiscard, int iSide, BorderProps.Mode mode, + Integer traitCode, PercentBaseContext context) { int iBP = bpProps.getBorderWidth(iSide, bDiscard); 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); + area.addTrait(traitCode, new BorderProps(bpProps.getBorderStyle(iSide), iBP, radiusStart, radiusEnd, + bpProps.getBorderColor(iSide), mode)); } } @@ -273,14 +269,8 @@ public final class TraitSetter { 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; + return new BorderProps(bordProps.getBorderStyle(side), width, radiusStart, radiusEnd, + bordProps.getBorderColor(side), BorderProps.Mode.SEPARATE); } else { return null; } @@ -290,8 +280,8 @@ public final class TraitSetter { assert borderInfo != null; int width = borderInfo.getRetainedWidth(); if (width != 0) { - return new BorderProps(borderInfo.getStyle(), width, borderInfo.getColor(), - (outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER)); + return BorderProps.makeRectangular(borderInfo.getStyle(), width, borderInfo.getColor(), + (outer ? BorderProps.Mode.COLLAPSE_OUTER : BorderProps.Mode.COLLAPSE_INNER)); } else { return null; } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index f810d20c5..5e9b785d2 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -597,9 +597,9 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); blocks[i][j].setPositioning(Block.ABSOLUTE); } - blocks[i][j].addTrait(side, new BorderProps(border.getStyle(), + blocks[i][j].addTrait(side, BorderProps.makeRectangular(border.getStyle(), border.getRetainedWidth(), border.getColor(), - outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER)); + outer ? BorderProps.Mode.COLLAPSE_OUTER : BorderProps.Mode.COLLAPSE_INNER)); } private static void adjustXOffset(Block block, int amount) { diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index c2bb56dcf..c16113de3 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -429,11 +429,12 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(sx1, clipy); float sx1a = sx1; float ex1a = ex1; - if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + + if (isCollapseOuter(bpsTop)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(sx1a, outery); @@ -463,11 +464,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(clipx, sy1); float sy1a = sy1; float ey1a = ey1; - if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, sy1a); @@ -497,11 +498,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(ex1, clipy); float sx1a = sx1; float ex1a = ex1; - if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(ex1a, outery); @@ -531,11 +532,11 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { moveTo(clipx, ey1); float sy1a = sy1; float ey1a = ey1; - if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsLeft)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, ey1a); @@ -551,6 +552,10 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { } } + private boolean isCollapseOuter(BorderProps bp) { + return bp != null && bp.isCollapseOuter(); + } + /** * Common method to render the background and borders for any inline area. * The all borders and padding are drawn outside the specified area. diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index cf97494fa..36afbb9e0 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -38,9 +38,6 @@ 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; @@ -76,6 +73,7 @@ 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; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -88,15 +86,12 @@ import org.apache.fop.util.CharUtilities; */ public class AFPPainter extends AbstractIFPainter { - - - - //** logging instance */ - private static Log log = LogFactory.getLog(AFPPainter.class); - private static final int X = 0; + private static final int Y = 1; + private final GraphicsPainter graphicsPainter; + /** the border painter */ private final AFPBorderPainterAdapter borderPainter; /** the rectangle painter */ @@ -114,9 +109,9 @@ public class AFPPainter extends AbstractIFPainter { public AFPPainter(AFPDocumentHandler documentHandler) { super(documentHandler); this.state = IFState.create(); - - this.borderPainter = new AFPBorderPainterAdapter( - new AFPBorderPainter(getPaintingState(), getDataStream()), this, documentHandler); + this.graphicsPainter = new AFPGraphicsPainter( + new AFPBorderPainter(getPaintingState(), getDataStream())); + this.borderPainter = new AFPBorderPainterAdapter(graphicsPainter, this, documentHandler); this.rectanglePainter = documentHandler.createRectanglePainter(); this.unitConv = getPaintingState().getUnitConverter(); this.eventProducer = AFPEventProducer.Provider.get(getUserAgent().getEventBroadcaster()); @@ -142,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 { @@ -248,10 +243,10 @@ public class AFPPainter extends AbstractIFPainter { /** {@inheritDoc} */ protected void drawImage(Image image, Rectangle rect, RenderingContext context, boolean convert, Map additionalHints) - throws IOException, ImageException { + throws IOException, ImageException { - AFPRenderingContext afpContext = (AFPRenderingContext)context; + AFPRenderingContext afpContext = (AFPRenderingContext) context; AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation( image.getInfo().getOriginalURI(), @@ -292,7 +287,7 @@ public class AFPPainter extends AbstractIFPainter { } if (rect.width != 0 && rect.height != 0) { if (fill instanceof Color) { - getPaintingState().setColor((Color)fill); + getPaintingState().setColor((Color) fill); } else { throw new UnsupportedOperationException("Non-Color paints NYI"); } @@ -315,13 +310,82 @@ public class AFPPainter extends AbstractIFPainter { } + private static final class AFPGraphicsPainter implements GraphicsPainter { + + private final AFPBorderPainter graphicsPainter; + + private AFPGraphicsPainter(AFPBorderPainter delegate) { + this.graphicsPainter = delegate; + } + + public void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) + throws IOException { + BorderPaintingInfo borderPaintInfo = new BorderPaintingInfo( + toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), + horz, style, color); + graphicsPainter.paint(borderPaintInfo); + } + + private float toPoints(int mpt) { + return mpt / 1000f; + } + + public void drawLine(Point start, Point end, int width, + Color color, RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException("Can only deal with horizontal lines right now"); + } + //Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated. + int halfWidth = width / 2; + drawBorderLine(start.x, start.y - halfWidth, end.x, start.y + halfWidth, + true, true, style.getEnumValue(), color); + } + + public void moveTo(int x, int y) throws IOException { + } + + public void lineTo(int x, int y) throws IOException { + } + + public void arcTo(double startAngle, double endAngle, int cx, int cy, + int width, int height) throws IOException { + } + + public void rotateCoordinates(double angle) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate rotation"); + } + + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate translation"); + } + + public void scaleCoordinates(float xScale, float yScale) throws IOException { + throw new UnsupportedOperationException("Cannot handle coordinate scaling"); + } + + public void closePath() throws IOException { + } + + public void clip() throws IOException { + } + + public void saveGraphicsState() throws IOException { + } + + public void restoreGraphicsState() throws IOException { + } + + + } + //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 double cornerCorrectionFactor; private final Rectangle borderRect; private final BorderProps bpsStart; private final BorderProps bpsEnd; @@ -331,11 +395,11 @@ public class AFPPainter extends AbstractIFPainter { private final Color innerBackgroundColor; /* TODO represent border related parameters in a class */ - private BorderImagePainter(double esf, Rectangle borderRect, + private BorderImagePainter(double cornerCorrectionFactor, Rectangle borderRect, BorderProps bpsStart, BorderProps bpsEnd, BorderProps bpsBefore, BorderProps bpsAfter, boolean[] roundCorner, Color innerBackgroundColor) { - this.esf = esf; + this.cornerCorrectionFactor = cornerCorrectionFactor; this.borderRect = borderRect; this.bpsStart = bpsStart; this.bpsBefore = bpsBefore; @@ -350,13 +414,12 @@ public class AFPPainter extends AbstractIFPainter { //background Area background = new Area(area); Area cornerRegion = new Area(); - Area[] cornerBorder - = new Area[]{new Area(), new Area(), new Area(), new Area()}; + Area[] cornerBorder = new Area[]{new Area(), new Area(), new Area(), new Area()}; if (roundCorner[TOP_LEFT]) { AffineTransform transform = new AffineTransform(); - int beforeRadius = (int)(esf * bpsBefore.getRadiusStart()); - int startRadius = (int)(esf * bpsStart.getRadiusStart()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusStart()); + int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusStart()); int beforeWidth = bpsBefore.width; int startWidth = bpsStart.width; @@ -380,8 +443,8 @@ public class AFPPainter extends AbstractIFPainter { AffineTransform transform = new AffineTransform(-1, 0, 0, 1, borderRect.width, 0); - int beforeRadius = (int)(esf * bpsBefore.getRadiusEnd()); - int startRadius = (int)(esf * bpsEnd.getRadiusStart()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusEnd()); + int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusStart()); int beforeWidth = bpsBefore.width; int startWidth = bpsEnd.width; @@ -405,8 +468,8 @@ public class AFPPainter extends AbstractIFPainter { 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 beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusEnd()); + int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusEnd()); int beforeWidth = bpsAfter.width; int startWidth = bpsEnd.width; @@ -429,8 +492,8 @@ public class AFPPainter extends AbstractIFPainter { AffineTransform transform = new AffineTransform(1, 0, 0, -1, 0, borderRect.height); - int beforeRadius = (int)(esf * bpsAfter.getRadiusStart()); - int startRadius = (int)(esf * bpsStart.getRadiusEnd()); + int beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusStart()); + int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusEnd()); int beforeWidth = bpsAfter.width; int startWidth = bpsStart.width; @@ -499,8 +562,7 @@ public class AFPPainter extends AbstractIFPainter { borderPath.lineTo( borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width), borderRect.height - bpsAfter.width); - borderPath.lineTo( - bpsStart == null ? 0 : bpsStart.width, + borderPath.lineTo(bpsStart == null ? 0 : bpsStart.width, borderRect.height - bpsAfter.width); Area border = new Area(borderPath); @@ -536,13 +598,12 @@ public class AFPPainter extends AbstractIFPainter { } } - private AFPBorderPainter delegate; private final AFPPainter painter; private final AFPDocumentHandler documentHandler; - public AFPBorderPainterAdapter(AFPBorderPainter borderPainter, AFPPainter painter, + public AFPBorderPainterAdapter(GraphicsPainter graphicsPainter, AFPPainter painter, AFPDocumentHandler documentHandler) { - this.delegate = borderPainter; + super(graphicsPainter); this.painter = painter; this.documentHandler = documentHandler; } @@ -559,55 +620,49 @@ public class AFPPainter extends AbstractIFPainter { return !hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd); } - private boolean hasRoundedCorners( final BorderProps bpsBefore, final BorderProps bpsAfter, + 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)); + && (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 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 Color innerBackgroundColor) throws IFException { + final double cornerCorrectionFactor = calculateCornerCorrectionFactor(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 - }; - + && bpsBefore.getRadiusStart() > 0 + && bpsStart.getRadiusStart() > 0 + && isNotCollapseOuter(bpsBefore) + && isNotCollapseOuter(bpsStart), + bpsEnd != null && bpsBefore != null + && bpsEnd.getRadiusStart() > 0 + && bpsBefore.getRadiusEnd() > 0 + && isNotCollapseOuter(bpsEnd) + && isNotCollapseOuter(bpsBefore), + bpsEnd != null && bpsAfter != null + && bpsEnd.getRadiusEnd() > 0 + && bpsAfter.getRadiusEnd() > 0 + && isNotCollapseOuter(bpsEnd) + && isNotCollapseOuter(bpsAfter), + bpsStart != null && bpsAfter != null + && bpsStart.getRadiusEnd() > 0 + && bpsAfter.getRadiusStart() > 0 + && isNotCollapseOuter(bpsStart) + && isNotCollapseOuter(bpsAfter) + }; if (!roundCorner[TOP_LEFT] && !roundCorner[TOP_RIGHT] - && !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) { + && !roundCorner[BOTTOM_RIGHT] && !roundCorner[BOTTOM_LEFT]) { try { drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); } catch (IOException ioe) { @@ -627,16 +682,19 @@ public class AFPPainter extends AbstractIFPainter { name = documentHandler.cacheRoundedCorner(areaKey); - painter = new BorderImagePainter(esf, borderRect, + painter = new BorderImagePainter(cornerCorrectionFactor, borderRect, bpsStart, bpsEnd, bpsBefore, bpsAfter, roundCorner, innerBackgroundColor); } paintCornersAsBitmap(painter, borderRect, name); } + private boolean isNotCollapseOuter(BorderProps bp) { + return !bp.isCollapseOuter(); + } private Area makeCornerClip(final int beforeRadius, final int startRadius, - final AffineTransform transform) { + final AffineTransform transform) { Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius); @@ -678,7 +736,7 @@ public class AFPPainter extends AbstractIFPainter { GeneralPath cut = new GeneralPath(); cut.moveTo(0, 0); - float borderWidthRatio = ((float)beforeWidth) / startWidth; + float borderWidthRatio = ((float) beforeWidth) / startWidth; if (beforeWidth * startRadius > startWidth * beforeRadius) { cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, 0); @@ -718,7 +776,7 @@ public class AFPPainter extends AbstractIFPainter { GeneralPath cut = new GeneralPath(); cut.moveTo(0, 0); - float borderWidthRatio = ((float)beforeWidth) / startWidth; + float borderWidthRatio = ((float) beforeWidth) / startWidth; if (beforeWidth * startRadius > startWidth * beforeRadius) { cut.lineTo(startRadius, borderWidthRatio * startRadius); cut.lineTo(startRadius, 0); @@ -735,7 +793,7 @@ public class AFPPainter extends AbstractIFPainter { private String makeKey(Rectangle area, BorderProps beforeProps, BorderProps endProps, BorderProps afterProps, BorderProps startProps, - Color innerBackgroundColor) { + Color innerBackgroundColor) { return hash(new StringBuffer() .append(area.width) @@ -770,9 +828,9 @@ public class AFPPainter extends AbstractIFPainter { 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]); + byte b = result[idx]; + sb.append(digits[(b & 0xf0) >> 4]); + sb.append(digits[b & 0x0f]); } return sb.toString(); } @@ -816,107 +874,19 @@ public class AFPPainter extends AbstractIFPainter { } } - @Override - protected void clip() throws IOException { - //not supported by AFP - } - - @Override - protected void closePath() throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void moveTo(int x, int y) throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void lineTo(int x, int y) throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void saveGraphicsState() throws IOException { - //used for clipping only, so not implemented - } - - @Override - protected void restoreGraphicsState() throws IOException { - //used for clipping only, so not implemented - } - - private float toPoints(int mpt) { - return mpt / 1000f; - } - - @Override - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color color) throws IOException { - BorderPaintingInfo borderPaintInfo = new BorderPaintingInfo( - toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), - horz, style, color); - delegate.paint(borderPaintInfo); - } - - @Override - public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) - throws IOException { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - //Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated. - int halfWidth = width / 2; - drawBorderLine(start.x, start.y - halfWidth, end.x, start.y + halfWidth, - 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"); - - } - + 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} */ @Override 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); + this.graphicsPainter.drawLine(start, end, width, color, style); } catch (IOException ioe) { throw new IFException("I/O error in drawLine()", ioe); } @@ -936,14 +906,14 @@ public class AFPPainter extends AbstractIFPainter { // register font as necessary Map fontMetricMap = getFontInfo().getFonts(); - final AFPFont afpFont = (AFPFont)fontMetricMap.get(fontKey); + final AFPFont afpFont = (AFPFont) fontMetricMap.get(fontKey); final Font font = getFontInfo().getFontInstance(triplet, fontSize); AFPPageFonts pageFonts = getPaintingState().getPageFonts(); AFPFontAttributes fontAttributes = pageFonts.registerFont(fontKey, afpFont, fontSize); final int fontReference = fontAttributes.getFontReference(); - final int[] coords = unitConv.mpts2units(new float[] {x, y} ); + final int[] coords = unitConv.mpts2units(new float[] {x, y}); final CharacterSet charSet = afpFont.getCharacterSet(fontSize); @@ -967,7 +937,7 @@ public class AFPPainter extends AbstractIFPainter { builder.absoluteMoveInline(p.x); builder.setExtendedTextColor(state.getTextColor()); - builder.setCodedFont((byte)fontReference); + builder.setCodedFont((byte) fontReference); int l = text.length(); int[] dx = IFUtil.convertDPToDX ( dp ); @@ -1106,15 +1076,12 @@ public class AFPPainter extends AbstractIFPainter { /** {@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); + return borderPainter.isBackgroundRequired(bpsBefore, bpsAfter, bpsStart, bpsEnd); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java b/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java new file mode 100644 index 000000000..112808f62 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.intermediate; + +import java.io.IOException; + +public class ArcToBezierCurveTransformer { + + private final BezierCurvePainter bezierCurvePainter; + + public ArcToBezierCurveTransformer(BezierCurvePainter bezierCurvePainter) { + this.bezierCurvePainter = bezierCurvePainter; + } + + /** + * 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 + */ + public 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 + bezierCurvePainter.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; + } + } + } +} diff --git a/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java new file mode 100644 index 000000000..92eb334e0 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.intermediate; + +import java.io.IOException; + +public interface BezierCurvePainter { + /** + * 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 + */ + void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) throws IOException; +} \ No newline at end of file diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 4104f924c..51dcf5905 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -20,17 +20,15 @@ package org.apache.fop.render.intermediate; import java.awt.Color; -import java.awt.Point; import java.awt.Rectangle; import java.io.IOException; import org.apache.fop.traits.BorderProps; -import org.apache.fop.traits.RuleStyle; /** * This is an abstract base class for handling border painting. */ -public abstract class BorderPainter { +public class BorderPainter { /** TODO remove before integration*/ public static final String ROUNDED_CORNERS = "fop.round-corners"; @@ -42,6 +40,11 @@ public abstract class BorderPainter { convention index of top-left, top-right, bottom-right and bottom-left border corners*/ protected static final int TOP_LEFT = 0, TOP_RIGHT = 1, BOTTOM_RIGHT = 2, BOTTOM_LEFT = 3; + private final GraphicsPainter graphicsPainter; + + public BorderPainter(GraphicsPainter graphicsPainter) { + this.graphicsPainter = graphicsPainter; + } /** * Draws borders. @@ -65,7 +68,7 @@ public abstract class BorderPainter { } private BorderProps sanitizeBorderProps(BorderProps bps) { - return bps == null ? bps : bps.width == 0 ? (BorderProps)null : bps; + return bps == null ? bps : bps.width == 0 ? (BorderProps) null : bps; } /** @@ -134,11 +137,11 @@ public abstract class BorderPainter { int sx1a = sx1; int ex1a = ex1; - if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsTop)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(sx1a, outery); @@ -165,11 +168,12 @@ public abstract class BorderPainter { moveTo(clipx, sy1); int sy1a = sy1; int ey1a = ey1; - if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + + if (isCollapseOuter(bpsRight)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, sy1a); @@ -196,11 +200,11 @@ public abstract class BorderPainter { moveTo(ex1, clipy); int sx1a = sx1; int ex1a = ex1; - if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { + if (isCollapseOuter(bpsLeft)) { sx1a -= clipw[LEFT]; } - if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsRight)) { ex1a += clipw[RIGHT]; } lineTo(ex1a, outery); @@ -230,11 +234,11 @@ public abstract class BorderPainter { int sy1a = sy1; int ey1a = ey1; - if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) { - if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsLeft)) { + if (isCollapseOuter(bpsTop)) { sy1a -= clipw[TOP]; } - if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) { + if (isCollapseOuter(bpsBottom)) { ey1a += clipw[BOTTOM]; } lineTo(outerx, ey1a); @@ -250,6 +254,10 @@ public abstract class BorderPainter { } } + private boolean isCollapseOuter(BorderProps bp) { + return bp != null && bp.isCollapseOuter(); + } + /** TODO merge with drawRectangularBorders? * @param borderRect the border rectangle * @param bpsBefore the border specification on the before side @@ -259,266 +267,274 @@ public abstract class BorderPainter { * @throws IOException on io exception * */ 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[TOP] && !b[RIGHT] && !b[BOTTOM] && !b[LEFT]) { + BorderProps beforeBorderProps, BorderProps afterBorderProps, + BorderProps startBorderProps, BorderProps endBorderProps) throws IOException { + BorderSegment before = borderSegmentForBefore(beforeBorderProps); + BorderSegment after = borderSegmentForAfter(afterBorderProps); + BorderSegment start = borderSegmentForStart(startBorderProps); + BorderSegment end = borderSegmentForEnd(endBorderProps); + if (before.getWidth() == 0 && after.getWidth() == 0 && start.getWidth() == 0 && end.getWidth() == 0) { return; } - int[] bw = new int[] { - (b[TOP] ? bpsBefore.width : 0), - (b[RIGHT] ? bpsEnd.width : 0), - (b[BOTTOM] ? bpsAfter.width : 0), - (b[LEFT] ? 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[LEFT]; - final int starty = borderRect.y + clipw[TOP]; - final int width = borderRect.width - clipw[LEFT] - clipw[RIGHT]; - final int height = borderRect.height - clipw[TOP] - clipw[BOTTOM]; - - boolean[] slant = new boolean[] { - (b[LEFT] && b[TOP]), (b[TOP] && b[RIGHT]), - (b[RIGHT] && b[BOTTOM]), (b[LEFT] && b[BOTTOM])}; + final int startx = borderRect.x + start.getClippedWidth(); + final int starty = borderRect.y + before.getClippedWidth(); + final int width = borderRect.width - start.getClippedWidth() - end.getClippedWidth(); + final int height = borderRect.height - before.getClippedWidth() - after.getClippedWidth(); //Determine scale factor if any adjacent elliptic corners overlap - double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd); + double cornerCorrectionFactor = calculateCornerScaleCorrection(width, height, before, after, start, end); + drawBorderSegment(start, before, end, 0, width, startx, starty, cornerCorrectionFactor); + drawBorderSegment(before, end, after, 1, height, startx + width, starty, cornerCorrectionFactor); + drawBorderSegment(end, after, start, 2, width, startx + width, starty + height, cornerCorrectionFactor); + drawBorderSegment(after, start, before, 3, height, startx, starty + height, cornerCorrectionFactor); + } - if (bpsBefore != null) { + private void drawBorderSegment(BorderSegment start, BorderSegment before, BorderSegment end, + int orientation, int width, int x, int y, double cornerCorrectionFactor) throws IOException { + if (before.getWidth() != 0) { //Let x increase in the START->END direction - final int sx2 = (slant[TOP_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); + final int sx2 = start.getWidth() - start.getClippedWidth(); final int ex1 = width; - final int ex2 = (slant[TOP_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); - final int outery = -clipw[TOP]; - final int innery = outery + bw[TOP]; - final int clipy = outery + clipw[TOP]; - 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; - + final int ex2 = ex1 - end.getWidth() + end.getClippedWidth(); + final int outery = -before.getClippedWidth(); + final int innery = outery + before.getWidth(); + final int ellipseSBRadiusX = (int) (cornerCorrectionFactor * start.getRadiusEnd()); + final int ellipseSBRadiusY = (int) (cornerCorrectionFactor * before.getRadiusStart()); + final int ellipseBERadiusX = (int) (cornerCorrectionFactor * end.getRadiusStart()); + final int ellipseBERadiusY = (int) (cornerCorrectionFactor * before.getRadiusEnd()); saveGraphicsState(); - translateCoordinates(startx, starty); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[LEFT], clipw[RIGHT], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsBefore, bpsStart, bpsEnd - ); + translateCoordinates(x, y); + if (orientation != 0) { + rotateCoordinates(Math.PI * orientation / 2d); + } + final int ellipseSBX = ellipseSBRadiusX; + final int ellipseSBY = ellipseSBRadiusY; + final int ellipseBEX = ex1 - ellipseBERadiusX; + final int ellipseBEY = ellipseBERadiusY; + int sx1a = 0; + int ex1a = ex1; + if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0) { + final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX, + ellipseSBRadiusY, sx2, innery); + 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 (before.isCollapseOuter()) { + if (start.isCollapseOuter()) { + sx1a -= start.getClippedWidth(); + } + if (end.isCollapseOuter()) { + ex1a += end.getClippedWidth(); + } + lineTo(sx1a, outery); + lineTo(ex1a, outery); + } + } + if (ellipseBERadiusX != 0 && ellipseBERadiusY != 0) { + final double[] outerJoinMetrics = getCornerBorderJoinMetrics( + ellipseBERadiusX, ellipseBERadiusY, ex1 - ex2, innery); + final double beJoinAngle = ex1 == ex2 ? Math.PI / 2 : 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, ex1 - ex2, innery); + 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, sx2, innery); + 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, + before.getStyle(), before.getColor()); + } else { + int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery); + drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true, + before.getStyle(), before.getColor()); + } restoreGraphicsState(); } + } - if (bpsStart != null) { - //Let x increase in the AFTER->BEFORE direction - final int sx2 = (slant[BOTTOM_LEFT] ? bw[BOTTOM] - clipw[BOTTOM] : 0); - final int ex1 = height; - final int ex2 = (slant[TOP_LEFT] ? ex1 - bw[TOP] + clipw[TOP] : ex1); - final int outery = -clipw[LEFT]; - final int innery = outery + bw[LEFT]; - final int clipy = outery + clipw[LEFT]; - 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; + private static BorderSegment borderSegmentForBefore(BorderProps before) { + return AbstractBorderSegment.asBorderSegment(before); + } - saveGraphicsState(); - translateCoordinates(startx, starty + height); - rotateCoordinates(Math.PI * 3d / 2d); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[BOTTOM], clipw[TOP], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsStart, bpsAfter, bpsBefore - ); - restoreGraphicsState(); + private static BorderSegment borderSegmentForAfter(BorderProps after) { + return AbstractBorderSegment.asFlippedBorderSegment(after); + } - } + private static BorderSegment borderSegmentForStart(BorderProps start) { + return AbstractBorderSegment.asFlippedBorderSegment(start); + } + private static BorderSegment borderSegmentForEnd(BorderProps end) { + return AbstractBorderSegment.asBorderSegment(end); + } - if (bpsAfter != null) { - //Let x increase in the START->END direction - final int sx2 = (slant[BOTTOM_LEFT] ? bw[LEFT] - clipw[LEFT] : 0); - final int ex1 = width; - final int ex2 = (slant[BOTTOM_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1); - final int outery = -clipw[BOTTOM]; - final int innery = outery + bw[BOTTOM]; - final int clipy = outery + clipw[BOTTOM]; - 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; + private interface BorderSegment { - saveGraphicsState(); - translateCoordinates(startx, starty + height); - scaleCoordinates(1, -1); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[LEFT], clipw[RIGHT], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsAfter, bpsStart, bpsEnd - ); - restoreGraphicsState(); - } + Color getColor(); - if (bpsEnd != null) { - //Let x increase in the BEFORE-> AFTER direction - final int sx2 = (slant[TOP_RIGHT] ? bw[TOP] - clipw[TOP] : 0); - final int ex1 = height; - final int ex2 = (slant[BOTTOM_RIGHT] ? ex1 - bw[BOTTOM] + clipw[BOTTOM] : ex1); - final int outery = -clipw[RIGHT]; - final int innery = outery + bw[RIGHT]; - final int clipy = outery + clipw[RIGHT]; - 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; + int getStyle(); - saveGraphicsState(); - translateCoordinates(startx + width, starty); - rotateCoordinates(Math.PI / 2d); - drawBorderSegment( sx2, ex1, ex2, outery, innery, - clipw[TOP], clipw[BOTTOM], - ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH, - ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH, - bpsEnd, bpsBefore, bpsAfter - ); - restoreGraphicsState(); - } + int getWidth(); + + int getClippedWidth(); + + int getRadiusStart(); + + int getRadiusEnd(); + + boolean isCollapseOuter(); + + boolean isSpecified(); } - /** TODO collect parameters into useful data structures*/ - 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 { + private abstract static class AbstractBorderSegment implements BorderSegment { - int sx1a = 0; - int ex1a = ex1; + private static BorderSegment asBorderSegment(BorderProps borderProps) { + return borderProps == null ? NullBorderSegment.INSTANCE : new WrappingBorderSegment(borderProps); + } + private static BorderSegment asFlippedBorderSegment(BorderProps borderProps) { + return borderProps == null ? NullBorderSegment.INSTANCE : new FlippedBorderSegment(borderProps); + } - if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0 ) { + public boolean isSpecified() { + return !(this instanceof NullBorderSegment); + } - final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX, - ellipseSBRadiusY, (double)innery / sx2); + private static class WrappingBorderSegment extends AbstractBorderSegment { - final double outerJoinPointX = joinMetrics[0]; - final double outerJoinPointY = joinMetrics[1]; - final double sbJoinAngle = joinMetrics[2]; + protected final BorderProps borderProps; - moveTo((int)outerJoinPointX, (int)outerJoinPointY); - arcTo(Math.PI + sbJoinAngle, Math.PI * 3 / 2, - ellipseSBX, ellipseSBY, ellipseSBRadiusX, ellipseSBRadiusY); - } else { + private final int clippedWidth; - moveTo(0, 0); + WrappingBorderSegment(BorderProps borderProps) { + this.borderProps = borderProps; + clippedWidth = BorderProps.getClippedWidth(borderProps); + } - if (bpsThis.mode == BorderProps.COLLAPSE_OUTER) { + public int getStyle() { + return borderProps.style; + } - if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { - sx1a -= clipWidthStart; - } - if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { - ex1a += clipWidthEnd; - } + public Color getColor() { + return borderProps.color; + } - lineTo(sx1a, outery); - lineTo(ex1a, outery); + public int getWidth() { + return borderProps.width; } - } - if (ellipseBERadiusX != 0 && ellipseBERadiusY != 0) { + public int getClippedWidth() { + return clippedWidth; + } + public boolean isCollapseOuter() { + return borderProps.isCollapseOuter(); + } - final double[] outerJoinMetrics = getCornerBorderJoinMetrics( - ellipseBERadiusX, ellipseBERadiusY, (double)innery / (ex1 - ex2)); - final double beJoinAngle = Math.PI / 2 - outerJoinMetrics[2]; + public int getRadiusStart() { + return borderProps.getRadiusStart(); + } - lineTo(ellipseBEX, 0); - arcTo( Math.PI * 3 / 2 , Math.PI * 3 / 2 + beJoinAngle, - ellipseBEX, ellipseBEY, ellipseBERadiusX, ellipseBERadiusY); + public int getRadiusEnd() { + return borderProps.getRadiusEnd(); + } + } - if (ellipseBEX < ex2 && ellipseBEY > innery) { + private static class FlippedBorderSegment extends WrappingBorderSegment { - 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]; + FlippedBorderSegment(BorderProps borderProps) { + super(borderProps); + } - 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); + public int getRadiusStart() { + return borderProps.getRadiusEnd(); } - } else { - lineTo(ex1, 0); - lineTo(ex2, innery); + public int getRadiusEnd() { + return borderProps.getRadiusStart(); + } } - if (ellipseSBRadiusX == 0) { - lineTo(sx2, innery); - } else { - if (ellipseSBX > sx2 && ellipseSBY > innery) { + private static final class NullBorderSegment extends AbstractBorderSegment { + public static final NullBorderSegment INSTANCE = new NullBorderSegment(); - final double[] innerJoinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX - sx2, - ellipseSBRadiusY - innery, (double)innery / sx2); + private NullBorderSegment() { + } - final double sbInnerJoinAngle = innerJoinMetrics[2]; + public int getWidth() { + return 0; + } - lineTo(ellipseSBX, innery); - arcTo(Math.PI * 3 / 2, sbInnerJoinAngle + Math.PI, - ellipseSBX, ellipseSBY, ellipseSBX - sx2, ellipseSBY - innery); - } else { - lineTo(sx2, innery); + public int getClippedWidth() { + return 0; } - } - closePath(); - clip(); + public int getRadiusStart() { + return 0; + } - if (ellipseBERadiusY == 0 && ellipseSBRadiusY == 0) { - drawBorderLine(sx1a, outery, ex1a, innery, true, true, - bpsThis.style, bpsThis.color); + public int getRadiusEnd() { + return 0; + } + + public boolean isCollapseOuter() { + return false; + } + + public Color getColor() { + throw new UnsupportedOperationException(); + } + + public int getStyle() { + throw new UnsupportedOperationException(); + } + + public boolean isSpecified() { + return false; + } + } + } + private double[] getCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY, double xWidth, + double yWidth) { + if (xWidth > 0) { + return getCornerBorderJoinMetrics(ellipseCenterX, ellipseCenterY, yWidth / xWidth); } else { - int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery); - drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true, - bpsThis.style, bpsThis.color); + return new double[]{0, ellipseCenterY, 0}; } } @@ -527,8 +543,8 @@ public abstract class BorderPainter { double x = ellipseCenterY * ellipseCenterX * ( ellipseCenterY + ellipseCenterX * borderWidthRatio - Math.sqrt(2d * ellipseCenterX * ellipseCenterY * borderWidthRatio) - ) / (ellipseCenterY * ellipseCenterY - + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio); + ) / (ellipseCenterY * ellipseCenterY + + ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio); double y = borderWidthRatio * x; return new double[]{x, y, Math.atan((ellipseCenterY - y) / (ellipseCenterX - x))}; } @@ -545,331 +561,335 @@ public abstract class BorderPainter { public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IOException { + BorderSegment before = borderSegmentForBefore(bpsBefore); + BorderSegment after = borderSegmentForAfter(bpsAfter); + BorderSegment start = borderSegmentForStart(bpsStart); + BorderSegment end = borderSegmentForEnd(bpsEnd); int startx = rect.x; int starty = rect.y; int width = rect.width; int height = rect.height; + double correctionFactor = calculateCornerCorrectionFactor(width + start.getWidth() + end.getWidth(), + height + before.getWidth() + after.getWidth(), bpsBefore, bpsAfter, bpsStart, bpsEnd); + Corner cornerBeforeEnd = Corner.createBeforeEndCorner(before, end, correctionFactor); + Corner cornerEndAfter = Corner.createEndAfterCorner(end, after, correctionFactor); + Corner cornerAfterStart = Corner.createAfterStartCorner(after, start, correctionFactor); + Corner cornerStartBefore = Corner.createStartBeforeCorner(start, before, correctionFactor); + new PathPainter(startx + cornerStartBefore.radiusX, starty) + .lineHorizTo(width - cornerStartBefore.radiusX - cornerBeforeEnd.radiusX) + .drawCorner(cornerBeforeEnd) + .lineVertTo(height - cornerBeforeEnd.radiusY - cornerEndAfter.radiusY) + .drawCorner(cornerEndAfter) + .lineHorizTo(cornerEndAfter.radiusX + cornerAfterStart.radiusX - width) + .drawCorner(cornerAfterStart) + .lineVertTo(cornerAfterStart.radiusY + cornerStartBefore.radiusY - height) + .drawCorner(cornerStartBefore); + clip(); + } - 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); + /** + * The four corners + * SB - Start-Before + * BE - Before-End + * EA - End-After + * AS - After-Start + * + * 0 --> x + * | + * v + * y + * + * SB BE + * *----* + * | | + * | | + * *----* + * AS EA + * + */ + private enum CornerAngles { + /** The before-end angles */ + BEFORE_END(Math.PI * 3 / 2, 0), + /** The end-after angles */ + END_AFTER(0, Math.PI / 2), + /** The after-start angles*/ + AFTER_START(Math.PI / 2, Math.PI), + /** The start-before angles */ + START_BEFORE(Math.PI, Math.PI * 3 / 2); + + /** Angle of the start of the corner arch relative to the x-axis in the counter-clockwise direction */ + private final double start; + + /** Angle of the end of the corner arch relative to the x-axis in the counter-clockwise direction */ + private final double end; + + CornerAngles(double start, double end) { + this.start = start; + this.end = end; } - 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); - } + private static final class Corner { - 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); - } + private static final Corner SQUARE = new Corner(0, 0, null, 0, 0, 0, 0); - // Draw clipping region in the order: Before->End->After->Start - moveTo(startx + ellipseSS, starty); + /** The radius of the elliptic corner in the x direction */ + protected final int radiusX; - lineTo(startx + width - ellipseES, starty); + /** The radius of the elliptic corner in the y direction */ + protected final int radiusY; - if (ellipseBE > 0 && ellipseES > 0) { - arcTo(Math.PI * 3 / 2, Math.PI * 2, - startx + width - ellipseES, starty + ellipseBE, ellipseES, ellipseBE); - } + /** The start and end angles of the corner ellipse */ + private final CornerAngles angles; - lineTo(startx + width, starty + height - ellipseAE); + /** The offset in the x direction of the center of the ellipse relative to the starting point */ + private final int centerX; - if (ellipseEE > 0 && ellipseAE > 0) { - arcTo(0, Math.PI / 2, startx + width - ellipseEE, - starty + height - ellipseAE, ellipseEE, ellipseAE); - } + /** The offset in the y direction of the center of the ellipse relative to the starting point */ + private final int centerY; - lineTo(startx + ellipseSE, starty + height); + /** The value in the x direction that the corner extends relative to the starting point */ + private final int incrementX; - if (ellipseSE > 0 && ellipseAS > 0) { - arcTo( Math.PI / 2, Math.PI, startx + ellipseSE, - starty + height - ellipseAS, ellipseSE, ellipseAS); - } + /** The value in the y direction that the corner extends relative to the starting point */ + private final int incrementY; - lineTo( startx, starty + ellipseBS); + private Corner(int radiusX, int radiusY, CornerAngles angles, int ellipseOffsetX, + int ellipseOffsetY, int incrementX, int incrementY) { + this.radiusX = radiusX; + this.radiusY = radiusY; + this.angles = angles; + this.centerX = ellipseOffsetX; + this.centerY = ellipseOffsetY; + this.incrementX = incrementX; + this.incrementY = incrementY; + } - if (ellipseSS > 0 && ellipseBS > 0) { - arcTo( Math.PI, Math.PI * 3 / 2, - startx + ellipseSS, starty + ellipseBS, ellipseSS, ellipseBS); + private static int extentFromRadiusStart(BorderSegment border, double correctionFactor) { + return extentFromRadius(border.getRadiusStart(), border, correctionFactor); } - clip(); + private static int extentFromRadiusEnd(BorderSegment border, double correctionFactor) { + return extentFromRadius(border.getRadiusEnd(), border, correctionFactor); + } - } + private static int extentFromRadius(int radius, BorderSegment border, double correctionFactor) { + return Math.max((int) (radius * correctionFactor) - border.getWidth(), 0); + } - /** - * TODO javadocs - * If an ellipse radii exceed the border edge length then all ellipses must be 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) { - double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusStart()) - + (bpsEnd == null ? 0 : bpsEnd.getRadiusStart()); - - if (ellipseExtent > 0) { - double f = width / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createBeforeEndCorner(BorderSegment before, BorderSegment end, + double correctionFactor) { + int width = end.getRadiusStart(); + int height = before.getRadiusEnd(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusStart(end, correctionFactor); + int y = extentFromRadiusEnd(before, correctionFactor); + return new Corner(x, y, CornerAngles.BEFORE_END, 0, y, x, y); } - if (bpsStart != null) { - double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusStart()) - + (bpsBefore == null ? 0 : bpsBefore.getRadiusStart()); - - if (ellipseExtent > 0) { - double f = height / ellipseExtent; - if ( f < esf) { - esf = f; - } + public static Corner createEndAfterCorner(BorderSegment end, BorderSegment after, + double correctionFactor) { + int width = end.getRadiusEnd(); + int height = after.getRadiusStart(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusEnd(end, correctionFactor); + int y = extentFromRadiusStart(after, correctionFactor); + return new Corner(x, y, CornerAngles.END_AFTER, -x, 0, -x, y); } - if (bpsAfter != null) { - double ellipseExtent = (bpsStart == null ? 0 : bpsStart.getRadiusEnd()) - + (bpsEnd == null ? 0 : bpsEnd.getRadiusEnd()); - - if (ellipseExtent > 0) { - double f = width / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createAfterStartCorner(BorderSegment after, BorderSegment start, + double correctionFactor) { + int width = start.getRadiusStart(); + int height = after.getRadiusEnd(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusStart(start, correctionFactor); + int y = extentFromRadiusEnd(after, correctionFactor); + return new Corner(x, y, CornerAngles.AFTER_START, 0, -y, -x, -y); } - if (bpsEnd != null) { - double ellipseExtent = (bpsAfter == null ? 0 : bpsAfter.getRadiusEnd()) - + (bpsBefore == null ? 0 : bpsBefore.getRadiusEnd()); - - if (ellipseExtent > 0) { - double f = height / ellipseExtent; - if (f < esf) { - esf = f; - } + public static Corner createStartBeforeCorner(BorderSegment start, BorderSegment before, + double correctionFactor) { + int width = start.getRadiusEnd(); + int height = before.getRadiusStart(); + if (width == 0 || height == 0) { + return SQUARE; } + int x = extentFromRadiusEnd(start, correctionFactor); + int y = extentFromRadiusStart(before, correctionFactor); + return new Corner(x, y, CornerAngles.START_BEFORE, x, 0, x, -y); } - - return esf; } /** - * Draws a border line. - * @param x1 X coordinate of the upper left corner - * of the line's bounding rectangle (in millipoints) - * @param y1 start Y coordinate of the upper left corner - * of the line's bounding rectangle (in millipoints) - * @param x2 end X coordinate of the lower right corner - * of the line's bounding rectangle (in millipoints) - * @param y2 end y coordinate of the lower right corner - * of the line's bounding rectangle (in millipoints) - * @param horz true if it is a horizontal line - * @param startOrBefore true if the line is the start or end edge of a border box - * @param style the border style - * @param color the border color - * @throws IOException if an I/O error occurs + * This is a helper class for constructing curves composed of move, line and arc operations. Coordinates + * are relative to the terminal point of the previous operation */ - protected abstract void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, - boolean horz, boolean startOrBefore, int style, Color color) throws IOException; + private final class PathPainter { - /** - * Draws a line/rule. - * @param start start point (coordinates in millipoints) - * @param end end point (coordinates in millipoints) - * @param width width of the line - * @param color the line color - * @param style the rule style - * @throws IOException if an I/O error occurs - */ - public abstract void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) throws IOException; + /** Current x position */ + private int x; - /** - * Moves the cursor to the given coordinate. - * @param x the X coordinate (in millipoints) - * @param y the Y coordinate (in millipoints) - * @throws IOException if an I/O error occurs - */ - protected abstract void moveTo(int x, int y) throws IOException; + /** Current y position */ + private int y; - /** - * Draws a line from the current cursor position to the given coordinates. - * @param x the X coordinate (in millipoints) - * @param y the Y coordinate (in millipoints) - * @throws IOException if an I/O error occurs - */ - protected abstract void lineTo(int x, int y) throws IOException; + PathPainter(int x, int y) throws IOException { + moveTo(x, y); + } - /** - * 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; + private void moveTo(int x, int y) throws IOException { + this.x += x; + this.y += y; + BorderPainter.this.moveTo(this.x, this.y); + } - /** - * 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 { + public PathPainter lineTo(int x, int y) throws IOException { + this.x += x; + this.y += y; + BorderPainter.this.lineTo(this.x, this.y); + return this; + } - // 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 + public PathPainter lineHorizTo(int x) throws IOException { + return lineTo(x, 0); + } - // 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); + public PathPainter lineVertTo(int y) throws IOException { + return lineTo(0, y); + } - final double sinStart = Math.sin(etaStart); - final double cosStart = Math.cos(etaStart); - final double sinEnd = Math.sin(etaEnd); - final double cosEnd = Math.cos(etaEnd); + PathPainter drawCorner(Corner corner) throws IOException { + if (corner.radiusX == 0 && corner.radiusY == 0) { + return this; + } + if (corner.radiusX == 0 || corner.radiusY == 0) { + x += corner.incrementX; + y += corner.incrementY; + BorderPainter.this.lineTo(x, y); + return this; + } + BorderPainter.this.arcTo(corner.angles.start, corner.angles.end, x + corner.centerX, + y + corner.centerY, corner.radiusX, corner.radiusY); + x += corner.incrementX; + y += corner.incrementY; + return this; + } + } - final double p0x = cx + cosStart * width; - final double p0y = cy + sinStart * height; - final double p3x = cx + cosEnd * width; - final double p3y = cy + sinEnd * height; + /** + * Calculate the correction factor to handle over-sized elliptic corner radii. + * + * @param width the border width + * @param height the border height + * @param before the before border properties + * @param after the after border properties + * @param start the start border properties + * @param end the end border properties + * + */ + protected static double calculateCornerCorrectionFactor(int width, int height, BorderProps before, + BorderProps after, BorderProps start, BorderProps end) { + return calculateCornerScaleCorrection(width, height, borderSegmentForBefore(before), + borderSegmentForAfter(after), borderSegmentForStart(start), borderSegmentForEnd(end)); + } + /** + * Calculate the scaling factor to handle over-sized elliptic corner radii. + * + * @param width the border width + * @param height the border height + * @param before the before border segment + * @param after the after border segment + * @param start the start border segment + * @param end the end border segment + */ + protected static double calculateCornerScaleCorrection(int width, int height, BorderSegment before, + BorderSegment after, BorderSegment start, BorderSegment end) { + return CornerScaleCorrectionCalculator.calculate(width, height, before, after, start, end); + } - 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; + private static final class CornerScaleCorrectionCalculator { + private double correctionFactor = 1; - int order = etaEnd > etaStart ? 1 : -1; + private CornerScaleCorrectionCalculator(int width, int height, + BorderSegment before, BorderSegment after, + BorderSegment start, BorderSegment end) { + calculateForSegment(width, start, before, end); + calculateForSegment(height, before, end, after); + calculateForSegment(width, end, after, start); + calculateForSegment(height, after, start, before); + } - // p1 = p0 + alpha*(-sin(startAngle), cos(startAngle)) - final double p1x = p0x - alpha * sinStart * width * order; - final double p1y = p0y + alpha * cosStart * height * order; + public static double calculate(int width, int height, + BorderSegment before, BorderSegment after, + BorderSegment start, BorderSegment end) { + return new CornerScaleCorrectionCalculator(width, height, before, after, start, end) + .correctionFactor; + } - // p1 = p3 + alpha*(sin(endAngle), -cos(endAngle)) - final double p2x = p3x + alpha * sinEnd * width * order; - final double p2y = p3y - alpha * cosEnd * height * order; + private void calculateForSegment(int width, BorderSegment bpsStart, BorderSegment bpsBefore, + BorderSegment bpsEnd) { + if (bpsBefore.isSpecified()) { + double ellipseExtent = bpsStart.getRadiusEnd() + bpsEnd.getRadiusStart(); + if (ellipseExtent > 0) { + double thisCorrectionFactor = width / ellipseExtent; + if (thisCorrectionFactor < correctionFactor) { + correctionFactor = thisCorrectionFactor; + } + } + } + } + } - //Draw the curve in original coordinate system - cubicBezierTo((int)p1x, (int)p1y, (int)p2x, (int)p2y, (int)p3x, (int)p3y); + private void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, boolean startOrBefore, + int style, Color color) throws IOException { + graphicsPainter.drawBorderLine(x1, y1, x2, y2, horz, startOrBefore, style, color); } - 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; - } - } + private void moveTo(int x, int y) throws IOException { + graphicsPainter.moveTo(x, y); } - /** - * 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; + private void lineTo(int x, int y) throws IOException { + graphicsPainter.lineTo(x, y); + } - /** - * 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; + private void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + graphicsPainter.arcTo(startAngle, endAngle, cx, cy, width, height); + } - /** - * 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; + private void rotateCoordinates(double angle) throws IOException { + graphicsPainter.rotateCoordinates(angle); + } + private void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + graphicsPainter.translateCoordinates(xTranslate, yTranslate); + } - /** - * Closes the current path. - * @throws IOException if an I/O error occurs - */ - protected abstract void closePath() throws IOException; + private void closePath() throws IOException { + graphicsPainter.closePath(); + } - /** - * Reduces the current clipping region to the current path. - * @throws IOException if an I/O error occurs - */ - protected abstract void clip() throws IOException; + private void clip() throws IOException { + graphicsPainter.clip(); + } - /** - * Save the graphics state on the stack. - * @throws IOException if an I/O error occurs - */ - protected abstract void saveGraphicsState() throws IOException; + private void saveGraphicsState() throws IOException { + graphicsPainter.saveGraphicsState(); + } - /** - * Restore the last graphics state from the stack. - * @throws IOException if an I/O error occurs - */ - protected abstract void restoreGraphicsState() throws IOException; + private void restoreGraphicsState() throws IOException { + graphicsPainter.restoreGraphicsState(); + } } diff --git a/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java b/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java new file mode 100644 index 000000000..369cacd43 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/GraphicsPainter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.intermediate; + +import java.awt.Color; +import java.awt.Point; +import java.io.IOException; + +import org.apache.fop.traits.RuleStyle; + +/** + * Used primarily by {@link BorderPainter}, implementations are created for rendering + * primitive graphical operations. + * + */ +public interface GraphicsPainter { + + /** + * Draws a border line. + * @param x1 X coordinate of the upper left corner + * of the line's bounding rectangle (in millipoints) + * @param y1 start Y coordinate of the upper left corner + * of the line's bounding rectangle (in millipoints) + * @param x2 end X coordinate of the lower right corner + * of the line's bounding rectangle (in millipoints) + * @param y2 end y coordinate of the lower right corner + * of the line's bounding rectangle (in millipoints) + * @param horz true if it is a horizontal line + * @param startOrBefore true if the line is the start or end edge of a border box + * @param style the border style + * @param color the border color + * @throws IOException if an I/O error occurs + */ + void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) throws IOException; + + /** + * Draws a line/rule. + * @param start start point (coordinates in millipoints) + * @param end end point (coordinates in millipoints) + * @param width width of the line + * @param color the line color + * @param style the rule style + * @throws IOException if an I/O error occurs + */ + void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) throws IOException; + + /** + * Moves the cursor to the given coordinate. + * @param x the X coordinate (in millipoints) + * @param y the Y coordinate (in millipoints) + * @throws IOException if an I/O error occurs + */ + void moveTo(int x, int y) throws IOException; + + /** + * Draws a line from the current cursor position to the given coordinates. + * @param x the X coordinate (in millipoints) + * @param y the Y coordinate (in millipoints) + * @throws IOException if an I/O error occurs + */ + void lineTo(int x, int y) 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 + */ + void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException; + + /** + * Rotate the coordinate frame + * @param angle angle in radians to rotate the coordinate frame + * @throws IOException if an I/O error occurs + */ + 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 + */ + 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 + */ + void scaleCoordinates(float xScale, float yScale) throws IOException; + + /** + * Closes the current path. + * @throws IOException if an I/O error occurs + */ + void closePath() throws IOException; + + /** + * Reduces the current clipping region to the current path. + * @throws IOException if an I/O error occurs + */ + void clip() throws IOException; + + /** + * Save the graphics state on the stack. + * @throws IOException if an I/O error occurs + */ + void saveGraphicsState() throws IOException; + + /** + * Restore the last graphics state from the stack. + * @throws IOException if an I/O error occurs + */ + void restoreGraphicsState() throws IOException; +} diff --git a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java b/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java deleted file mode 100644 index b3ad5ff7a..000000000 --- a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.java2d; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -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; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * Java2D-specific implementation of the {@link BorderPainter}. - */ -public class Java2DBorderPainter extends BorderPainter { - - /** logging instance */ - private static Log log = LogFactory.getLog(Java2DBorderPainter.class); - - private Java2DPainter painter; - - private GeneralPath currentPath = null; - - /** - * Construct a java2d border painter. - * @param painter a painter - */ - public Java2DBorderPainter(Java2DPainter painter) { - this.painter = painter; - } - - private Java2DGraphicsState getG2DState() { - return this.painter.g2dState; - } - - private Graphics2D getG2D() { - return getG2DState().getGraph(); - } - - /** {@inheritDoc} */ - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color color) { - float w = x2 - x1; - float h = y2 - y1; - if ((w < 0) || (h < 0)) { - log.error("Negative extent received. Border won't be painted."); - return; - } - switch (style) { - case Constants.EN_DASHED: - getG2D().setColor(color); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - float ym = y1 + (h / 2); - BasicStroke s = new BasicStroke(h, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - float xm = x1 + (w / 2); - BasicStroke s = new BasicStroke(w, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - break; - case Constants.EN_DOTTED: - getG2D().setColor(color); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - float ym = y1 + (h / 2); - BasicStroke s = new BasicStroke(h, BasicStroke.CAP_ROUND, - BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - float xm = x1 + (w / 2); - BasicStroke s = new BasicStroke(w, BasicStroke.CAP_ROUND, - BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - break; - case Constants.EN_DOUBLE: - getG2D().setColor(color); - if (horz) { - float h3 = h / 3; - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - BasicStroke s = new BasicStroke(h3); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - getG2D().draw(new Line2D.Float(x1, ym2, x2, ym2)); - } else { - float w3 = w / 3; - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - BasicStroke s = new BasicStroke(w3); - getG2D().setStroke(s); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - getG2D().draw(new Line2D.Float(xm2, y1, xm2, y2)); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - if (horz) { - Color uppercol = ColorUtil.lightenColor(color, -colFactor); - Color lowercol = ColorUtil.lightenColor(color, colFactor); - float h3 = h / 3; - float ym1 = y1 + (h3 / 2); - getG2D().setStroke(new BasicStroke(h3)); - getG2D().setColor(uppercol); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(x1, ym1 + h3, x2, ym1 + h3)); - getG2D().setColor(lowercol); - getG2D().draw(new Line2D.Float(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3)); - } else { - Color leftcol = ColorUtil.lightenColor(color, -colFactor); - Color rightcol = ColorUtil.lightenColor(color, colFactor); - float w3 = w / 3; - float xm1 = x1 + (w3 / 2); - getG2D().setStroke(new BasicStroke(w3)); - getG2D().setColor(leftcol); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(xm1 + w3, y1, xm1 + w3, y2)); - getG2D().setColor(rightcol); - getG2D().draw(new Line2D.Float(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2)); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - if (horz) { - color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); - getG2D().setStroke(new BasicStroke(h)); - float ym1 = y1 + (h / 2); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); - } else { - color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); - float xm1 = x1 + (w / 2); - getG2D().setStroke(new BasicStroke(w)); - getG2D().setColor(color); - getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); - } - break; - case Constants.EN_HIDDEN: - break; - default: - getG2D().setColor(color); - if (horz) { - float ym = y1 + (h / 2); - getG2D().setStroke(new BasicStroke(h)); - getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); - } else { - float xm = x1 + (w / 2); - getG2D().setStroke(new BasicStroke(w)); - getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - getG2DState().updateClip(boundingRect); - - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - int shift = half; //This shifts the dots to the right by half a dot's width - drawBorderLine(start.x + shift, start.y - half, end.x + shift, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - getG2DState().updateColor(ColorUtil.lightenColor(color, 0.6f)); - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - closePath(); - getG2D().fill(currentPath); - currentPath = null; - getG2DState().updateColor(color); - if (style.getEnumValue() == Constants.EN_GROOVE) { - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + half); - lineTo(start.x + half, starty + half); - lineTo(start.x, starty + 2 * half); - } else { - moveTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - lineTo(start.x, starty + half); - lineTo(end.x - half, starty + half); - } - closePath(); - getG2D().fill(currentPath); - currentPath = null; - - case Constants.EN_NONE: - // No rule is drawn - break; - default: - } // end switch - restoreGraphicsState(); - } - - /** {@inheritDoc} */ - protected void clip() { - if (currentPath == null) { - throw new IllegalStateException("No current path available!"); - } - getG2DState().updateClip(currentPath); - currentPath = null; - } - - /** {@inheritDoc} */ - protected void closePath() { - currentPath.closePath(); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.lineTo(x, y); - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) { - if (currentPath == null) { - currentPath = new GeneralPath(); - } - currentPath.moveTo(x, y); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() { - this.painter.saveGraphicsState(); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() { - this.painter.restoreGraphicsState(); - 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 - - } - -} diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java new file mode 100644 index 000000000..b485daf3d --- /dev/null +++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.java2d; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +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; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +class Java2DGraphicsPainter implements GraphicsPainter { + + /** logging instance */ + static final Log log = LogFactory.getLog(Java2DGraphicsPainter.class); + + + private GeneralPath currentPath; + + private final Java2DPainter painter; + + + Java2DGraphicsPainter(Java2DPainter painter) { + this.painter = painter; + } + + private Java2DGraphicsState getG2DState() { + return this.painter.g2dState; + } + + + private Graphics2D getG2D() { + return getG2DState().getGraph(); + } + + public void drawBorderLine(int x1, int y1, int x2, int y2, + boolean horz, boolean startOrBefore, int style, Color color) + throws IOException { + float w = x2 - x1; + float h = y2 - y1; + if ((w < 0) || (h < 0)) { + log.error("Negative extent received. Border won't be painted."); + return; + } + switch (style) { + case Constants.EN_DASHED: + getG2D().setColor(color); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOTTED: + getG2D().setColor(color); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOUBLE: + getG2D().setColor(color); + if (horz) { + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + BasicStroke s = new BasicStroke(h3); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + getG2D().draw(new Line2D.Float(x1, ym2, x2, ym2)); + } else { + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + BasicStroke s = new BasicStroke(w3); + getG2D().setStroke(s); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + getG2D().draw(new Line2D.Float(xm2, y1, xm2, y2)); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + if (horz) { + Color uppercol = ColorUtil.lightenColor(color, -colFactor); + Color lowercol = ColorUtil.lightenColor(color, colFactor); + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + getG2D().setStroke(new BasicStroke(h3)); + getG2D().setColor(uppercol); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(x1, ym1 + h3, x2, ym1 + h3)); + getG2D().setColor(lowercol); + getG2D().draw(new Line2D.Float(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3)); + } else { + Color leftcol = ColorUtil.lightenColor(color, -colFactor); + Color rightcol = ColorUtil.lightenColor(color, colFactor); + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + getG2D().setStroke(new BasicStroke(w3)); + getG2D().setColor(leftcol); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(xm1 + w3, y1, xm1 + w3, y2)); + getG2D().setColor(rightcol); + getG2D().draw(new Line2D.Float(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2)); + } + break; + case Constants.EN_INSET: + case Constants.EN_OUTSET: + colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + if (horz) { + color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); + getG2D().setStroke(new BasicStroke(h)); + float ym1 = y1 + (h / 2); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(x1, ym1, x2, ym1)); + } else { + color = ColorUtil.lightenColor(color, (startOrBefore ? 1 : -1) * colFactor); + float xm1 = x1 + (w / 2); + getG2D().setStroke(new BasicStroke(w)); + getG2D().setColor(color); + getG2D().draw(new Line2D.Float(xm1, y1, xm1, y2)); + } + break; + case Constants.EN_HIDDEN: + break; + default: + getG2D().setColor(color); + if (horz) { + float ym = y1 + (h / 2); + getG2D().setStroke(new BasicStroke(h)); + getG2D().draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float xm = x1 + (w / 2); + getG2D().setStroke(new BasicStroke(w)); + getG2D().draw(new Line2D.Float(xm, y1, xm, y2)); + } + } + } + + public void drawLine(Point start, Point end, int width, Color color, + RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + getG2DState().updateClip(boundingRect); + + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, start.y - half, end.x, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + int shift = half; //This shifts the dots to the right by half a dot's width + drawBorderLine(start.x + shift, start.y - half, end.x + shift, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + getG2DState().updateColor(ColorUtil.lightenColor(color, 0.6f)); + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + closePath(); + getG2D().fill(currentPath); + currentPath = null; + getG2DState().updateColor(color); + if (style.getEnumValue() == Constants.EN_GROOVE) { + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + half); + lineTo(start.x + half, starty + half); + lineTo(start.x, starty + 2 * half); + } else { + moveTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + lineTo(start.x, starty + half); + lineTo(end.x - half, starty + half); + } + closePath(); + getG2D().fill(currentPath); + currentPath = null; + + case Constants.EN_NONE: + // No rule is drawn + break; + default: + } // end switch + restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) throws IOException { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.moveTo(x, y); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) throws IOException { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.lineTo(x, y); + } + + /** {@inheritDoc} */ + public void arcTo(double startAngle, double endAngle, int cx, int cy, + int width, int height) throws IOException { + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) + throws IOException { + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) + throws IOException { + } + + /** {@inheritDoc} */ + public void closePath() throws IOException { + currentPath.closePath(); + } + + /** {@inheritDoc} */ + public void clip() throws IOException { + if (currentPath == null) { + throw new IllegalStateException("No current path available!"); + } + getG2DState().updateClip(currentPath); + currentPath = null; + } + + /** {@inheritDoc} */ + public void saveGraphicsState() throws IOException { + this.painter.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() throws IOException { + this.painter.restoreGraphicsState(); + this.currentPath = null; + } + +} \ No newline at end of file diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 7b8a96b99..e34fb4bbb 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -38,6 +38,8 @@ import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; @@ -58,7 +60,9 @@ public class Java2DPainter extends AbstractIFPainter { /** The font information */ protected FontInfo fontInfo; - private Java2DBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private final BorderPainter borderPainter; /** The current state, holds a Graphics2D and its context */ protected Java2DGraphicsState g2dState; @@ -92,7 +96,8 @@ public class Java2DPainter extends AbstractIFPainter { } this.fontInfo = fontInfo; this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform()); - this.borderPainter = new Java2DBorderPainter(this); + graphicsPainter = new Java2DGraphicsPainter(this); + this.borderPainter = new BorderPainter(graphicsPainter); } /** {@inheritDoc} */ @@ -202,7 +207,11 @@ public class Java2DPainter extends AbstractIFPainter { /** {@inheritDoc} */ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { - this.borderPainter.drawLine(start, end, width, color, style); + try { + this.graphicsPainter.drawLine(start, end, width, color, style); + } catch (IOException ioe) { + throw new IFException("Unexpected error drawing line", ioe); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java b/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java deleted file mode 100644 index 4bfba9df3..000000000 --- a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.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; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * PDF-specific implementation of the {@link BorderPainter}. - */ -public class PDFBorderPainter extends BorderPainter { - - /** logging instance */ - private static final Log LOG = LogFactory.getLog(PDFBorderPainter.class); - - private PDFContentGenerator generator; - - /** - * Construct a border painter. - * @param generator a pdf content generator - */ - public PDFBorderPainter(PDFContentGenerator generator) { - this.generator = generator; - } - - /** {@inheritDoc} */ - 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); - } - - /** - * @param generator pdf content generator - * @see BorderPainter#drawBorderLine - */ - public static void drawBorderLine( // CSOK: ParameterNumber|MethodLength - PDFContentGenerator generator, - float x1, float y1, float x2, float y2, boolean horz, // CSOK: JavadocMethod - boolean startOrBefore, int style, Color col) { // CSOK: JavadocMethod - 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); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - generator.add("[" + format(unit) + "] 0 d "); - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - generator.add("[" + format(unit) + "] 0 d "); - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_DOTTED: - generator.setColor(col, false); - generator.add("1 J "); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - generator.add("[0 " + format(unit) + "] 0 d "); - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - generator.add("[0 " + format(unit) + " ] 0 d "); - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_DOUBLE: - generator.setColor(col, false); - generator.add("[] 0 d "); - if (horz) { - float h3 = h / 3; - generator.add(format(h3) + " w\n"); - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - generator.add(format(x1) + " " + format(ym2) + " m " - + format(x2) + " " + format(ym2) + " l S\n"); - } else { - float w3 = w / 3; - generator.add(format(w3) + " w\n"); - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - generator.add(format(xm2) + " " + format(y1) + " m " - + format(xm2) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - generator.add("[] 0 d "); - if (horz) { - Color uppercol = ColorUtil.lightenColor(col, -colFactor); - Color lowercol = ColorUtil.lightenColor(col, colFactor); - float h3 = h / 3; - generator.add(format(h3) + " w\n"); - float ym1 = y1 + (h3 / 2); - generator.setColor(uppercol, false); - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - generator.setColor(col, false); - generator.add(format(x1) + " " + format(ym1 + h3) + " m " - + format(x2) + " " + format(ym1 + h3) + " l S\n"); - generator.setColor(lowercol, false); - generator.add(format(x1) + " " + format(ym1 + h3 + h3) + " m " - + format(x2) + " " + format(ym1 + h3 + h3) + " l S\n"); - } else { - Color leftcol = ColorUtil.lightenColor(col, -colFactor); - Color rightcol = ColorUtil.lightenColor(col, colFactor); - float w3 = w / 3; - generator.add(format(w3) + " w\n"); - float xm1 = x1 + (w3 / 2); - generator.setColor(leftcol, false); - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - generator.setColor(col, false); - generator.add(format(xm1 + w3) + " " + format(y1) + " m " - + format(xm1 + w3) + " " + format(y2) + " l S\n"); - generator.setColor(rightcol, false); - generator.add(format(xm1 + w3 + w3) + " " + format(y1) + " m " - + format(xm1 + w3 + w3) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - generator.add("[] 0 d "); - Color c = col; - if (horz) { - c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - generator.add(format(h) + " w\n"); - float ym1 = y1 + (h / 2); - generator.setColor(c, false); - generator.add(format(x1) + " " + format(ym1) + " m " - + format(x2) + " " + format(ym1) + " l S\n"); - } else { - c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - generator.add(format(w) + " w\n"); - float xm1 = x1 + (w / 2); - generator.setColor(c, false); - generator.add(format(xm1) + " " + format(y1) + " m " - + format(xm1) + " " + format(y2) + " l S\n"); - } - break; - case Constants.EN_HIDDEN: - break; - default: - generator.setColor(col, false); - generator.add("[] 0 d "); - if (horz) { - generator.add(format(h) + " w\n"); - float ym = y1 + (h / 2); - generator.add(format(x1) + " " + format(ym) + " m " - + format(x2) + " " + format(ym) + " l S\n"); - } else { - generator.add(format(w) + " w\n"); - float xm = x1 + (w / 2); - generator.add(format(xm) + " " + format(y1) + " m " - + format(xm) + " " + format(y2) + " l S\n"); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - generator.clipRect(boundingRect); - //This displaces the dots to the right by half a dot's width - //TODO There's room for improvement here - generator.add("1 0 0 1 " + format(half) + " 0 cm\n"); - drawBorderLine(start.x, start.y - half, end.x, end.y + half, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - generator.setColor(ColorUtil.lightenColor(color, 0.6f), true); - generator.add(format(start.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty) + " l\n"); - generator.add(format(end.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add("h\n"); - generator.add("f\n"); - generator.setColor(color, true); - if (style == RuleStyle.GROOVE) { - generator.add(format(start.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty) + " l\n"); - generator.add(format(end.x) + " " + format(starty + half) + " l\n"); - generator.add(format(start.x + half) + " " + format(starty + half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - } else { - generator.add(format(end.x) + " " + format(starty) + " m\n"); - generator.add(format(end.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + 2 * half) + " l\n"); - generator.add(format(start.x) + " " + format(starty + half) + " l\n"); - generator.add(format(end.x - half) + " " + format(starty + half) + " l\n"); - } - generator.add("h\n"); - generator.add("f\n"); - break; - default: - throw new UnsupportedOperationException("rule style not supported"); - } - restoreGraphicsState(); - } - - static final String format(int coordinate) { - //TODO lose scale? - return format(coordinate / 1000f); - } - - static final String format(float coordinate) { - return PDFContentGenerator.format(coordinate); - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) { - generator.add(format(x) + " " + format(y) + " m "); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) { - generator.add(format(x) + " " + format(y) + " l "); - } - - /** {@inheritDoc} */ - protected void closePath() { - generator.add("h "); - } - - /** {@inheritDoc} */ - protected void clip() { - generator.add("W\n" + "n\n"); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() { - generator.add("q\n"); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() { - 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); - } - -} diff --git a/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java new file mode 100644 index 000000000..ec3073e6f --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java @@ -0,0 +1,499 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.IOException; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.ArcToBezierCurveTransformer; +import org.apache.fop.render.intermediate.BezierCurvePainter; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +/** + * PDF-specific implementation of the {@link GraphicsPainter}. + */ +public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { + + private final PDFContentGeneratorHelper generator; + + /** Used for drawing arcs since PS does not natively support drawing elliptic curves */ + private final ArcToBezierCurveTransformer arcToBezierCurveTransformer; + + public PDFGraphicsPainter(PDFContentGenerator generator) { + this.generator = new PDFContentGeneratorHelper(generator); + this.arcToBezierCurveTransformer = new ArcToBezierCurveTransformer(this); + } + + /** {@inheritDoc} */ + public void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, + boolean startOrBefore, int style, Color col) { + //TODO lose scale? + drawBorderLine2(x1 / 1000f, y1 / 1000f, x2 / 1000f, y2 / 1000f, + horz, startOrBefore, style, col); + } + + /** {@inheritDoc} */ + private void drawBorderLine2(float x1, float y1, float x2, float y2, boolean horz, + boolean startOrBefore, int style, Color col) { + float w = x2 - x1; + float h = y2 - y1; + switch (style) { + case Constants.EN_DASHED: + generator.setColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int) (w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + generator.setDashLine(unit) + .setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int) (h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + generator.setDashLine(unit) + .setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + break; + case Constants.EN_DOTTED: + generator.setColor(col).setRoundCap(); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int) (w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + generator.setDashLine(0, unit) + .setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int) (h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + generator.setDashLine(0, unit) + .setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + break; + case Constants.EN_DOUBLE: + generator.setColor(col) + .setSolidLine(); + if (horz) { + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + generator.setLineWidth(h3) + .strokeLine(x1, ym1, x2, ym1) + .strokeLine(x1, ym2, x2, ym2); + } else { + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + generator.setLineWidth(w3) + .strokeLine(xm1, y1, xm1, y2) + .strokeLine(xm2, y1, xm2, y2); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + { + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + generator.setSolidLine(); + if (horz) { + Color uppercol = ColorUtil.lightenColor(col, -colFactor); + Color lowercol = ColorUtil.lightenColor(col, colFactor); + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + generator.setLineWidth(h3) + .setColor(uppercol) + .strokeLine(x1, ym1, x2, ym1) + .setColor(col) + .strokeLine(x1, ym1 + h3, x2, ym1 + h3) + .setColor(lowercol) + .strokeLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); + } else { + Color leftcol = ColorUtil.lightenColor(col, -colFactor); + Color rightcol = ColorUtil.lightenColor(col, colFactor); + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + generator.setLineWidth(w3) + .setColor(leftcol) + .strokeLine(xm1, y1, xm1, y2) + .setColor(col) + .strokeLine(xm1 + w3, y1, xm1 + w3, y2) + .setColor(rightcol) + .strokeLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); + } + break; + } + case Constants.EN_INSET: + case Constants.EN_OUTSET: + { + float colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + generator.setSolidLine(); + Color c = col; + if (horz) { + c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); + float ym1 = y1 + (h / 2); + generator.setLineWidth(h) + .setColor(c) + .strokeLine(x1, ym1, x2, ym1); + } else { + c = ColorUtil.lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); + float xm1 = x1 + (w / 2); + generator.setLineWidth(w) + .setColor(c) + .strokeLine(xm1, y1, xm1, y2); + } + break; + } + case Constants.EN_HIDDEN: + break; + default: + generator.setColor(col).setSolidLine(); + if (horz) { + float ym = y1 + (h / 2); + generator.setLineWidth(h) + .strokeLine(x1, ym, x2, ym); + } else { + float xm = x1 + (w / 2); + generator.setLineWidth(w) + .strokeLine(xm, y1, xm, y2); + } + } + } + + /** {@inheritDoc} */ + public void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, start.y - half, end.x, end.y + half, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + generator.clipRect(boundingRect) + //This displaces the dots to the right by half a dot's width + //TODO There's room for improvement here + .transformCoordinatesLine(1, 0, 0 , 1, half, 0); + drawBorderLine(start.x, start.y - half, end.x, end.y + half, true, true, style.getEnumValue(), + color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + generator.setFillColor(ColorUtil.lightenColor(color, 0.6f)) + .fillRect(start.x, start.y, end.x, starty + 2 * half) + .setFillColor(color) + .fillRidge(style, start.x, start.y, end.x, end.y, half); + break; + default: + throw new UnsupportedOperationException("rule style not supported"); + } + restoreGraphicsState(); + } + + private static String format(int coordinate) { + //TODO lose scale? + return format(coordinate / 1000f); + } + + private static String format(float coordinate) { + return PDFContentGenerator.format(coordinate); + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) { + generator.moveTo(x, y); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) { + generator.lineTo(x, y); + } + + /** {@inheritDoc} */ + public void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + arcToBezierCurveTransformer.arcTo(startAngle, endAngle, cx, cy, width, height); + } + + /** {@inheritDoc} */ + public void closePath() { + generator.closePath(); + } + + /** {@inheritDoc} */ + public void clip() { + generator.clip(); + } + + /** {@inheritDoc} */ + public void saveGraphicsState() { + generator.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() { + generator.restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + float s = (float) Math.sin(angle); + float c = (float) Math.cos(angle); + generator.transformFloatCoordinates(c, s, -s, c, 0, 0); + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + generator.transformCoordinates(1000, 0, 0, 1000, xTranslate, yTranslate); + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) throws IOException { + generator.transformFloatCoordinates(xScale, 0, 0, yScale, 0, 0); + } + + /** {@inheritDoc} */ + public void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { + generator.cubicBezierTo(p1x, p1y, p2x, p2y, p3x, p3y); + } + + // TODO consider enriching PDFContentGenerator with part of this API + private static class PDFContentGeneratorHelper { + + private final PDFContentGenerator generator; + + public PDFContentGeneratorHelper(PDFContentGenerator generator) { + this.generator = generator; + } + + public PDFContentGeneratorHelper moveTo(int x, int y) { + return add("m", format(x), format(y)); + } + + public PDFContentGeneratorHelper lineTo(int x, int y) { + return add("l", format(x), format(y)); + } + + /** {@inheritDoc} */ + public PDFContentGeneratorHelper cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { + return add("c", format(p1x), format(p1y), format(p2x), format(p2y), format(p3x), format(p3y)); + } + + public PDFContentGeneratorHelper closePath() { + return add("h"); + } + + public PDFContentGeneratorHelper clip() { + return addLine("W\nn"); + } + + public PDFContentGeneratorHelper clipRect(Rectangle rectangle) { + generator.clipRect(rectangle); + return this; + } + + public PDFContentGeneratorHelper saveGraphicsState() { + return addLine("q"); + } + + public PDFContentGeneratorHelper restoreGraphicsState() { + return addLine("Q"); + } + + public PDFContentGeneratorHelper setSolidLine() { + generator.add("[] 0 d "); + return this; + } + + public PDFContentGeneratorHelper setRoundCap() { + return add("J", "1"); + } + + public PDFContentGeneratorHelper strokeLine(float xStart, float yStart, float xEnd, float yEnd) { + add("m", xStart, yStart); + return addLine("l S", xEnd, yEnd); + } + + public PDFContentGeneratorHelper fillRect(int xStart, int yStart, int xEnd, int yEnd) { + String xS = format(xStart); + String xE = format(xEnd); + String yS = format(yStart); + String yE = format(yEnd); + return addLine("m", xS, yS) + .addLine("l", xE, yS) + .addLine("l", xE, yE) + .addLine("l", xS, yE) + .addLine("h") + .addLine("f"); + } + + public PDFContentGeneratorHelper fillRidge(RuleStyle style, int xStart, int yStart, int xEnd, + int yEnd, int half) { + String xS = format(xStart); + String xE = format(xEnd); + String yS = format(yStart); + String yE = format(yEnd); + if (style == RuleStyle.GROOVE) { + addLine("m", xS, yS) + .addLine("l", xE, yS) + .addLine("l", xE, format(yStart + half)) + .addLine("l", format(xStart + half), format(yStart + half)) + .addLine("l", xS, format(yStart + 2 * half)); + } else { + addLine("m", xE, yS) + .addLine("l", xE, format(yStart + 2 * half)) + .addLine("l", xS, format(yStart + 2 * half)) + .addLine("l", xS, format(yStart + half)) + .addLine("l", format(xEnd - half), format(yStart + half)); + } + return addLine("h").addLine("f"); + } + + public PDFContentGeneratorHelper setLineWidth(float width) { + return addLine("w", width); + } + + public PDFContentGeneratorHelper setDashLine(float first, float... rest) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(format(first)); + for (float unit : rest) { + sb.append(" ").append(format(unit)); + } + sb.append("] 0 d "); + generator.add(sb.toString()); + return this; + } + + public PDFContentGeneratorHelper setColor(Color col) { + generator.setColor(col, false); + return this; + } + + public PDFContentGeneratorHelper setFillColor(Color col) { + generator.setColor(col, true); + return this; + } + + public PDFContentGeneratorHelper transformFloatCoordinates(float a, float b, float c, float d, + float e, float f) { + return add("cm", a, b, c, d, e, f); + } + + public PDFContentGeneratorHelper transformCoordinates(int a, int b, int c, int d, int e, int f) { + return add("cm", format(a), format(b), format(c), format(d), format(e), format(f)); + } + + public PDFContentGeneratorHelper transformCoordinatesLine(int a, int b, int c, int d, int e, int f) { + return addLine("cm", format(a), format(b), format(c), format(d), format(e), format(f)); + } + + public PDFContentGeneratorHelper add(String op) { + assert op.equals(op.trim()); + generator.add(op + " "); + return this; + } + + private PDFContentGeneratorHelper add(String op, String... args) { + add(createArgs(args), op); + return this; + } + + public PDFContentGeneratorHelper addLine(String op) { + assert op.equals(op.trim()); + generator.add(op + "\n"); + return this; + } + + public PDFContentGeneratorHelper addLine(String op, String... args) { + addLine(createArgs(args), op); + return this; + } + + private PDFContentGeneratorHelper add(String op, float... args) { + add(createArgs(args), op); + return this; + } + + public PDFContentGeneratorHelper addLine(String op, float... args) { + addLine(createArgs(args), op); + return this; + } + + private StringBuilder createArgs(float... args) { + StringBuilder sb = new StringBuilder(); + for (float arg : args) { + sb.append(format(arg)).append(" "); + } + return sb; + } + + private StringBuilder createArgs(String... args) { + StringBuilder sb = new StringBuilder(); + for (String arg : args) { + sb.append(arg).append(" "); + } + return sb; + } + + private void add(StringBuilder args, String op) { + assert op.equals(op.trim()); + generator.add(args.append(op).append(" ").toString()); + } + + private void addLine(StringBuilder args, String op) { + assert op.equals(op.trim()); + generator.add(args.append(op).append("\n").toString()); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 77ec1d3bf..4ea7da945 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -40,6 +40,8 @@ import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -57,7 +59,9 @@ public class PDFPainter extends AbstractIFPainter { /** The current content generator */ protected PDFContentGenerator generator; - private final PDFBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private final BorderPainter borderPainter; private boolean accessEnabled; @@ -75,7 +79,8 @@ public class PDFPainter extends AbstractIFPainter { super(documentHandler); this.logicalStructureHandler = logicalStructureHandler; this.generator = documentHandler.getGenerator(); - this.borderPainter = new PDFBorderPainter(this.generator); + this.graphicsPainter = new PDFGraphicsPainter(this.generator); + this.borderPainter = new BorderPainter(this.graphicsPainter); this.state = IFState.create(); accessEnabled = this.getUserAgent().isAccessibilityEnabled(); } @@ -267,7 +272,11 @@ public class PDFPainter extends AbstractIFPainter { public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { generator.endTextObject(); - this.borderPainter.drawLine(start, end, width, color, style); + try { + this.graphicsPainter.drawLine(start, end, width, color, style); + } catch (IOException ioe) { + throw new IFException("Cannot draw line", ioe); + } } private Typeface getTypeface(String fontName) { diff --git a/src/java/org/apache/fop/render/ps/PSBorderPainter.java b/src/java/org/apache/fop/render/ps/PSBorderPainter.java deleted file mode 100644 index 476e14c99..000000000 --- a/src/java/org/apache/fop/render/ps/PSBorderPainter.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.ps; - -import java.awt.Color; -import java.awt.Point; -import java.io.IOException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.xmlgraphics.ps.PSGenerator; - -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.BorderPainter; -import org.apache.fop.traits.RuleStyle; -import org.apache.fop.util.ColorUtil; - -/** - * PostScript-specific implementation of the {@link BorderPainter}. - */ -public class PSBorderPainter extends BorderPainter { - - /** logging instance */ - private static Log log = LogFactory.getLog(PSBorderPainter.class); - - private PSGenerator generator; - - /** - * Creates a new border painter for PostScript. - * @param generator the PostScript generator - */ - public PSBorderPainter(PSGenerator generator) { - this.generator = generator; - } - - /** {@inheritDoc} */ - protected void drawBorderLine( // CSOK: ParameterNumber - int x1, int y1, int x2, int y2, boolean horz, - boolean startOrBefore, int style, Color col) throws IOException { - drawBorderLine(generator, toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), - horz, startOrBefore, style, col); - } - - private static void drawLine(PSGenerator gen, - float startx, float starty, float endx, float endy) throws IOException { - gen.writeln(gen.formatDouble(startx) + " " - + gen.formatDouble(starty) + " " + gen.mapCommand("moveto") + " " - + gen.formatDouble(endx) + " " - + gen.formatDouble(endy) + " " + gen.mapCommand("lineto") + " " - + gen.mapCommand("stroke") + " " + gen.mapCommand("newpath")); - } - - /** - * @param gen ps content generator - * @see BorderPainter#drawBorderLine - */ - public static void drawBorderLine( // CSOK: ParameterNumber - PSGenerator gen, - float x1, float y1, float x2, float y2, boolean horz, // CSOK: JavadocMethod - boolean startOrBefore, int style, Color col) // CSOK: JavadocMethod - throws IOException { // CSOK: JavadocMethod - float w = x2 - x1; - float h = y2 - y1; - if ((w < 0) || (h < 0)) { - log.error("Negative extent received. Border won't be painted."); - return; - } - switch (style) { - case Constants.EN_DASHED: - gen.useColor(col); - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - gen.useDash("[" + unit + "] 0"); - gen.useLineCap(0); - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - gen.useDash("[" + unit + "] 0"); - gen.useLineCap(0); - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - break; - case Constants.EN_DOTTED: - gen.useColor(col); - gen.useLineCap(1); //Rounded! - if (horz) { - float unit = Math.abs(2 * h); - int rep = (int)(w / unit); - if (rep % 2 == 0) { - rep++; - } - unit = w / rep; - gen.useDash("[0 " + unit + "] 0"); - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - float unit = Math.abs(2 * w); - int rep = (int)(h / unit); - if (rep % 2 == 0) { - rep++; - } - unit = h / rep; - gen.useDash("[0 " + unit + "] 0"); - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - break; - case Constants.EN_DOUBLE: - gen.useColor(col); - gen.useDash(null); - if (horz) { - float h3 = h / 3; - gen.useLineWidth(h3); - float ym1 = y1 + (h3 / 2); - float ym2 = ym1 + h3 + h3; - drawLine(gen, x1, ym1, x2, ym1); - drawLine(gen, x1, ym2, x2, ym2); - } else { - float w3 = w / 3; - gen.useLineWidth(w3); - float xm1 = x1 + (w3 / 2); - float xm2 = xm1 + w3 + w3; - drawLine(gen, xm1, y1, xm1, y2); - drawLine(gen, xm2, y1, xm2, y2); - } - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); - gen.useDash(null); - if (horz) { - Color uppercol = ColorUtil.lightenColor(col, -colFactor); - Color lowercol = ColorUtil.lightenColor(col, colFactor); - float h3 = h / 3; - gen.useLineWidth(h3); - float ym1 = y1 + (h3 / 2); - gen.useColor(uppercol); - drawLine(gen, x1, ym1, x2, ym1); - gen.useColor(col); - drawLine(gen, x1, ym1 + h3, x2, ym1 + h3); - gen.useColor(lowercol); - drawLine(gen, x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); - } else { - Color leftcol = ColorUtil.lightenColor(col, -colFactor); - Color rightcol = ColorUtil.lightenColor(col, colFactor); - float w3 = w / 3; - gen.useLineWidth(w3); - float xm1 = x1 + (w3 / 2); - gen.useColor(leftcol); - drawLine(gen, xm1, y1, xm1, y2); - gen.useColor(col); - drawLine(gen, xm1 + w3, y1, xm1 + w3, y2); - gen.useColor(rightcol); - drawLine(gen, xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); - } - break; - case Constants.EN_INSET: - case Constants.EN_OUTSET: - colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); - gen.useDash(null); - if (horz) { - Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); - gen.useLineWidth(h); - float ym1 = y1 + (h / 2); - gen.useColor(c); - drawLine(gen, x1, ym1, x2, ym1); - } else { - Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); - gen.useLineWidth(w); - float xm1 = x1 + (w / 2); - gen.useColor(c); - drawLine(gen, xm1, y1, xm1, y2); - } - break; - case Constants.EN_HIDDEN: - break; - default: - gen.useColor(col); - gen.useDash(null); - gen.useLineCap(0); - if (horz) { - gen.useLineWidth(h); - float ym = y1 + (h / 2); - drawLine(gen, x1, ym, x2, ym); - } else { - gen.useLineWidth(w); - float xm = x1 + (w / 2); - drawLine(gen, xm, y1, xm, y2); - } - } - } - - /** {@inheritDoc} */ - public void drawLine(Point start, Point end, - int width, Color color, RuleStyle style) throws IOException { - if (start.y != end.y) { - //TODO Support arbitrary lines if necessary - throw new UnsupportedOperationException( - "Can only deal with horizontal lines right now"); - } - - saveGraphicsState(); - int half = width / 2; - int starty = start.y - half; - //Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); - - switch (style.getEnumValue()) { - case Constants.EN_SOLID: - case Constants.EN_DASHED: - case Constants.EN_DOUBLE: - drawBorderLine(start.x, starty, end.x, starty + width, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_DOTTED: - clipRect(start.x, starty, end.x - start.x, width); - //This displaces the dots to the right by half a dot's width - //TODO There's room for improvement here - generator.concatMatrix(1, 0, 0, 1, toPoints(half), 0); - drawBorderLine(start.x, starty, end.x, starty + width, - true, true, style.getEnumValue(), color); - break; - case Constants.EN_GROOVE: - case Constants.EN_RIDGE: - generator.useColor(ColorUtil.lightenColor(color, 0.6f)); - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - closePath(); - generator.write(" " + generator.mapCommand("fill")); - generator.writeln(" " + generator.mapCommand("newpath")); - generator.useColor(color); - if (style == RuleStyle.GROOVE) { - moveTo(start.x, starty); - lineTo(end.x, starty); - lineTo(end.x, starty + half); - lineTo(start.x + half, starty + half); - lineTo(start.x, starty + 2 * half); - } else { - moveTo(end.x, starty); - lineTo(end.x, starty + 2 * half); - lineTo(start.x, starty + 2 * half); - lineTo(start.x, starty + half); - lineTo(end.x - half, starty + half); - } - closePath(); - generator.write(" " + generator.mapCommand("fill")); - generator.writeln(" " + generator.mapCommand("newpath")); - break; - default: - throw new UnsupportedOperationException("rule style not supported"); - } - - restoreGraphicsState(); - - } - - private static float toPoints(int mpt) { - return mpt / 1000f; - } - - /** {@inheritDoc} */ - protected void moveTo(int x, int y) throws IOException { - generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("moveto")); - } - - /** {@inheritDoc} */ - protected void lineTo(int x, int y) throws IOException { - generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("lineto")); - } - - /** {@inheritDoc} */ - protected void closePath() throws IOException { - generator.writeln("cp"); - } - - private void clipRect(int x, int y, int width, int height) throws IOException { - generator.defineRect(toPoints(x), toPoints(y), toPoints(width), toPoints(height)); - clip(); - } - - /** {@inheritDoc} */ - protected void clip() throws IOException { - generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); - } - - /** {@inheritDoc} */ - protected void saveGraphicsState() throws IOException { - generator.saveGraphicsState(); - } - - /** {@inheritDoc} */ - protected void restoreGraphicsState() throws IOException { - 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()); - } - -} diff --git a/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java new file mode 100644 index 000000000..e9b4b4ff5 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java @@ -0,0 +1,387 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Color; +import java.awt.Point; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.xmlgraphics.ps.PSGenerator; + +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.ArcToBezierCurveTransformer; +import org.apache.fop.render.intermediate.BezierCurvePainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; +import org.apache.fop.traits.RuleStyle; +import org.apache.fop.util.ColorUtil; + +/** + * PostScript-specific implementation of the {@link BorderPainter}. + */ +public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { + + /** logging instance */ + private static Log log = LogFactory.getLog(PSGraphicsPainter.class); + + private final PSGenerator generator; + + /** Used for drawing arcs since PS does not natively support drawing elliptic curves */ + private final ArcToBezierCurveTransformer arcToBezierCurveTransformer; + + /** + * Creates a new border painter for PostScript. + * @param generator the PostScript generator + */ + public PSGraphicsPainter(PSGenerator generator) { + this.generator = generator; + this.arcToBezierCurveTransformer = new ArcToBezierCurveTransformer(this); + } + + /** {@inheritDoc} */ + public void drawBorderLine(int x1, int y1, int x2, int y2, boolean horz, + boolean startOrBefore, int style, Color col) throws IOException { + drawBorderLine(generator, toPoints(x1), toPoints(y1), toPoints(x2), toPoints(y2), + horz, startOrBefore, style, col); + } + + private static void drawLine(PSGenerator gen, + float startx, float starty, float endx, float endy) throws IOException { + gen.writeln(gen.formatDouble(startx) + " " + + gen.formatDouble(starty) + " " + gen.mapCommand("moveto") + " " + + gen.formatDouble(endx) + " " + + gen.formatDouble(endy) + " " + gen.mapCommand("lineto") + " " + + gen.mapCommand("stroke") + " " + gen.mapCommand("newpath")); + } + + /** {@inheritDoc} */ + public static void drawBorderLine(PSGenerator gen, + float x1, float y1, float x2, float y2, boolean horz, + boolean startOrBefore, int style, Color col) throws IOException { + float w = x2 - x1; + float h = y2 - y1; + if ((w < 0) || (h < 0)) { + log.error("Negative extent received. Border won't be painted."); + return; + } + switch (style) { + case Constants.EN_DASHED: + gen.useColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + gen.useDash("[" + unit + "] 0"); + gen.useLineCap(0); + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + gen.useDash("[" + unit + "] 0"); + gen.useLineCap(0); + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + break; + case Constants.EN_DOTTED: + gen.useColor(col); + gen.useLineCap(1); //Rounded! + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + gen.useDash("[0 " + unit + "] 0"); + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + gen.useDash("[0 " + unit + "] 0"); + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + break; + case Constants.EN_DOUBLE: + gen.useColor(col); + gen.useDash(null); + if (horz) { + float h3 = h / 3; + gen.useLineWidth(h3); + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + drawLine(gen, x1, ym1, x2, ym1); + drawLine(gen, x1, ym2, x2, ym2); + } else { + float w3 = w / 3; + gen.useLineWidth(w3); + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + drawLine(gen, xm1, y1, xm1, y2); + drawLine(gen, xm2, y1, xm2, y2); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + gen.useDash(null); + if (horz) { + Color uppercol = ColorUtil.lightenColor(col, -colFactor); + Color lowercol = ColorUtil.lightenColor(col, colFactor); + float h3 = h / 3; + gen.useLineWidth(h3); + float ym1 = y1 + (h3 / 2); + gen.useColor(uppercol); + drawLine(gen, x1, ym1, x2, ym1); + gen.useColor(col); + drawLine(gen, x1, ym1 + h3, x2, ym1 + h3); + gen.useColor(lowercol); + drawLine(gen, x1, ym1 + h3 + h3, x2, ym1 + h3 + h3); + } else { + Color leftcol = ColorUtil.lightenColor(col, -colFactor); + Color rightcol = ColorUtil.lightenColor(col, colFactor); + float w3 = w / 3; + gen.useLineWidth(w3); + float xm1 = x1 + (w3 / 2); + gen.useColor(leftcol); + drawLine(gen, xm1, y1, xm1, y2); + gen.useColor(col); + drawLine(gen, xm1 + w3, y1, xm1 + w3, y2); + gen.useColor(rightcol); + drawLine(gen, xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); + } + break; + case Constants.EN_INSET: + case Constants.EN_OUTSET: + colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + gen.useDash(null); + if (horz) { + Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + gen.useLineWidth(h); + float ym1 = y1 + (h / 2); + gen.useColor(c); + drawLine(gen, x1, ym1, x2, ym1); + } else { + Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + gen.useLineWidth(w); + float xm1 = x1 + (w / 2); + gen.useColor(c); + drawLine(gen, xm1, y1, xm1, y2); + } + break; + case Constants.EN_HIDDEN: + break; + default: + gen.useColor(col); + gen.useDash(null); + gen.useLineCap(0); + if (horz) { + gen.useLineWidth(h); + float ym = y1 + (h / 2); + drawLine(gen, x1, ym, x2, ym); + } else { + gen.useLineWidth(w); + float xm = x1 + (w / 2); + drawLine(gen, xm, y1, xm, y2); + } + } + } + + /** {@inheritDoc} */ + public void drawLine(Point start, Point end, + int width, Color color, RuleStyle style) throws IOException { + if (start.y != end.y) { + //TODO Support arbitrary lines if necessary + throw new UnsupportedOperationException( + "Can only deal with horizontal lines right now"); + } + + saveGraphicsState(); + int half = width / 2; + int starty = start.y - half; + //Rectangle boundingRect = new Rectangle(start.x, start.y - half, end.x - start.x, width); + + switch (style.getEnumValue()) { + case Constants.EN_SOLID: + case Constants.EN_DASHED: + case Constants.EN_DOUBLE: + drawBorderLine(start.x, starty, end.x, starty + width, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_DOTTED: + clipRect(start.x, starty, end.x - start.x, width); + //This displaces the dots to the right by half a dot's width + //TODO There's room for improvement here + generator.concatMatrix(1, 0, 0, 1, toPoints(half), 0); + drawBorderLine(start.x, starty, end.x, starty + width, + true, true, style.getEnumValue(), color); + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + generator.useColor(ColorUtil.lightenColor(color, 0.6f)); + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + closePath(); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); + generator.useColor(color); + if (style == RuleStyle.GROOVE) { + moveTo(start.x, starty); + lineTo(end.x, starty); + lineTo(end.x, starty + half); + lineTo(start.x + half, starty + half); + lineTo(start.x, starty + 2 * half); + } else { + moveTo(end.x, starty); + lineTo(end.x, starty + 2 * half); + lineTo(start.x, starty + 2 * half); + lineTo(start.x, starty + half); + lineTo(end.x - half, starty + half); + } + closePath(); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); + break; + default: + throw new UnsupportedOperationException("rule style not supported"); + } + + restoreGraphicsState(); + + } + + private static float toPoints(int mpt) { + return mpt / 1000f; + } + + /** {@inheritDoc} */ + public void moveTo(int x, int y) throws IOException { + generator.writeln(generator.formatDouble(toPoints(x)) + " " + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("moveto")); + } + + /** {@inheritDoc} */ + public void lineTo(int x, int y) throws IOException { + generator.writeln(generator.formatDouble(toPoints(x)) + " " + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("lineto")); + } + + /** {@inheritDoc} */ + public void arcTo(final double startAngle, final double endAngle, final int cx, final int cy, + final int width, final int height) throws IOException { + arcToBezierCurveTransformer.arcTo(startAngle, endAngle, cx, cy, width, height); + } + + /** {@inheritDoc} */ + public void closePath() throws IOException { + generator.writeln("cp"); + } + + private void clipRect(int x, int y, int width, int height) throws IOException { + generator.defineRect(toPoints(x), toPoints(y), toPoints(width), toPoints(height)); + clip(); + } + + /** {@inheritDoc} */ + public void clip() throws IOException { + generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); + } + + /** {@inheritDoc} */ + public void saveGraphicsState() throws IOException { + generator.saveGraphicsState(); + } + + /** {@inheritDoc} */ + public void restoreGraphicsState() throws IOException { + generator.restoreGraphicsState(); + } + + /** {@inheritDoc} */ + public void rotateCoordinates(double angle) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(angle * 180d / Math.PI)) + .append(" rotate "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void translateCoordinates(int xTranslate, int yTranslate) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(toPoints(xTranslate))) + .append(" ") + .append(generator.formatDouble(toPoints(yTranslate))) + .append(" translate "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void scaleCoordinates(float xScale, float yScale) throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(xScale)) + .append(" ") + .append(generator.formatDouble(yScale)) + .append(" scale "); + generator.writeln(sb.toString()); + } + + /** {@inheritDoc} */ + public void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) + throws IOException { + StringBuffer sb = new StringBuffer() + .append(generator.formatDouble(toPoints(p1x))) + .append(" ") + .append(generator.formatDouble(toPoints(p1y))) + .append(" ") + .append(generator.formatDouble(toPoints(p2x))) + .append(" ") + .append(generator.formatDouble(toPoints(p2y))) + .append(" ") + .append(generator.formatDouble(toPoints(p3x))) + .append(" ") + .append(generator.formatDouble(toPoints(p3y))) + .append(" curveto "); + generator.writeln(sb.toString()); + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index b8947ff60..97bf7e647 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -48,6 +48,8 @@ import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.intermediate.AbstractIFPainter; +import org.apache.fop.render.intermediate.BorderPainter; +import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.render.intermediate.IFUtil; @@ -64,7 +66,9 @@ public class PSPainter extends AbstractIFPainter { /** logging instance */ private static Log log = LogFactory.getLog(PSPainter.class); - private PSBorderPainter borderPainter; + private final GraphicsPainter graphicsPainter; + + private BorderPainter borderPainter; private boolean inTextMode = false; @@ -78,7 +82,8 @@ public class PSPainter extends AbstractIFPainter { protected PSPainter(PSDocumentHandler documentHandler, IFState state) { super(documentHandler); - this.borderPainter = new PSBorderPainter(getGenerator()); + this.graphicsPainter = new PSGraphicsPainter(getGenerator()); + this.borderPainter = new BorderPainter(graphicsPainter); this.state = state; } @@ -260,7 +265,7 @@ public class PSPainter extends AbstractIFPainter { throws IFException { try { endTextObject(); - this.borderPainter.drawLine(start, end, width, color, style); + this.graphicsPainter.drawLine(start, end, width, color, style); } catch (IOException ioe) { throw new IFException("I/O error in drawLine()", ioe); } diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 3eca9c43f..61a7d86b6 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -34,52 +34,75 @@ import org.apache.fop.util.ColorUtil; */ public class BorderProps implements Serializable { - private static final long serialVersionUID = -886871454032189183L; + private static final long serialVersionUID = 7053576586478548795L; - /** Separate border model */ - public static final int SEPARATE = 0; - /** Collapsing border model, for borders inside a table */ - public static final int COLLAPSE_INNER = 1; - /** Collapsing border model, for borders at the table's outer border */ - public static final int COLLAPSE_OUTER = 2; + public enum Mode { + SEPARATE("separate") { + @Override + int getClippedWidth(BorderProps bp) { + return 0; + } + }, + COLLAPSE_INNER("collapse-inner"), // for borders inside a table + COLLAPSE_OUTER("collapse-outer"); // for borders at the table's outer border + + private final String value; + + Mode(String value) { + this.value = value; + } + + int getClippedWidth(BorderProps bp) { + return bp.width / 2; + }; + } /** Border style (one of EN_*) */ - public int style; // Enum for border style // CSOK: VisibilityModifier + public final int style; // Enum for border style // CSOK: VisibilityModifier /** Border color */ - public Color color; // CSOK: VisibilityModifier + public final Color color; // CSOK: VisibilityModifier + /** Border width */ - public int width; // CSOK: VisibilityModifier + public final 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 + private final int radiusStart; + + private final int radiusEnd; + + /** Border mode */ + private final Mode mode; // CSOK: VisibilityModifier /** * Constructs a new BorderProps instance. * @param style border style (one of EN_*) * @param width border width + * @param radiusStart radius of start corner in the direction perpendicular to border segment + * @param radiusEnd radius of end corner in the direction perpendicular to border segment * @param color border color * @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */ - public BorderProps(int style, int width, Color color, int mode) { + public BorderProps(int style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) { this.style = style; this.width = width; + this.radiusStart = radiusStart; + this.radiusEnd = radiusEnd; this.color = color; this.mode = mode; } /** - * Constructs a new BorderProps instance. - * @param style border style (one of the XSL enum values for border style) + * Factory method for a new BorderProps instance with rectangular corners. + * @param style border style (one of EN_*) * @param width border width * @param color border color * @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */ - public BorderProps(String style, int width, Color color, int mode) { - this(getConstantForStyle(style), width, color, mode); + public static BorderProps makeRectangular(int style, int width, Color color, Mode mode) { + return new BorderProps(style, width, 0, 0, color, mode); + } + + private BorderProps(String style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) { + this(getConstantForStyle(style), width, radiusStart, radiusEnd, color, mode); } /** @@ -90,14 +113,6 @@ public class BorderProps implements Serializable { 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 */ @@ -105,24 +120,12 @@ public class BorderProps implements Serializable { 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 */ public static int getClippedWidth(BorderProps bp) { - if ((bp != null) && (bp.mode != SEPARATE)) { - return bp.width / 2; - } else { - return 0; - } + return bp == null ? 0 : bp.mode.getClippedWidth(bp); } private String getStyleString() { @@ -133,6 +136,10 @@ public class BorderProps implements Serializable { return BorderStyle.valueOf(style).getEnumValue(); } + public boolean isCollapseOuter() { + return mode == Mode.COLLAPSE_OUTER; + } + /** {@inheritDoc} */ @Override public int hashCode() { @@ -169,83 +176,79 @@ public class BorderProps implements Serializable { * @return a BorderProps instance */ public static BorderProps valueOf(FOUserAgent foUserAgent, String s) { - if (s.startsWith("(") && s.endsWith(")")) { - s = s.substring(1, s.length() - 1); - Pattern pattern = Pattern.compile("([^,\\(]+(?:\\(.*\\))?)"); - Matcher m = pattern.matcher(s); - boolean found; - found = m.find(); - String style = m.group(); - found = m.find(); - String color = m.group(); - found = m.find(); - int width = Integer.parseInt(m.group()); - int mode = SEPARATE; - found = m.find(); - if (found) { - String ms = m.group(); - if ("collapse-inner".equalsIgnoreCase(ms)) { - mode = COLLAPSE_INNER; - } else if ("collapse-outer".equalsIgnoreCase(ms)) { - mode = COLLAPSE_OUTER; - } - } - Color c; - try { - c = ColorUtil.parseColorString(foUserAgent, color); - } catch (PropertyException e) { - throw new IllegalArgumentException(e.getMessage()); - } - - 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"); - } + return BorderPropsDeserializer.INSTANCE.valueOf(foUserAgent, s); } - /** {@inheritDoc} */ @Override public String toString() { StringBuffer sbuf = new StringBuffer(); - sbuf.append('('); - sbuf.append(getStyleString()); - sbuf.append(','); - sbuf.append(ColorUtil.colorToString(color)); - sbuf.append(','); - sbuf.append(width); - if (mode != SEPARATE) { - if (mode == COLLAPSE_INNER) { - sbuf.append(",collapse-inner"); - } else { - sbuf.append(",collapse-outer"); - } + sbuf.append('(') + .append(getStyleString()).append(',') + .append(ColorUtil.colorToString(color)).append(',') + .append(width); + if (!mode.equals(Mode.SEPARATE)) { + sbuf.append(",").append(mode.value); } if (radiusStart != 0 || radiusEnd != 0) { - if (mode == SEPARATE) { + if (mode.equals(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(",").append(Mode.SEPARATE.value); } - sbuf.append(','); - sbuf.append(radiusStart); - - sbuf.append(','); - sbuf.append(radiusEnd); + sbuf.append(',').append(radiusStart) + .append(',').append(radiusEnd); } sbuf.append(')'); return sbuf.toString(); } + private static class BorderPropsDeserializer { + + private static final BorderPropsDeserializer INSTANCE = new BorderPropsDeserializer(); + + private static final Pattern PATTERN = Pattern.compile("([^,\\(]+(?:\\(.*\\))?)"); + + private BorderPropsDeserializer() { + } + + public BorderProps valueOf(FOUserAgent foUserAgent, String s) { + if (s.startsWith("(") && s.endsWith(")")) { + s = s.substring(1, s.length() - 1); + Matcher m = PATTERN.matcher(s); + m.find(); + String style = m.group(); + m.find(); + String color = m.group(); + m.find(); + int width = Integer.parseInt(m.group()); + Mode mode = Mode.SEPARATE; + if ( m.find()) { + String ms = m.group(); + if (Mode.COLLAPSE_INNER.value.equalsIgnoreCase(ms)) { + mode = Mode.COLLAPSE_INNER; + } else if (Mode.COLLAPSE_OUTER.value.equalsIgnoreCase(ms)) { + mode = Mode.COLLAPSE_OUTER; + } + } + Color c; + try { + c = ColorUtil.parseColorString(foUserAgent, color); + } catch (PropertyException e) { + throw new IllegalArgumentException(e.getMessage()); + } + int startRadius = 0; + int endRadius = 0; + if (m.find()) { + startRadius = Integer.parseInt(m.group()); + m.find(); + endRadius = Integer.parseInt(m.group()); + } + return new BorderProps(style, width, startRadius, endRadius, c, mode); + } else { + throw new IllegalArgumentException("BorderProps must be surrounded by parentheses"); + } + } + } + } diff --git a/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java new file mode 100644 index 000000000..fd6209bf1 --- /dev/null +++ b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.afp; + +import java.awt.Color; +import java.awt.Rectangle; +import java.util.Map; + +import org.junit.Test; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.DefaultImageContext; +import org.apache.xmlgraphics.image.loader.impl.DefaultImageSessionContext; +import org.apache.xmlgraphics.image.loader.impl.ImageBuffered; + +import org.apache.fop.afp.AFPEventProducer; +import org.apache.fop.afp.AFPPaintingState; +import org.apache.fop.afp.AFPResourceManager; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.events.EventBroadcaster; +import org.apache.fop.fo.Constants; +import org.apache.fop.render.ImageHandlerRegistry; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.traits.BorderProps; + +public class AFPPainterTestCase { + + @Test + public void testDrawBorderRect() { + // the goal of this test is to check that the drawing of rounded corners in AFP uses a bitmap of the + // rounded corners (in fact the whole rectangle with rounded corners). the check is done by verifying + // that the AFPImageHandlerRenderedImage.handleImage() method is called + // mock + AFPPaintingState afpPaintingState = mock(AFPPaintingState.class); + when(afpPaintingState.getResolution()).thenReturn(72); + // mock + EventBroadcaster eventBroadcaster = mock(EventBroadcaster.class); + // mock + DefaultImageContext defaultImageContext = mock(DefaultImageContext.class); + when(defaultImageContext.getSourceResolution()).thenReturn(72000f); + // mock + DefaultImageSessionContext defaultImageSessionContxt = mock(DefaultImageSessionContext.class); + when(defaultImageSessionContxt.getParentContext()).thenReturn(defaultImageContext); + when(defaultImageSessionContxt.getTargetResolution()).thenReturn(72000f); + // mock + ImageBuffered imageBuffered = mock(ImageBuffered.class); + // mock + ImageManager imageManager = mock(ImageManager.class); + // mock + AFPImageHandlerRenderedImage afpImageHandlerRenderedImage = mock(AFPImageHandlerRenderedImage.class); + // mock + ImageHandlerRegistry imageHandlerRegistry = mock(ImageHandlerRegistry.class); + when(imageHandlerRegistry.getHandler(any(AFPRenderingContext.class), any(Image.class))).thenReturn( + afpImageHandlerRenderedImage); + // mock + FOUserAgent foUserAgent = mock(FOUserAgent.class); + when(foUserAgent.getEventBroadcaster()).thenReturn(eventBroadcaster); + when(foUserAgent.getImageSessionContext()).thenReturn(defaultImageSessionContxt); + when(foUserAgent.getImageManager()).thenReturn(imageManager); + when(foUserAgent.getImageHandlerRegistry()).thenReturn(imageHandlerRegistry); + // mock + AFPEventProducer afpEventProducer = mock(AFPEventProducer.class); + when(AFPEventProducer.Provider.get(eventBroadcaster)).thenReturn(afpEventProducer); + // mock + AFPResourceManager afpResourceManager = mock(AFPResourceManager.class); + when(afpResourceManager.isObjectCached(null)).thenReturn(false); + // mock + IFContext ifContext = mock(IFContext.class); + when(ifContext.getUserAgent()).thenReturn(foUserAgent); + // mock + AFPDocumentHandler afpDocumentHandler = mock(AFPDocumentHandler.class); + when(afpDocumentHandler.getPaintingState()).thenReturn(afpPaintingState); + when(afpDocumentHandler.getContext()).thenReturn(ifContext); + when(afpDocumentHandler.getResourceManager()).thenReturn(afpResourceManager); + when(afpDocumentHandler.cacheRoundedCorner("a2a48964ba2d")).thenReturn("RC000000"); + // real instance, no mock + AFPPainter afpPainter = new AFPPainter(afpDocumentHandler); + // build rectangle 200 x 50 (points, which are converted to millipoints) + Rectangle rectangle = new Rectangle(0, 0, 200000, 50000); + // build border properties + int style = Constants.EN_SOLID; + BorderProps.Mode mode = BorderProps.Mode.SEPARATE; + Color color = Color.BLACK; + int borderWidth = 4000; + int radiusStart = 30000; + int radiusEnd = 30000; + BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + try { + when(imageManager.convertImage(any(Image.class), any(ImageFlavor[].class), any(Map.class))) + .thenReturn(imageBuffered); + afpPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE); + // note: here we would really like to verify that the second and third arguments passed to + // handleImage() are the instances ib and rect declared above but that causes mockito to throw + // an exception, probably because we cannot declare the AFPRenderingContext and are forced to + // use any(), which forces the use of any() for all arguments + verify(afpImageHandlerRenderedImage).handleImage(any(AFPRenderingContext.class), + any(Image.class), any(Rectangle.class)); + } catch (Exception e) { + fail("something broke..."); + } + } + +} diff --git a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java index 0f3e6e847..592335648 100644 --- a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java +++ b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java @@ -60,6 +60,10 @@ public class AbstractIFPainterTestCase { public void clipRect(Rectangle rect) throws IFException { } + public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, + BorderProps bpsStart, BorderProps bpsEnd) throws IFException { + } + public void fillRect(Rectangle rect, Paint fill) throws IFException { } @@ -78,12 +82,6 @@ public class AbstractIFPainterTestCase { String text) throws IFException { } - public void clipBackground(Rectangle rect, BorderProps bpsBefore, - BorderProps bpsAfter, BorderProps bpsStart, - BorderProps bpsEnd) throws IFException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } }; FontInfo fontInfo = mock(FontInfo.class); when(handler.getFontInfo()).thenReturn(fontInfo); diff --git a/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java new file mode 100644 index 000000000..61093c629 --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package org.apache.fop.render.intermediate; + +import java.io.IOException; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class ArcToBezierCurveTransformerTestCase { + + @Test + public void arcTo() throws Exception { + testArcTo(Math.PI / 3, Math.PI / 2, 100, 200, 1000, 1000); + } + + private void testArcTo(double startAngle, double endAngle, int xCenter, int yCenter, int width, + int height) throws IOException { + assertAngleWithinFirstQuadrant(startAngle); + assertAngleWithinFirstQuadrant(endAngle); + BezierCurvePainter bezierCurvePainter = mock(BezierCurvePainter.class); + ArcToBezierCurveTransformer sut = new ArcToBezierCurveTransformer(bezierCurvePainter); + sut.arcTo(startAngle, endAngle, xCenter, yCenter, width, height); + double tan1 = Math.tan(startAngle); + double tan2 = Math.tan(endAngle); + double lambda1 = Math.atan(height * tan1 / width); + double lambda2 = Math.atan(height * tan2 / width); + double xStart = width * Math.cos(lambda1) + xCenter; + double yStart = height * Math.sin(lambda1) + yCenter; + double xEnd = width * Math.cos(lambda2) + xCenter; + double yEnd = height * Math.sin(lambda2) + yCenter; + ArgumentCaptor xP1Captor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor yP1Captor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor xP2Captor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor yP2Captor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor xP3Captor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor yP3Captor = ArgumentCaptor.forClass(Integer.class); + verify(bezierCurvePainter).cubicBezierTo(xP1Captor.capture(), yP1Captor.capture(), + xP2Captor.capture(), yP2Captor.capture(), xP3Captor.capture(), yP3Captor.capture()); + int xP1 = xP1Captor.getValue(); + int yP1 = yP1Captor.getValue(); + int xP2 = xP2Captor.getValue(); + int yP2 = yP2Captor.getValue(); + int xP3 = xP3Captor.getValue(); + int yP3 = yP3Captor.getValue(); + // TODO do more than check the direction of the tangents at the end + // points + assertEquals((yP1 - yStart) / (xP1 - xStart), -width * width / height / height / tan1, 0.01); + assertEquals((yP2 - yEnd) / (xP2 - xEnd), -width * width / height / height / tan2, 0.01); + assertEquals((int) xEnd, xP3); + assertEquals((int) yEnd, yP3); + } + + private void assertAngleWithinFirstQuadrant(double angle) { + if (angle <= 0 || angle > Math.PI / 2) { + fail("Angle " + angle + " is in (0, " + Math.PI / 2 + ")"); + } + } +} \ No newline at end of file diff --git a/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java new file mode 100644 index 000000000..fab6e0f4c --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java @@ -0,0 +1,565 @@ +/* + * 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. + */ + +package org.apache.fop.render.intermediate; + +import java.awt.Color; +import java.awt.Rectangle; +import java.io.IOException; + +import org.junit.Test; + +import org.apache.fop.fo.Constants; +import org.apache.fop.traits.BorderProps; +import org.apache.fop.traits.BorderProps.Mode; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class BorderPainterTestCase { + + private static final BorderProps BORDER_PROPS = new BorderProps(Constants.EN_SOLID, 10, 50, 50, + Color.BLACK, BorderProps.Mode.SEPARATE); + + @Test + public void clipBackground() throws Exception { + // Rectangular borders + test(new ClipBackgroundTester(0, 0, 10, 10)); + test(new ClipBackgroundTester(5, 10, 10, 10)); + test(new ClipBackgroundTester(0, 0, 10, 10).setBorderWidth(1)); + test(new ClipBackgroundTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester()); + // Rounded corners + test(new ClipBackgroundTester(0, 0, 10, 10).setEndBefore(1, 1)); + test(new ClipBackgroundTester(0, 0, 10, 10).setEndAfter(1, 1)); + test(new ClipBackgroundTester(0, 0, 10, 10).setStartAfter(1, 1)); + test(new ClipBackgroundTester(0, 0, 10, 10).setStartBefore(1, 1)); + test(new ClipBackgroundTester(0, 0, 100, 100) + .setCornerRadii(10) + .beforeBorder().setWidth(5).tester() + .startBorder().setWidth(5).tester()); + test(new ClipBackgroundTester(0, 0, 100, 100) + .setCornerRadii(10) + .beforeBorder().setWidth(10).tester() + .startBorder().setWidth(10).tester()); + test(new ClipBackgroundTester(0, 0, 100, 100) + .setCornerRadii(10) + .beforeBorder().setWidth(5).tester()); + test(new ClipBackgroundTester(0, 0, 100, 100) + .setCornerRadii(10) + .setStartBefore(10, 10) + .beforeBorder().setWidth(10).tester()); + } + + private void test(BorderPainterTester tester) throws IOException { + tester.test(); + } + + @Test (expected = IFException.class) + public void drawBordersThrowsIFException() throws Exception { + GraphicsPainter graphicsPainter = mock(GraphicsPainter.class); + doThrow(new IOException()).when(graphicsPainter).saveGraphicsState(); + new BorderPainter(graphicsPainter).drawBorders(new Rectangle(0, 0, 1000, 1000), BORDER_PROPS, + BORDER_PROPS, BORDER_PROPS, BORDER_PROPS, Color.WHITE); + } + + @Test + public void testDrawRectangularBorders() throws IOException { + test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10)); + test(new DrawRectangularBordersTester(0, 0, 1000, 1000)); + test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10) + .beforeBorder().setWidth(0).tester()); + } + + @Test + public void testDrawRectangularBordersWithNullBorders() throws IOException, IFException { + GraphicsPainter graphicsPainter = mock(GraphicsPainter.class); + BorderProps nullBorderProps = null; + new BorderPainter(graphicsPainter).drawRectangularBorders(new Rectangle(0, 0, 1000, 1000), + nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps); + verifyZeroInteractions(graphicsPainter); + } + + @Test + public void drawRoundedBorders() throws Exception { + test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10)); + test(new DrawRoundedBordersTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester()); + test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10).setCornerRadii(5) + .beforeBorder().setWidth(0).tester()); + test(new DrawRoundedBordersTester(0, 0, 10, 10) + .beforeBorder().setWidth(10).tester().endBorder().setWidth(10).tester()); + test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10)); + test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10) + .beforeBorder().setWidth(5).tester()); + test(new DrawRoundedBordersTester(0, 0, 60, 60).setBorderWidth(4).setCornerRadii(30)); + } + + @Test + public void testDrawRoundedBordersWithNullBorders() throws IOException, IFException { + GraphicsPainter graphicsPainter = mock(GraphicsPainter.class); + BorderProps nullBorderProps = null; + new BorderPainter(graphicsPainter).drawRoundedBorders(new Rectangle(0, 0, 1000, 1000), + nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps); + verifyZeroInteractions(graphicsPainter); + } + + @Test + public void testCalculateCornerCorrectionFactor() { + calculateCornerCorrectionFactorHelper(30000, 500000); + calculateCornerCorrectionFactorHelper(30000, 10000); + } + + private void calculateCornerCorrectionFactorHelper(int radius, int rectWidth) { + BorderProps borderProps = new BorderProps(Constants.EN_SOLID, 4000, radius, radius, Color.BLACK, + BorderProps.Mode.SEPARATE); + int rectHeight = rectWidth + 100; + double expected = (2 * radius > rectWidth) ? (double) rectWidth / (2 * radius) : 1.0; + double actual = BorderPainter.calculateCornerCorrectionFactor(rectWidth, rectHeight, borderProps, + borderProps, borderProps, borderProps); + assertEquals(expected, actual, 0); + } + + private abstract static class BorderPainterTester> { + + protected final Rectangle borderExtent; + + protected BorderProps before; + + protected BorderProps after; + + protected BorderProps start; + + protected BorderProps end; + + protected final GraphicsPainter graphicsPainter; + + protected final BorderPainter sut; + + private final T thisInstance; + + private final BorderPropsBuilder beforeBuilder; + + private final BorderPropsBuilder afterBuilder; + + private final BorderPropsBuilder startBuilder; + + private final BorderPropsBuilder endBuilder; + + public BorderPainterTester(int xOrigin, int yOrigin, int width, int height) { + this.thisInstance = (T) this; + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Cannot test degenerate borders"); + } + beforeBuilder = new BorderPropsBuilder(this.thisInstance); + afterBuilder = new BorderPropsBuilder(this.thisInstance); + startBuilder = new BorderPropsBuilder(this.thisInstance); + endBuilder = new BorderPropsBuilder(this.thisInstance); + this.borderExtent = new Rectangle(xOrigin, yOrigin, width, height); + this.graphicsPainter = mock(GraphicsPainter.class); + this.sut = new BorderPainter(graphicsPainter); + } + + public BorderPropsBuilder beforeBorder() { + return beforeBuilder; + } + + public BorderPropsBuilder afterBorder() { + return afterBuilder; + } + + public BorderPropsBuilder startBorder() { + return startBuilder; + } + + public BorderPropsBuilder endBorder() { + return endBuilder; + } + + public T setBorderWidth(int width) { + beforeBuilder.setWidth(width); + endBuilder.setWidth(width); + afterBuilder.setWidth(width); + startBuilder.setWidth(width); + return thisInstance; + } + + public T setCornerRadii(int radius) { + return setCornerRadii(radius, radius); + } + + public T setCornerRadii(int xRadius, int yRadius) { + setStartBefore(xRadius, yRadius); + setEndBefore(xRadius, yRadius); + setEndAfter(xRadius, yRadius); + setStartAfter(xRadius, yRadius); + return thisInstance; + } + + public T setStartBefore(int xRadius, int yRadius) { + startBuilder.setRadiusStart(xRadius); + beforeBuilder.setRadiusStart(yRadius); + return thisInstance; + } + + public T setEndBefore(int xRadius, int yRadius) { + endBuilder.setRadiusStart(xRadius); + beforeBuilder.setRadiusEnd(yRadius); + return thisInstance; + } + + public T setEndAfter(int xRadius, int yRadius) { + endBuilder.setRadiusEnd(xRadius); + afterBuilder.setRadiusEnd(yRadius); + return thisInstance; + } + + public T setStartAfter(int xRadius, int yRadius) { + startBuilder.setRadiusEnd(xRadius); + afterBuilder.setRadiusStart(yRadius); + return thisInstance; + } + + public final void test() throws IOException { + before = beforeBuilder.build(); + after = afterBuilder.build(); + end = endBuilder.build(); + start = startBuilder.build(); + testMethod(); + } + + protected abstract void testMethod() throws IOException; + + protected static int numberOfNonZeroBorders(BorderProps first, BorderProps... borders) { + int i = first.width == 0 ? 0 : 1; + for (BorderProps borderProp : borders) { + if (borderProp.width > 0) { + i++; + } + } + return i; + } + + protected int numberOfNonZeroBorders() { + return numberOfNonZeroBorders(before, end, after, start); + } + + } + + private static class BorderPropsBuilder> { + + private final int style = 0; + + private final Color color = null; + + private final Mode mode = BorderProps.Mode.SEPARATE; + + private int width; + + private int radiusStart; + + private int radiusEnd; + + private final T tester; + + public BorderPropsBuilder(T tester) { + this.tester = tester; + } + + public T tester() { + return tester; + } + + public BorderPropsBuilder setWidth(int width) { + this.width = width; + return this; + } + + public BorderPropsBuilder setRadiusStart(int radiusStart) { + this.radiusStart = radiusStart; + return this; + } + + public BorderPropsBuilder setRadiusEnd(int radiusEnd) { + this.radiusEnd = radiusEnd; + return this; + } + + public BorderProps build() { + return new BorderProps(style, width, radiusStart, radiusEnd, color, mode); + } + } + + private static final class DrawRectangularBordersTester + extends BorderPainterTester { + + public DrawRectangularBordersTester(int xOrigin, int yOrigin, int width, int height) + throws IOException { + super(xOrigin, yOrigin, width, height); + } + + public DrawRectangularBordersTester setStartBefore(int xRadius, int yRadius) { + return notSupported(); + } + + public DrawRectangularBordersTester setEndBefore(int xRadius, int yRadius) { + return notSupported(); + } + + public DrawRectangularBordersTester setEndAfter(int xRadius, int yRadius) { + return notSupported(); + } + + public DrawRectangularBordersTester setStartAfter(int xRadius, int yRadius) { + return notSupported(); + } + + private DrawRectangularBordersTester notSupported() { + throw new UnsupportedOperationException(); + } + + public void testMethod() throws IOException { + sut.drawRectangularBorders(borderExtent, before, after, start, end); + verifyDrawing(); + } + + private void verifyDrawing() throws IOException { + final int rectX = borderExtent.x; + final int rectY = borderExtent.y; + final int rectWidth = borderExtent.width; + final int rectHeight = borderExtent.height; + if (before.width > 0) { + verify(graphicsPainter).moveTo(rectX, rectY); + verify(graphicsPainter).lineTo(rectWidth, rectY); + verify(graphicsPainter, times(numberOfNonZeroBorders(before, end))) + .lineTo(rectWidth - end.width, rectY + before.width); + verify(graphicsPainter, times(numberOfNonZeroBorders(before, start))) + .lineTo(rectX + start.width, rectY + before.width); + } + if (end.width > 0) { + verify(graphicsPainter).moveTo(rectWidth, rectY); + verify(graphicsPainter).lineTo(rectWidth, rectHeight); + verify(graphicsPainter, times(numberOfNonZeroBorders(end, after))) + .lineTo(rectWidth - end.width, rectHeight - after.width); + verify(graphicsPainter, times(numberOfNonZeroBorders(end, before))) + .lineTo(rectWidth - end.width, rectY + before.width); + } + if (after.width > 0) { + verify(graphicsPainter).moveTo(rectWidth, rectHeight); + verify(graphicsPainter).lineTo(rectX, rectHeight); + verify(graphicsPainter, times(numberOfNonZeroBorders(after, end))) + .lineTo(rectX + start.width, rectHeight - after.width); + verify(graphicsPainter, times(numberOfNonZeroBorders(after, start))) + .lineTo(rectWidth - end.width, rectHeight - after.width); + } + if (start.width > 0) { + verify(graphicsPainter).moveTo(rectX, rectHeight); + verify(graphicsPainter).lineTo(rectX, rectY); + verify(graphicsPainter, times(numberOfNonZeroBorders(start, before))) + .lineTo(rectX + start.width, rectY + before.width); + verify(graphicsPainter, times(numberOfNonZeroBorders(start, after))) + .lineTo(rectX + start.width, rectHeight - after.width); + } + int numBorders = numberOfNonZeroBorders(); + verify(graphicsPainter, times(numBorders)).saveGraphicsState(); + verify(graphicsPainter, times(numBorders)).closePath(); + verify(graphicsPainter, times(numBorders)).restoreGraphicsState(); + verify(graphicsPainter, times(numBorders)).clip(); + + } + } + + private static final class DrawRoundedBordersTester extends BorderPainterTester { + + public DrawRoundedBordersTester(int xOrigin, int yOrigin, int width, int height) throws IOException { + super(xOrigin, yOrigin, width, height); + } + + public void testMethod() throws IOException { + sut.drawRoundedBorders(borderExtent, before, after, start, end); + verifyDrawing(); + } + + private void verifyDrawing() throws IOException { + int numBorders = numberOfNonZeroBorders(); + final int rectWidth = borderExtent.width; + final int rectHeight = borderExtent.height; + if (before.width > 0) { + verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - end.getRadiusStart(), 0); + verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, before.width, + start.getRadiusStart(), before.getRadiusStart()), before.width); + } + if (end.width > 0) { + verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusEnd(), 0); + verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, end.width, + before.getRadiusEnd(), end.getRadiusStart()), end.width); + } + if (after.width > 0) { + verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - start.getRadiusEnd(), 0); + verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, after.width, + start.getRadiusEnd(), after.getRadiusStart()), after.width); + } + if (start.width > 0) { + verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusStart(), 0); + verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, start.width, + before.getRadiusStart(), before.getRadiusStart()), start.width); + } + // verify the drawing of the symmetric rounded corners (the ones that are a quarter of a circle) + // verification is restricted to those since it is too complex in the general case + if (before.width == end.width && before.getRadiusStart() == before.getRadiusEnd() + && end.getRadiusStart() == end.getRadiusEnd() + && before.getRadiusEnd() == end.getRadiusStart() && end.getRadiusStart() > 0) { + verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2, + before.getRadiusStart(), end.getRadiusEnd(), before.getRadiusStart(), + end.getRadiusEnd()); + } + if (end.width == after.width && end.getRadiusStart() == end.getRadiusEnd() + && after.getRadiusStart() == after.getRadiusEnd() + && end.getRadiusEnd() == after.getRadiusStart() && after.getRadiusStart() > 0) { + verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2, + end.getRadiusStart(), after.getRadiusEnd(), end.getRadiusStart(), + after.getRadiusEnd()); + } + if (after.width == start.width && after.getRadiusStart() == after.getRadiusEnd() + && start.getRadiusStart() == start.getRadiusEnd() + && after.getRadiusEnd() == start.getRadiusStart() && start.getRadiusStart() > 0) { + verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2, + after.getRadiusStart(), start.getRadiusEnd(), after.getRadiusStart(), + start.getRadiusEnd()); + } + if (start.width == before.width && start.getRadiusStart() == start.getRadiusEnd() + && before.getRadiusStart() == before.getRadiusEnd() + && start.getRadiusEnd() == before.getRadiusStart() && before.getRadiusStart() > 0) { + verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2, + start.getRadiusStart(), before.getRadiusEnd(), start.getRadiusStart(), + before.getRadiusEnd()); + } + verify(graphicsPainter, times(numBorders)).saveGraphicsState(); + verify(graphicsPainter, times(numBorders)).closePath(); + verify(graphicsPainter, times(numBorders)).restoreGraphicsState(); + verify(graphicsPainter, times(numBorders)).clip(); + } + + private int calcLineEnd(int xWidth, int yWidth, int xRadius, int yRadius) { + return yWidth > yRadius ? yWidth : xWidth > 0 ? Math.max(xRadius, xWidth) : 0; + } + + } + + private static final class ClipBackgroundTester extends BorderPainterTester { + + public ClipBackgroundTester(int xOrigin, int yOrigin, int width, int height) throws IOException { + super(xOrigin, yOrigin, width, height); + } + + public void testMethod() throws IOException { + sut.clipBackground(borderExtent, before, after, start, end); + verifyClipping(); + } + + private void verifyClipping() throws IOException { + int xOrigin = borderExtent.x; + int yOrigin = borderExtent.y; + int xEnd = xOrigin + borderExtent.width; + int yEnd = yOrigin + borderExtent.height; + + Corner startBeforeCorner = Corner.createStartBeforeCorner(getInnerRadiusStart(start), + getInnerRadiusStart(before)); + Corner endBeforeCorner = Corner.createEndBeforeCorner(getInnerRadiusStart(end), getRadiusEnd(before)); + Corner endAfterCorner = Corner.createEndAfterCorner(getRadiusEnd(end), getRadiusEnd(after)); + Corner startAfterCorner = Corner.createStartAfterCorner(getRadiusEnd(start), + getInnerRadiusStart(after)); + verify(graphicsPainter, times(1)).moveTo(xOrigin + startBeforeCorner.xRadius, yOrigin); + verify(graphicsPainter, times(1)).lineTo(xEnd - endBeforeCorner.xRadius, yOrigin); + endBeforeCorner.verifyCornerDrawn(graphicsPainter, xEnd - endBeforeCorner.xRadius, + yOrigin + endBeforeCorner.yRadius); + verify(graphicsPainter, times(1)).lineTo(xEnd, yEnd - endAfterCorner.yRadius); + endAfterCorner.verifyCornerDrawn(graphicsPainter, xEnd - endAfterCorner.xRadius, + yEnd - endAfterCorner.yRadius); + verify(graphicsPainter, times(1)).lineTo(xOrigin + startAfterCorner.xRadius, yEnd); + startAfterCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startAfterCorner.xRadius, + yEnd - startAfterCorner.yRadius); + verify(graphicsPainter, times(1)).lineTo(xOrigin, yOrigin + startBeforeCorner.yRadius); + startBeforeCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startBeforeCorner.xRadius, + yOrigin + startBeforeCorner.yRadius); + verify(graphicsPainter, times(1)).clip(); + } + + private int getInnerRadiusStart(BorderProps borderProps) { + return getInnerRadius(borderProps.getRadiusStart(), borderProps.width); + } + + private int getRadiusEnd(BorderProps borderProps) { + return getInnerRadius(borderProps.getRadiusEnd(), borderProps.width); + } + + private int getInnerRadius(int radius, int borderWidth) { + return Math.max(radius - borderWidth, 0); + } + + private static class Corner { + + public final int xRadius; + + public final int yRadius; + + private final double startAngle; + + private final double endAngle; + + public Corner(int xRadius, int yRadius, double startAngle, double endAngle) { + this.xRadius = xRadius; + this.yRadius = yRadius; + this.startAngle = startAngle; + this.endAngle = endAngle; + } + + public static Corner createStartBeforeCorner(int xRadius, int yRadius) { + return new Corner(xRadius, yRadius, Math.PI, Math.PI * 3 / 2); + } + + public static Corner createEndBeforeCorner(int xRadius, int yRadius) { + return new Corner(xRadius, yRadius, Math.PI * 3 / 2, 0); + } + + public static Corner createEndAfterCorner(int xRadius, int yRadius) { + return new Corner(xRadius, yRadius, 0, Math.PI / 2); + } + + public static Corner createStartAfterCorner(int xRadius, int yRadius) { + return new Corner(xRadius, yRadius, Math.PI / 2, Math.PI); + } + + public void verifyCornerDrawn(GraphicsPainter graphicsPainter, int xCenter, int yCenter) + throws IOException { + if (xRadius != 0 && yRadius != 0) { + verify(graphicsPainter, times(1)).arcTo(startAngle, endAngle, + xCenter, yCenter, xRadius, yRadius); + } else { + verify(graphicsPainter, never()).arcTo(startAngle, endAngle, + xCenter, yCenter, xRadius, yRadius); + } + } + } + } + + +} \ No newline at end of file diff --git a/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java new file mode 100644 index 000000000..4f3a5e628 --- /dev/null +++ b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.fop.pdf.PDFNumber; + +import static org.mockito.Matchers.endsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class PDFGraphicsPainterTestCase { + + private PDFGraphicsPainter sut; + + private PDFContentGenerator generator; + + @Before + public void setup() { + generator = mock(PDFContentGenerator.class); + sut = new PDFGraphicsPainter(generator); + } + + @Test + public void moveTo() { + int x = 10; + int y = 20; + sut.moveTo(x, y); + verify(generator).add(op("m", x, y)); + } + + @Test + public void lineTo() { + int x = 10; + int y = 20; + sut.lineTo(x, y); + verify(generator).add(op("l", x, y)); + } + + @Test + public void arcTo() throws IOException { + int width = 10; + int height = 10; + int x = 0; + int y = 0; + double startAngle = 0; + double endAngle = Math.PI / 2; + sut.arcTo(startAngle, endAngle, x, y, width, height); + //TODO stricter verification + verify(generator).add(endsWith(" c ")); + } + + @Test + public void closePath() { + sut.closePath(); + verify(generator).add(op("h")); + } + + @Test + public void clip() { + sut.clip(); + verify(generator).add(opln("W\nn")); + } + + @Test + public void saveGraphicsState() { + sut.saveGraphicsState(); + verify(generator).add(opln("q")); + } + + @Test + public void restoreGraphicsState() { + sut.restoreGraphicsState(); + verify(generator).add(opln("Q")); + } + + @Test + public void rotateCoordinates() throws IOException { + double angle = 0; + float s = (float) Math.sin(angle); + float c = (float) Math.cos(angle); + sut.rotateCoordinates(angle); + testTransformCoordinatesF(c, s, -s, c, 0, 0); + } + + @Test + public void translateCoordinates() throws IOException { + int x = 10; + int y = 20; + sut.translateCoordinates(x, y); + testTransformCoordinates(1000, 0, 0, 1000, x, y); + } + + @Test + public void scaleCoordinates() throws IOException { + float xScaleFactor = 10f; + float yScaleFactor = 2f; + sut.scaleCoordinates(xScaleFactor, yScaleFactor); + testTransformCoordinatesF(xScaleFactor, 0f, 0f, yScaleFactor, 0f, 0f); + } + + @Test + public void cubicBezierTo() { + int[] args = new int[]{1, 2, 3, 4, 5, 6}; + sut.cubicBezierTo(args[0], args[1], args[2], args[3], args[4], args[5]); + verify(generator).add(op("c", args)); + } + + private void testTransformCoordinatesF(float... args) { + verify(generator).add(opf("cm", args)); + } + + private void testTransformCoordinates(int... args) { + verify(generator).add(op("cm", args)); + } + + private String opf(String op, float... args) { + return opf(op, " ", args); + } + + private String op(String op, int... args) { + return op(op, " ", args); + } + + private String opln(String op, int... args) { + return op(op, "\n", args); + } + + private String opf(String op, String ending, float... args) { + StringBuilder sb = new StringBuilder(); + for (float arg : args) { + sb.append("" + PDFNumber.doubleOut(arg) + " "); + } + return sb.append(op.trim()).append(ending).toString(); + } + + private String op(String op, String ending, int... args) { + float[] formattedArgs = new float[args.length]; + for (int i = 0; i < args.length; i++) { + formattedArgs[i] = format(args[i]); + } + return opf(op, ending, formattedArgs); + } + + private float format(int i) { + return (float) i / 1000; + } + +} diff --git a/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java new file mode 100644 index 000000000..31f2f5c3b --- /dev/null +++ b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf; + +import java.awt.Color; +import java.awt.Rectangle; + +import org.junit.Test; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.endsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.traits.BorderProps; + +public class PDFPainterTestCase { + + @Test + public void testDrawBorderRect() { + // the goal of this test is to check that the drawing of rounded corners in PDF calls + // PDFGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a " c " command is written + // to the PDFContentGenerator + // mock + PDFContentGenerator pdfContentGenerator = mock(PDFContentGenerator.class); + // the next line is commented out because the format() method is static and not possible to mock + // when(PDFContentGenerator.format(anyFloat())).thenReturn("20.0"); + // mock + FOUserAgent foUserAgent = mock(FOUserAgent.class); + when(foUserAgent.isAccessibilityEnabled()).thenReturn(false); + // mock + IFContext ifContext = mock(IFContext.class); + when(ifContext.getUserAgent()).thenReturn(foUserAgent); + // mock + PDFDocumentHandler pdfDocumentHandler = mock(PDFDocumentHandler.class); + when(pdfDocumentHandler.getGenerator()).thenReturn(pdfContentGenerator); + when(pdfDocumentHandler.getContext()).thenReturn(ifContext); + // mock + PDFLogicalStructureHandler pdfLogicalStructureHandler = mock(PDFLogicalStructureHandler.class); + // real, not mock + PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, pdfLogicalStructureHandler); + // build rectangle 200 x 50 (points, which are converted to milipoints) + Rectangle rectangle = new Rectangle(0, 0, 200000, 50000); + // build border properties: width 4pt, radius 30pt + int style = Constants.EN_SOLID; + BorderProps.Mode mode = BorderProps.Mode.SEPARATE; + Color color = Color.BLACK; + int borderWidth = 4000; + int radiusStart = 30000; + int radiusEnd = 30000; + BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + try { + pdfPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE); + // since we cannot mock the PDFContentGenerator.format() static method we have to restrict the + // verification to commands that end with " c ". + verify(pdfContentGenerator, times(16)).add(endsWith(" c ")); + } catch (Exception e) { + fail("something broke..."); + } + } + +} diff --git a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java index 90db3b98f..49ffb77fb 100644 --- a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java +++ b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java @@ -16,6 +16,8 @@ */ package org.apache.fop.render.ps; +import java.awt.Color; +import java.awt.Rectangle; import java.io.IOException; import java.util.Collections; @@ -26,9 +28,13 @@ import org.mockito.verification.VerificationMode; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.Constants; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFState; +import org.apache.fop.traits.BorderProps; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyFloat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -75,4 +81,42 @@ public class PSPainterTestCase { } verify(gen, test).useColor(state.getTextColor()); } + + @Test + public void testDrawBorderRect() { + // the goal of this test is to check that the drawing of rounded corners in PS calls + // PSGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a curveto command is written + // to the PSGenerator + // mock + PSGenerator psGenerator = mock(PSGenerator.class); + when(psGenerator.formatDouble(anyFloat())).thenReturn("20.0"); // simplify! + // mock + PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class); + // mock + PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class); + when(psDocumentHandler.getGenerator()).thenReturn(psGenerator); + when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil); + // real instance, no mock + PSPainter psPainter = new PSPainter(psDocumentHandler); + // build rectangle 200 x 50 (points, which are converted to milipoints) + Rectangle rectangle = new Rectangle(0, 0, 200000, 50000); + // build border properties: width 4pt, radius 30pt + int style = Constants.EN_SOLID; + BorderProps.Mode mode = BorderProps.Mode.SEPARATE; + Color color = Color.BLACK; + int borderWidth = 4000; + int radiusStart = 30000; + int radiusEnd = 30000; + BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + try { + psPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE); + verify(psGenerator, times(16)).writeln("20.0 20.0 20.0 20.0 20.0 20.0 curveto "); + } catch (Exception e) { + fail("something broke..."); + } + } + } diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java index 25227867b..ec93d708e 100644 --- a/test/java/org/apache/fop/traits/BorderPropsTestCase.java +++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java @@ -19,16 +19,17 @@ package org.apache.fop.traits; -import static org.junit.Assert.assertEquals; - import java.awt.Color; +import org.junit.Test; + import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; import org.apache.fop.fo.Constants; import org.apache.fop.util.ColorUtil; -import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * Tests the BorderProps class. @@ -44,23 +45,38 @@ public class BorderPropsTestCase { Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f); //Normalize: Avoid false alarms due to color conversion (rounding) col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col)); - - BorderProps b1 = new BorderProps(Constants.EN_DOUBLE, 1250, - col, BorderProps.COLLAPSE_OUTER); - String ser = b1.toString(); - BorderProps b2 = BorderProps.valueOf(null, ser); - assertEquals(b1, b2); + BorderProps sut = BorderProps.makeRectangular(Constants.EN_DOUBLE, 1250, col, + BorderProps.Mode.COLLAPSE_OUTER); + testSerialization(sut); float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f}; col = DeviceCMYKColorSpace.createCMYKColor(cmyk); //Convert to sRGB with CMYK alternative as constructed by the cmyk() function float[] rgb = col.getRGBColorComponents(null); col = new ColorWithAlternatives(rgb[0], rgb[1], rgb[2], new Color[] {col}); - b1 = new BorderProps(Constants.EN_INSET, 9999, - col, BorderProps.SEPARATE); - ser = b1.toString(); - b2 = BorderProps.valueOf(null, ser); - assertEquals(b1, b2); + sut = BorderProps.makeRectangular(Constants.EN_INSET, 9999, col, BorderProps.Mode.SEPARATE); + testSerialization(sut); + } + + /** + * Test serialization and deserialization to/from String. + * @throws Exception if an error occurs + */ + @Test + public void testSerializationWithCornerRadii() throws Exception { + Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f); + //Normalize: Avoid false alarms due to color conversion (rounding) + col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col)); + for(BorderProps.Mode mode : BorderProps.Mode.values()) { + BorderProps sut = BorderProps.makeRectangular(Constants.EN_SOLID, 10, col, mode); + testSerialization(sut); + sut = new BorderProps(Constants.EN_SOLID, 10, 4, 3, col, mode); + testSerialization(sut); + } + } + + private void testSerialization(BorderProps borderProp) { + assertEquals(borderProp, BorderProps.valueOf(null, borderProp.toString())); } } -- cgit v1.2.3 From b9e74f1261ea539f5f2a95fe0bc6e1dc61a1eb93 Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Tue, 9 Oct 2012 15:33:24 +0000 Subject: Addressed Findbug and Checkstyle issues git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1396073 13f79535-47bb-0310-9956-ffa450edef68 --- findbugs-exclude.xml | 2 +- .../org/apache/fop/afp/AFPResourceManager.java | 1 - src/java/org/apache/fop/fo/Constants.java | 2 +- .../properties/CommonBorderPaddingBackground.java | 1 - .../fop/render/AbstractPathOrientedRenderer.java | 2 +- .../apache/fop/render/afp/AFPDocumentHandler.java | 5 +-- src/java/org/apache/fop/render/afp/AFPPainter.java | 26 +++---------- .../render/intermediate/BezierCurvePainter.java | 2 +- .../fop/render/intermediate/BorderPainter.java | 43 ++++++++++++++++------ .../apache/fop/render/intermediate/IFRenderer.java | 3 +- .../fop/render/java2d/Java2DGraphicsPainter.java | 3 +- .../apache/fop/render/pdf/PDFGraphicsPainter.java | 10 ++--- .../apache/fop/render/ps/PSGraphicsPainter.java | 9 ++--- src/java/org/apache/fop/traits/BorderProps.java | 8 ++-- 14 files changed, 57 insertions(+), 60 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index a764eac41..006a43380 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -4424,7 +4424,7 @@ - + diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index 59dab55c3..99b28cafd 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -242,7 +242,6 @@ public class AFPResourceManager { private void includeObject(AbstractNamedAFPObject namedObj, AFPDataObjectInfo dataObjectInfo) { // create the include object - AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo(); String objectName = namedObj.getName(); AbstractCachedObject cachedObject; diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 722a4ca94..1a2425af5 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -806,7 +806,7 @@ public interface Constants { * numbers. */ int PR_X_NUMBER_CONVERSION_FEATURES = 285; - + /** Scope for table header */ int PR_X_HEADER_COLUMN = 286; diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index 70e74f6d6..eaf678790 100644 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -38,7 +38,6 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; -import org.apache.fop.util.CompareUtil; /** * Stores all common border and padding properties. diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index c16113de3..b87f5c2ca 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -344,7 +344,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { * @param bpsStart the border traits associated with start edge * @param bpsEnd the border traits associated with end edge * @param level of bidirectional embedding - * @param innerBackgroundColor the background color of the block + * @param innerBackgroundColor the background color of the block */ protected void drawBorders( // CSOK: ParameterNumber float startx, float starty, float width, float height, diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 5a0732c84..cd8304b4b 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -27,6 +27,7 @@ import java.net.URI; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.fop.afp.AFPDitheredRectanglePainter; @@ -93,8 +94,6 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler private int roundedCornerCount = 0; - /** Medium Map referenced on previous page **/ - private String lastMediumMap; private static enum Location { ELSEWHERE, IN_DOCUMENT_HEADER, FOLLOWING_PAGE_SEQUENCE, IN_PAGE_HEADER } @@ -419,7 +418,7 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler // Make a unique id StringBuffer idBuilder = new StringBuffer("RC"); - String tmp = Integer.toHexString(roundedCornerCount).toUpperCase(); + String tmp = Integer.toHexString(roundedCornerCount).toUpperCase(Locale.ENGLISH); if (tmp.length() > 6) { //Will never happen //log.error("Rounded corners cache capacity exceeded"); diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 36afbb9e0..3a41e5feb 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -736,16 +736,8 @@ public class AFPPainter extends AbstractIFPainter { 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); - } - + cut.lineTo(startRadius, ((float) startRadius * beforeWidth) / startWidth); + cut.lineTo(startRadius, 0); clip.intersect(new Area(cut)); clip.transform(transform); return clip; @@ -776,16 +768,8 @@ public class AFPPainter extends AbstractIFPainter { 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); - } - + cut.lineTo(startRadius, ((float) startRadius * beforeWidth) / startWidth); + cut.lineTo(startRadius, 0); clip.subtract(new Area(cut)); clip.transform(transform); return clip; @@ -860,7 +844,7 @@ public class AFPPainter extends AbstractIFPainter { hints.put(ImageHandlerUtil.CONVERSION_MODE, ImageHandlerUtil.CONVERSION_MODE_BITMAP); hints.put("TARGET_RESOLUTION", - new Integer(context.getPaintingState().getResolution())); + Integer.valueOf(context.getPaintingState().getResolution())); try { diff --git a/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java index 92eb334e0..dd28dd5c8 100644 --- a/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java +++ b/src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java @@ -34,4 +34,4 @@ public interface BezierCurvePainter { * @throws IOException if an I/O error occurs */ void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) throws IOException; -} \ No newline at end of file +} diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index 51dcf5905..e286590ca 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -33,12 +33,31 @@ public class BorderPainter { /** TODO remove before integration*/ public static final String ROUNDED_CORNERS = "fop.round-corners"; - /** TODO Use a class to model border instead of an array - * convention index of top, bottom, right and left borders */ - protected static final int TOP = 0, RIGHT = 1, BOTTOM = 2, LEFT = 3; - /** TODO Use a class to model border corners instead of an array - convention index of top-left, top-right, bottom-right and bottom-left border corners*/ - protected static final int TOP_LEFT = 0, TOP_RIGHT = 1, BOTTOM_RIGHT = 2, BOTTOM_LEFT = 3; + // TODO Use a class to model border instead of an array + /** Convention index of before top */ + protected static final int TOP = 0; + + /** Convention index of right border */ + protected static final int RIGHT = 1; + + /** Convention index of bottom border */ + protected static final int BOTTOM = 2; + + /** Convention index of left border */ + protected static final int LEFT = 3; + + // TODO Use a class to model border corners instead of an array + /** Convention index of top-left border corners */ + protected static final int TOP_LEFT = 0; + + /** Convention index of top-right-end border corners */ + protected static final int TOP_RIGHT = 1; + + /** Convention index of bottom-right border corners */ + protected static final int BOTTOM_RIGHT = 2; + + /** Convention index of bottom-left border corners */ + protected static final int BOTTOM_LEFT = 3; private final GraphicsPainter graphicsPainter; @@ -227,7 +246,7 @@ public class BorderPainter { int outerx = startx - clipw[LEFT]; int clipx = outerx + clipw[LEFT]; int innerx = outerx + bw[LEFT]; - + saveGraphicsState(); moveTo(clipx, ey1); @@ -281,10 +300,12 @@ public class BorderPainter { final int width = borderRect.width - start.getClippedWidth() - end.getClippedWidth(); final int height = borderRect.height - before.getClippedWidth() - after.getClippedWidth(); //Determine scale factor if any adjacent elliptic corners overlap - double cornerCorrectionFactor = calculateCornerScaleCorrection(width, height, before, after, start, end); + double cornerCorrectionFactor = calculateCornerScaleCorrection(width, height, before, after, start, + end); drawBorderSegment(start, before, end, 0, width, startx, starty, cornerCorrectionFactor); drawBorderSegment(before, end, after, 1, height, startx + width, starty, cornerCorrectionFactor); - drawBorderSegment(end, after, start, 2, width, startx + width, starty + height, cornerCorrectionFactor); + drawBorderSegment(end, after, start, 2, width, startx + width, starty + height, + cornerCorrectionFactor); drawBorderSegment(after, start, before, 3, height, startx, starty + height, cornerCorrectionFactor); } @@ -637,10 +658,10 @@ public class BorderPainter { private static final Corner SQUARE = new Corner(0, 0, null, 0, 0, 0, 0); /** The radius of the elliptic corner in the x direction */ - protected final int radiusX; + private final int radiusX; /** The radius of the elliptic corner in the y direction */ - protected final int radiusY; + private final int radiusY; /** The start and end angles of the corner ellipse */ private final CornerAngles angles; diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 47a53deca..3a22e83aa 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -39,10 +39,11 @@ import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.xml.sax.SAXException; -import org.apache.batik.parser.AWTTransformProducer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.batik.parser.AWTTransformProducer; + import org.apache.xmlgraphics.xmp.Metadata; import org.apache.xmlgraphics.xmp.schemas.DublinCoreAdapter; import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema; diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java index b485daf3d..588d417fa 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java @@ -329,5 +329,4 @@ class Java2DGraphicsPainter implements GraphicsPainter { this.painter.restoreGraphicsState(); this.currentPath = null; } - -} \ No newline at end of file +} diff --git a/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java index ec3073e6f..ff1ffb5ff 100644 --- a/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java @@ -59,6 +59,7 @@ public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { boolean startOrBefore, int style, Color col) { float w = x2 - x1; float h = y2 - y1; + float colFactor; switch (style) { case Constants.EN_DASHED: generator.setColor(col); @@ -133,8 +134,7 @@ public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { break; case Constants.EN_GROOVE: case Constants.EN_RIDGE: - { - float colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); + colFactor = (style == Constants.EN_GROOVE ? 0.4f : -0.4f); generator.setSolidLine(); if (horz) { Color uppercol = ColorUtil.lightenColor(col, -colFactor); @@ -162,11 +162,9 @@ public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { .strokeLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2); } break; - } case Constants.EN_INSET: case Constants.EN_OUTSET: - { - float colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); + colFactor = (style == Constants.EN_OUTSET ? 0.4f : -0.4f); generator.setSolidLine(); Color c = col; if (horz) { @@ -183,7 +181,6 @@ public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { .strokeLine(xm1, y1, xm1, y2); } break; - } case Constants.EN_HIDDEN: break; default: @@ -382,7 +379,6 @@ public class PDFGraphicsPainter implements GraphicsPainter, BezierCurvePainter { String xS = format(xStart); String xE = format(xEnd); String yS = format(yStart); - String yE = format(yEnd); if (style == RuleStyle.GROOVE) { addLine("m", xS, yS) .addLine("l", xE, yS) diff --git a/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java index e9b4b4ff5..ab766f701 100644 --- a/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java +++ b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java @@ -31,7 +31,6 @@ import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.fop.fo.Constants; import org.apache.fop.render.intermediate.ArcToBezierCurveTransformer; import org.apache.fop.render.intermediate.BezierCurvePainter; -import org.apache.fop.render.intermediate.BorderPainter; import org.apache.fop.render.intermediate.GraphicsPainter; import org.apache.fop.traits.RuleStyle; import org.apache.fop.util.ColorUtil; @@ -89,7 +88,7 @@ public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { gen.useColor(col); if (horz) { float unit = Math.abs(2 * h); - int rep = (int)(w / unit); + int rep = (int) (w / unit); if (rep % 2 == 0) { rep++; } @@ -101,7 +100,7 @@ public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { drawLine(gen, x1, ym, x2, ym); } else { float unit = Math.abs(2 * w); - int rep = (int)(h / unit); + int rep = (int) (h / unit); if (rep % 2 == 0) { rep++; } @@ -118,7 +117,7 @@ public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { gen.useLineCap(1); //Rounded! if (horz) { float unit = Math.abs(2 * h); - int rep = (int)(w / unit); + int rep = (int) (w / unit); if (rep % 2 == 0) { rep++; } @@ -129,7 +128,7 @@ public class PSGraphicsPainter implements GraphicsPainter, BezierCurvePainter { drawLine(gen, x1, ym, x2, ym); } else { float unit = Math.abs(2 * w); - int rep = (int)(h / unit); + int rep = (int) (h / unit); if (rep % 2 == 0) { rep++; } diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 61a7d86b6..e274517d9 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -34,7 +34,7 @@ import org.apache.fop.util.ColorUtil; */ public class BorderProps implements Serializable { - private static final long serialVersionUID = 7053576586478548795L; + private static final long serialVersionUID = 8022237892391068187L; public enum Mode { SEPARATE("separate") { @@ -155,7 +155,7 @@ public class BorderProps implements Serializable { return true; } else { if (obj instanceof BorderProps) { - BorderProps other = (BorderProps)obj; + BorderProps other = (BorderProps) obj; return (style == other.style) && org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor( color, other.color) @@ -203,7 +203,7 @@ public class BorderProps implements Serializable { return sbuf.toString(); } - private static class BorderPropsDeserializer { + private static final class BorderPropsDeserializer { private static final BorderPropsDeserializer INSTANCE = new BorderPropsDeserializer(); @@ -223,7 +223,7 @@ public class BorderProps implements Serializable { m.find(); int width = Integer.parseInt(m.group()); Mode mode = Mode.SEPARATE; - if ( m.find()) { + if (m.find()) { String ms = m.group(); if (Mode.COLLAPSE_INNER.value.equalsIgnoreCase(ms)) { mode = Mode.COLLAPSE_INNER; -- cgit v1.2.3 From 3f8838ef97cc1fc9799be4d1be2ac3a86dae596d Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Mon, 22 Oct 2012 14:06:43 +0000 Subject: Support for relative border-*-*-radius fox properties Plus cleanup git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1400883 13f79535-47bb-0310-9956-ffa450edef68 --- examples/fo/advanced/rounded-corners.fo | 28 ++++---- src/java/org/apache/fop/fo/Constants.java | 26 ++++--- src/java/org/apache/fop/fo/FOPropertyMapping.java | 79 +++++++++++++++------- .../fop/fo/extensions/ExtensionElementMapping.java | 7 +- .../properties/BoxCornerPropShorthandParser.java | 2 +- .../properties/CommonBorderPaddingBackground.java | 23 +++---- src/java/org/apache/fop/layoutmgr/TraitSetter.java | 16 +++-- .../fop/render/intermediate/BorderPainter.java | 15 ++-- .../apache/fop/render/pdf/PDFPainterTestCase.java | 45 ++++-------- .../apache/fop/render/ps/PSPainterTestCase.java | 18 +---- 10 files changed, 134 insertions(+), 125 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/examples/fo/advanced/rounded-corners.fo b/examples/fo/advanced/rounded-corners.fo index d8df05922..7b275ae17 100644 --- a/examples/fo/advanced/rounded-corners.fo +++ b/examples/fo/advanced/rounded-corners.fo @@ -12,7 +12,7 @@ Examples using Rounded Corners - The "border-radius" property in the http://xmlgraphics.apache.org/fop/extensions namespace is a shorthand property for setting appropriate values for "border-*-radius-*" (see below), and is used to specify round corners on block areas. The property takes one or two values: + The "border-radius" property in the http://xmlgraphics.apache.org/fop/extensions namespace is a shorthand property for setting appropriate values for "border-*-radius" (see below), and is used to specify round corners on block areas. The property takes one or two values: Circular corners are specified when 'border-radius' is set to a single value. This value is the radius of the outer quadrant of the corner. @@ -21,20 +21,20 @@ - To specify the corner radii of two adjacent border segments, A and B, the properties border-A-radius-B and border-B-radius-A are set. The value border-A-radius-B is the radius of the A-B corner in the direction perpendicular to segment A. - + The radii for a specific corner can be specified for each CORNER (in 'before-start', 'before-end', 'after-start', 'after-end') with the property "border-CORNER-radius". These properties + have the same interpretation as the "border-radius" property. - + - + @@ -53,14 +53,14 @@ - + I - + II @@ -76,39 +76,39 @@ - The border segments join at a line that coincides with the one that interesects the inner and outer corners of the equivalent rectangular corner. This is clearly demonstrated below where rounded borders are overlayed on top of rectangular borders. + The border segments join at a line that coincides with the one that intersects the inner and outer corners of the equivalent rectangular corner. This is clearly demonstrated below where rounded borders are overlayed on top of rectangular borders. - + - + - + - + - + - + diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 1a2425af5..7a83a12f8 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -781,37 +781,43 @@ public interface Constants { /** 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; + int PR_X_BORDER_START_RADIUS_BEFORE = 280; /** Property constant FOP proprietary*/ - int PR_X_BORDER_START_RADIUS_END = 281; + int PR_X_BORDER_START_RADIUS_AFTER = 281; /** Property constant FOP proprietary*/ - int PR_X_BORDER_END_RADIUS_START = 282; + int PR_X_BORDER_END_RADIUS_BEFORE = 282; /** Property constant FOP proprietary*/ - int PR_X_BORDER_END_RADIUS_END = 283; + int PR_X_BORDER_END_RADIUS_AFTER = 283; /** Property constant FOP proprietary*/ int PR_X_BORDER_RADIUS = 284; + /** Property constant FOP proprietary*/ + int PR_X_BORDER_BEFORE_START_RADIUS = 285; + /** Property constant FOP proprietary*/ + int PR_X_BORDER_BEFORE_END_RADIUS = 286; + /** Property constant FOP proprietary*/ + int PR_X_BORDER_AFTER_START_RADIUS = 287; + /** Property constant FOP proprietary*/ + int PR_X_BORDER_AFTER_END_RADIUS = 288; + /** * Property constant - FOP proprietary extension (see NumberConverter) used * to perform additional control over number conversion when generating page * numbers. */ - int PR_X_NUMBER_CONVERSION_FEATURES = 285; + int PR_X_NUMBER_CONVERSION_FEATURES = 289; /** Scope for table header */ - int PR_X_HEADER_COLUMN = 286; + int PR_X_HEADER_COLUMN = 290; /** Number of property constants defined */ - int PROPERTY_COUNT = 286; + int PROPERTY_COUNT = 290; // compound property constants diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 08afbcd52..3ae81c62a 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -206,12 +206,11 @@ public final class FOPropertyMapping implements Constants { genericBorderStyle.setDefault("none"); // GenericCondCornerRadius - genericCondCornerRadius = new CondLengthProperty.Maker(0); + 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); @@ -2617,53 +2616,59 @@ public final class FOPropertyMapping implements Constants { // fox:border-*-radius-* - - // border-before-radius-start - m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START); + m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_BEFORE_START_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_END); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_BEFORE_END_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_START); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_AFTER_START_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_END); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_AFTER_END_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_BEFORE); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_BEFORE_START_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_AFTER); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_AFTER_START_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_BEFORE); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_BEFORE_END_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); 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 = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_AFTER); m.useGeneric(genericCondCornerRadius); + m.addShorthand(generics[PR_X_BORDER_AFTER_END_RADIUS]); + m.addShorthand(generics[PR_X_BORDER_RADIUS]); m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard"); addPropertyMaker("fox:border-end-radius-after", m); @@ -2793,14 +2798,14 @@ public final class FOPropertyMapping implements Constants { addPropertyMaker("border-right", m); // border-style - m = new ListProperty.Maker(PR_BORDER_STYLE); + m = new ListProperty.Maker(PR_BORDER_STYLE); m.setInherited(false); m.setDefault(""); m.setDatatypeParser(new BoxPropShorthandParser()); addPropertyMaker("border-style", m); // border-spacing - m = new ListProperty.Maker(PR_BORDER_SPACING); + m = new ListProperty.Maker(PR_BORDER_SPACING); m.setInherited(true); m.setDefault("0pt"); m.setDatatypeParser(new BorderSpacingShorthandParser()); @@ -2821,12 +2826,40 @@ public final class FOPropertyMapping implements Constants { addPropertyMaker("border-width", m); // fox:border-radius - m = new ListProperty.Maker(PR_X_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); + // fox:border-before-start-radius + m = new ListProperty.Maker(PR_X_BORDER_BEFORE_START_RADIUS); + m.setInherited(false); + m.setDatatypeParser(new BoxCornerPropShorthandParser()); + m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); + addPropertyMaker("fox:border-before-start-radius", m); + + // fox:border-before-end-radius + m = new ListProperty.Maker(PR_X_BORDER_BEFORE_END_RADIUS); + m.setInherited(false); + m.setDatatypeParser(new BoxCornerPropShorthandParser()); + m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); + addPropertyMaker("fox:border-before-end-radius", m); + + // fox:border-after-start-radius + m = new ListProperty.Maker(PR_X_BORDER_AFTER_START_RADIUS); + m.setInherited(false); + m.setDatatypeParser(new BoxCornerPropShorthandParser()); + m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); + addPropertyMaker("fox:border-after-start-radius", m); + + // fox:border-after-end-radius + m = new ListProperty.Maker(PR_X_BORDER_AFTER_END_RADIUS); + m.setInherited(false); + m.setDatatypeParser(new BoxCornerPropShorthandParser()); + m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); + addPropertyMaker("fox:border-after-end-radius", m); + // cue m = new ToBeImplementedProperty.Maker(PR_CUE); m.setInherited(false); diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 076691cb4..09b47f02a 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -40,8 +40,7 @@ public class ExtensionElementMapping extends ElementMapping { /** The standard XML prefix for elements and attributes in this namespace. */ public static final String STANDARD_PREFIX = "fox"; - private static final Set PROPERTY_ATTRIBUTES - = new java.util.HashSet(); + private static final Set PROPERTY_ATTRIBUTES = new java.util.HashSet(); static { //These are FOP's standard extension properties (fox:*) @@ -63,6 +62,10 @@ public class ExtensionElementMapping extends ElementMapping { PROPERTY_ATTRIBUTES.add("border-end-radius-before"); PROPERTY_ATTRIBUTES.add("border-end-radius-after"); PROPERTY_ATTRIBUTES.add("border-radius"); + PROPERTY_ATTRIBUTES.add("border-before-start-radius"); + PROPERTY_ATTRIBUTES.add("border-before-end-radius"); + PROPERTY_ATTRIBUTES.add("border-after-start-radius"); + PROPERTY_ATTRIBUTES.add("border-after-end-radius"); } /** diff --git a/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java b/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java index d3e37a7c9..06f198573 100644 --- a/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java +++ b/src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java @@ -39,8 +39,8 @@ public class BoxCornerPropShorthandParser extends GenericShorthandParser { * 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) */ + @Override protected Property convertValueForProperty(int propId, Property property, PropertyMaker maker, diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index eaf678790..61c7b95cd 100644 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -372,15 +372,15 @@ public class CommonBorderPaddingBackground { Constants.PR_BORDER_START_STYLE, Constants.PR_BORDER_START_WIDTH, Constants.PR_PADDING_START, - Constants.PR_X_BORDER_START_RADIUS_START, - Constants.PR_X_BORDER_START_RADIUS_END); + Constants.PR_X_BORDER_START_RADIUS_BEFORE, + Constants.PR_X_BORDER_START_RADIUS_AFTER); initBorderInfo(pList, END, Constants.PR_BORDER_END_COLOR, Constants.PR_BORDER_END_STYLE, Constants.PR_BORDER_END_WIDTH, Constants.PR_PADDING_END, - Constants.PR_X_BORDER_END_RADIUS_START, - Constants.PR_X_BORDER_END_RADIUS_END); + Constants.PR_X_BORDER_END_RADIUS_BEFORE, + Constants.PR_X_BORDER_END_RADIUS_AFTER); } @@ -452,15 +452,12 @@ public class CommonBorderPaddingBackground { 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) { - FOUserAgent ua = pList.getFObj().getUserAgent(); - setBorderInfo(BorderInfo.getInstance(style, - pList.get(widthProp).getCondLength(), - pList.get(colorProp).getColor(ua), - pList.get(radiusStartProp).getCondLength(), - pList.get(radiusEndProp).getCondLength()), side); - // } - + FOUserAgent ua = pList.getFObj().getUserAgent(); + setBorderInfo(BorderInfo.getInstance(style, + pList.get(widthProp).getCondLength(), + pList.get(colorProp).getColor(ua), + pList.get(radiusStartProp).getCondLength(), + pList.get(radiusEndProp).getCondLength()), side); } diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index b96283a4e..c4aac1d8c 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -104,14 +104,16 @@ public final class TraitSetter { */ private static void addBorderTrait(Area area, CommonBorderPaddingBackground bpProps, - boolean bDiscard, int iSide, BorderProps.Mode mode, + + boolean discard, int side, BorderProps.Mode mode, Integer traitCode, PercentBaseContext context) { - int iBP = bpProps.getBorderWidth(iSide, bDiscard); - int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context); - int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context); - if (iBP > 0 || radiusStart > 0 || radiusEnd > 0) { - area.addTrait(traitCode, new BorderProps(bpProps.getBorderStyle(iSide), iBP, radiusStart, radiusEnd, - bpProps.getBorderColor(iSide), mode)); + int width = bpProps.getBorderWidth(side, discard); + int radiusStart = bpProps.getBorderRadiusStart(side, discard, context); + int radiusEnd = bpProps.getBorderRadiusEnd(side, discard, context); + if (width > 0 || radiusStart > 0 || radiusEnd > 0) { + area.addTrait(traitCode, new BorderProps(bpProps.getBorderStyle(side), width, radiusStart, + radiusEnd, bpProps.getBorderColor(side), mode)); + } } diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java index e286590ca..19e30b50f 100644 --- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java +++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java @@ -30,9 +30,6 @@ import org.apache.fop.traits.BorderProps; */ public class BorderPainter { - /** TODO remove before integration*/ - public static final String ROUNDED_CORNERS = "fop.round-corners"; - // TODO Use a class to model border instead of an array /** Convention index of before top */ protected static final int TOP = 0; @@ -318,10 +315,10 @@ public class BorderPainter { final int ex2 = ex1 - end.getWidth() + end.getClippedWidth(); final int outery = -before.getClippedWidth(); final int innery = outery + before.getWidth(); - final int ellipseSBRadiusX = (int) (cornerCorrectionFactor * start.getRadiusEnd()); - final int ellipseSBRadiusY = (int) (cornerCorrectionFactor * before.getRadiusStart()); - final int ellipseBERadiusX = (int) (cornerCorrectionFactor * end.getRadiusStart()); - final int ellipseBERadiusY = (int) (cornerCorrectionFactor * before.getRadiusEnd()); + final int ellipseSBRadiusX = correctRadius(cornerCorrectionFactor, start.getRadiusEnd()); + final int ellipseSBRadiusY = correctRadius(cornerCorrectionFactor, before.getRadiusStart()); + final int ellipseBERadiusX = correctRadius(cornerCorrectionFactor, end.getRadiusStart()); + final int ellipseBERadiusY = correctRadius(cornerCorrectionFactor, before.getRadiusEnd()); saveGraphicsState(); translateCoordinates(x, y); if (orientation != 0) { @@ -406,6 +403,10 @@ public class BorderPainter { } } + private static int correctRadius(double cornerCorrectionFactor, int radius) { + return (int) (Math.round(cornerCorrectionFactor * radius)); + } + private static BorderSegment borderSegmentForBefore(BorderProps before) { return AbstractBorderSegment.asBorderSegment(before); } diff --git a/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java index 31f2f5c3b..f431b3ee9 100644 --- a/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java +++ b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java @@ -24,64 +24,43 @@ import java.awt.Rectangle; import org.junit.Test; -import static org.junit.Assert.fail; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.Constants; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.traits.BorderProps; + import static org.mockito.Matchers.endsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.fo.Constants; -import org.apache.fop.render.intermediate.IFContext; -import org.apache.fop.traits.BorderProps; - public class PDFPainterTestCase { @Test - public void testDrawBorderRect() { + public void testDrawBorderRect() throws Exception { // the goal of this test is to check that the drawing of rounded corners in PDF calls // PDFGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a " c " command is written // to the PDFContentGenerator - // mock PDFContentGenerator pdfContentGenerator = mock(PDFContentGenerator.class); - // the next line is commented out because the format() method is static and not possible to mock - // when(PDFContentGenerator.format(anyFloat())).thenReturn("20.0"); - // mock FOUserAgent foUserAgent = mock(FOUserAgent.class); when(foUserAgent.isAccessibilityEnabled()).thenReturn(false); - // mock IFContext ifContext = mock(IFContext.class); when(ifContext.getUserAgent()).thenReturn(foUserAgent); - // mock PDFDocumentHandler pdfDocumentHandler = mock(PDFDocumentHandler.class); when(pdfDocumentHandler.getGenerator()).thenReturn(pdfContentGenerator); when(pdfDocumentHandler.getContext()).thenReturn(ifContext); - // mock PDFLogicalStructureHandler pdfLogicalStructureHandler = mock(PDFLogicalStructureHandler.class); - // real, not mock PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, pdfLogicalStructureHandler); // build rectangle 200 x 50 (points, which are converted to milipoints) Rectangle rectangle = new Rectangle(0, 0, 200000, 50000); // build border properties: width 4pt, radius 30pt - int style = Constants.EN_SOLID; - BorderProps.Mode mode = BorderProps.Mode.SEPARATE; - Color color = Color.BLACK; - int borderWidth = 4000; - int radiusStart = 30000; - int radiusEnd = 30000; - BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - try { - pdfPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE); - // since we cannot mock the PDFContentGenerator.format() static method we have to restrict the - // verification to commands that end with " c ". - verify(pdfContentGenerator, times(16)).add(endsWith(" c ")); - } catch (Exception e) { - fail("something broke..."); - } + BorderProps border = new BorderProps(Constants.EN_SOLID, 4000, 30000, 30000, Color.BLACK, + BorderProps.Mode.SEPARATE); + pdfPainter.drawBorderRect(rectangle, border, border, border, border, Color.WHITE); + // since we cannot mock the PDFContentGenerator.format() static method we have to restrict the + // verification to commands that end with " c ". + verify(pdfContentGenerator, times(16)).add(endsWith(" c ")); } } diff --git a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java index 7fac2b584..0a9f552ff 100644 --- a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java +++ b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java @@ -87,32 +87,20 @@ public class PSPainterTestCase { // the goal of this test is to check that the drawing of rounded corners in PS calls // PSGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a curveto command is written // to the PSGenerator - // mock PSGenerator psGenerator = mock(PSGenerator.class); when(psGenerator.formatDouble(anyFloat())).thenReturn("20.0"); // simplify! - // mock PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class); - // mock PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class); when(psDocumentHandler.getGenerator()).thenReturn(psGenerator); when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil); - // real instance, no mock PSPainter psPainter = new PSPainter(psDocumentHandler); // build rectangle 200 x 50 (points, which are converted to milipoints) Rectangle rectangle = new Rectangle(0, 0, 200000, 50000); // build border properties: width 4pt, radius 30pt - int style = Constants.EN_SOLID; - BorderProps.Mode mode = BorderProps.Mode.SEPARATE; - Color color = Color.BLACK; - int borderWidth = 4000; - int radiusStart = 30000; - int radiusEnd = 30000; - BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); - BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode); + BorderProps border = new BorderProps(Constants.EN_SOLID, 4000, 30000, 30000, Color.BLACK, + BorderProps.Mode.SEPARATE); try { - psPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE); + psPainter.drawBorderRect(rectangle, border, border, border, border, Color.WHITE); verify(psGenerator, times(16)).writeln("20.0 20.0 20.0 20.0 20.0 20.0 curveto "); } catch (Exception e) { fail("something broke..."); -- cgit v1.2.3