aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop
diff options
context:
space:
mode:
authorPeter Hancock <phancock@apache.org>2012-10-24 10:20:17 +0000
committerPeter Hancock <phancock@apache.org>2012-10-24 10:20:17 +0000
commit7e09a8037fc79d14550c301ea039f9163a735b19 (patch)
treed9b85d984b258e3a7b1e4b882ed3c90f7c550a2b /src/java/org/apache/fop
parentf17dbea7f8210e18ef5e9f103a684637ac36a916 (diff)
parent5de50e85027df695f0d3d9356f7f684ba464ed1d (diff)
downloadxmlgraphics-fop-7e09a8037fc79d14550c301ea039f9163a735b19.tar.gz
xmlgraphics-fop-7e09a8037fc79d14550c301ea039f9163a735b19.zip
Added support for Rounded Corners (see Bugzilla 54041).
Contributions made by PH, JM and LB git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1401607 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop')
-rw-r--r--src/java/org/apache/fop/afp/AFPResourceManager.java158
-rw-r--r--src/java/org/apache/fop/area/Trait.java1
-rw-r--r--src/java/org/apache/fop/fo/Constants.java34
-rw-r--r--src/java/org/apache/fop/fo/FOPropertyMapping.java107
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java17
-rw-r--r--src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java66
-rw-r--r--src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java177
-rw-r--r--src/java/org/apache/fop/layoutmgr/TraitSetter.java63
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java4
-rw-r--r--src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java87
-rw-r--r--src/java/org/apache/fop/render/afp/AFPDocumentHandler.java52
-rw-r--r--src/java/org/apache/fop/render/afp/AFPImageHandler.java52
-rw-r--r--src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java5
-rw-r--r--src/java/org/apache/fop/render/afp/AFPImageHandlerRawJPEG.java4
-rw-r--r--src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java5
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java660
-rw-r--r--src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java6
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java8
-rw-r--r--src/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformer.java102
-rw-r--r--src/java/org/apache/fop/render/intermediate/BezierCurvePainter.java37
-rw-r--r--src/java/org/apache/fop/render/intermediate/BorderPainter.java873
-rw-r--r--src/java/org/apache/fop/render/intermediate/GraphicsPainter.java145
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java31
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java43
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java32
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java74
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java318
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java332
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java31
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java12
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFBorderPainter.java331
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java495
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java40
-rw-r--r--src/java/org/apache/fop/render/ps/PSBorderPainter.java333
-rw-r--r--src/java/org/apache/fop/render/ps/PSGraphicsPainter.java386
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java31
-rw-r--r--src/java/org/apache/fop/traits/BorderProps.java202
37 files changed, 3926 insertions, 1428 deletions
diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java
index 2e5f78043..99b28cafd 100644
--- a/src/java/org/apache/fop/afp/AFPResourceManager.java
+++ b/src/java/org/apache/fop/afp/AFPResourceManager.java
@@ -67,14 +67,9 @@ public class AFPResourceManager {
/** Maintain a reference count of instream objects for referencing purposes */
private int instreamObjectCount = 0;
- /** a mapping of resourceInfo --> include name */
- private final Map<AFPResourceInfo, String> includeNameMap
- = new java.util.HashMap<AFPResourceInfo, String>();
-
- /** a mapping of resourceInfo --> page segment name */
- private Map<AFPResourceInfo, String> pageSegmentMap
- = new java.util.HashMap<AFPResourceInfo, String>();
-
+ /** Mapping of resourceInfo to AbstractCachedObject */
+ private final Map<AFPResourceInfo, AbstractCachedObject> includeObjectCache
+ = new java.util.HashMap<AFPResourceInfo, AbstractCachedObject>();
private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults();
/**
@@ -140,21 +135,7 @@ public class AFPResourceManager {
public boolean tryIncludeObject(AFPDataObjectInfo dataObjectInfo) throws IOException {
AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
updateResourceInfoUri(resourceInfo);
-
- String objectName = 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 true;
- }
-
- objectName = pageSegmentMap.get(resourceInfo);
- if (objectName != null) {
- // an existing data resource so reference it by adding an include to the current page
- includePageSegment(dataObjectInfo, objectName);
- return true;
- }
- return false;
+ return includeCachedObject(resourceInfo, dataObjectInfo.getObjectAreaInfo());
}
/**
@@ -195,7 +176,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()) {
@@ -213,23 +194,108 @@ public class AFPResourceManager {
// add data object into its resource group destination
resourceGroup.addObject(namedObj);
-
- // create the include object
- String 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
+ 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) {
@@ -316,8 +382,9 @@ public class AFPResourceManager {
resourceInfo.setName(resourceName);
resourceInfo.setUri(uri.toASCIIString());
- String objectName = includeNameMap.get(resourceInfo);
- if (objectName == null) {
+ AbstractCachedObject cachedObject = (AbstractCachedObject)
+ includeObjectCache.get(resourceInfo);
+ if (cachedObject == null) {
if (log.isDebugEnabled()) {
log.debug("Adding included resource: " + resourceName);
}
@@ -330,8 +397,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
}
@@ -354,9 +425,8 @@ public class AFPResourceManager {
resourceInfo.setName(resourceName);
resourceInfo.setUri(uri.toASCIIString());
- String resource = includeNameMap.get(resourceInfo);
- if (resource == null) {
-
+ AbstractCachedObject cachedObject = (AbstractCachedObject) includeObjectCache.get(resourceInfo);
+ if (cachedObject == null) {
ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
//resourceObject delegates write commands to copyNamedResource()
@@ -382,11 +452,9 @@ public class AFPResourceManager {
@Override
protected void writeEnd(OutputStream os) throws IOException { }
};
-
resourceGroup.addObject(resourceObject);
-
- includeNameMap.put(resourceInfo, resourceName);
-
+ cachedObject = new CachedObject(resourceName, null);
+ includeObjectCache.put(resourceInfo, cachedObject);
}
}
diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java
index 65320fd48..cd0d4becf 100644
--- a/src/java/org/apache/fop/area/Trait.java
+++ b/src/java/org/apache/fop/area/Trait.java
@@ -169,6 +169,7 @@ public final class Trait implements Serializable {
/** shift direction trait */
public static final Integer SHIFT_DIRECTION = 42;
+
/** Maximum value used by trait keys */
public static final int MAX_TRAIT_KEY = 42;
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java
index 1f098fe05..7a83a12f8 100644
--- a/src/java/org/apache/fop/fo/Constants.java
+++ b/src/java/org/apache/fop/fo/Constants.java
@@ -778,18 +778,46 @@ public interface Constants {
int PR_X_ALT_TEXT = 274;
/** 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;
+ /** 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_BEFORE = 280;
+ /** Property constant FOP proprietary*/
+ int PR_X_BORDER_START_RADIUS_AFTER = 281;
+ /** Property constant FOP proprietary*/
+ int PR_X_BORDER_END_RADIUS_BEFORE = 282;
+ /** Property constant FOP proprietary*/
+ 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 = 276;
+ int PR_X_NUMBER_CONVERSION_FEATURES = 289;
/** Scope for table header */
- int PR_X_HEADER_COLUMN = 277;
+ int PR_X_HEADER_COLUMN = 290;
/** Number of property constants defined */
- int PROPERTY_COUNT = 277;
+ 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 6f77efc93..3ae81c62a 100644
--- a/src/java/org/apache/fop/fo/FOPropertyMapping.java
+++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java
@@ -30,6 +30,7 @@ import org.apache.fop.fo.pagination.Flow;
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;
@@ -99,6 +100,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;
@@ -203,6 +205,13 @@ 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);
+
// GenericBreak
genericBreak = new EnumProperty.Maker(0);
genericBreak.setInherited(false);
@@ -2605,6 +2614,65 @@ public final class FOPropertyMapping implements Constants {
m.setDefault("");
addPropertyMaker("fox:alt-text", m);
+
+ // fox:border-*-radius-*
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+
// provisional-label-separation
m = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION);
m.setInherited(true);
@@ -2730,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());
@@ -2757,6 +2825,41 @@ 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);
+
+ // 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 a040edf1a..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<String> PROPERTY_ATTRIBUTES
- = new java.util.HashSet<String>();
+ private static final Set<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>();
static {
//These are FOP's standard extension properties (fox:*)
@@ -53,6 +52,20 @@ public class ExtensionElementMapping extends ElementMapping {
//These are FOP's extension properties for accessibility
PROPERTY_ATTRIBUTES.add("alt-text");
PROPERTY_ATTRIBUTES.add("header");
+ //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");
+ 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
new file mode 100644
index 000000000..06f198573
--- /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}
+ */
+ @Override
+ 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 85a17ccd6..61c7b95cd 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.
@@ -98,6 +97,8 @@ public class CommonBorderPaddingBackground {
/** the "end" edge */
public static final int END = 3;
+
+
/**
* Utility class to express border info.
*/
@@ -110,32 +111,40 @@ public class CommonBorderPaddingBackground {
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;
@@ -170,6 +179,20 @@ public class CommonBorderPaddingBackground {
}
}
+ /**
+ * @return the border-*-start-radius
+ */
+ public CondLengthProperty getRadiusStart() {
+ return this.radiusStart;
+ }
+
+ /**
+ * @return the border-*-end-radius
+ */
+ public CondLengthProperty getRadiusEnd() {
+ return this.radiusEnd;
+ }
+
@Override
public String toString() {
StringBuffer sb = new StringBuffer("BorderInfo");
@@ -179,6 +202,10 @@ public class CommonBorderPaddingBackground {
sb.append(mColor);
sb.append(", ");
sb.append(mWidth);
+ sb.append(", ");
+ sb.append(radiusStart);
+ sb.append(", ");
+ sb.append(radiusEnd);
sb.append("}");
return sb.toString();
}
@@ -191,10 +218,12 @@ public class CommonBorderPaddingBackground {
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
@@ -204,18 +233,24 @@ public class CommonBorderPaddingBackground {
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);
+ 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
@@ -292,8 +327,11 @@ public class CommonBorderPaddingBackground {
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 +357,30 @@ public class CommonBorderPaddingBackground {
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_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_PADDING_END,
+ Constants.PR_X_BORDER_END_RADIUS_BEFORE,
+ Constants.PR_X_BORDER_END_RADIUS_AFTER);
}
@@ -346,8 +392,7 @@ public class CommonBorderPaddingBackground {
* @return a CommonBorderPaddingBackground instance (cached if possible)
* @throws PropertyException in case of an error
*/
- public static CommonBorderPaddingBackground getInstance(PropertyList pList)
- throws PropertyException {
+ public static CommonBorderPaddingBackground getInstance(PropertyList pList) throws PropertyException {
CommonBorderPaddingBackground newInstance = new CommonBorderPaddingBackground(pList);
CommonBorderPaddingBackground cachedInstance = null;
/* if padding-* and background-position-* resolve to absolute lengths
@@ -400,21 +445,23 @@ public class CommonBorderPaddingBackground {
}
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) {
- FOUserAgent ua = pList.getFObj().getUserAgent();
- setBorderInfo(BorderInfo.getInstance(style,
+ FOUserAgent ua = pList.getFObj().getUserAgent();
+ setBorderInfo(BorderInfo.getInstance(style,
pList.get(widthProp).getCondLength(),
- pList.get(colorProp).getColor(ua)), side);
- }
-
+ pList.get(colorProp).getColor(ua),
+ pList.get(radiusStartProp).getCondLength(),
+ pList.get(radiusEndProp).getCondLength()), side);
}
+
+
/**
* Sets a border.
* @param info the border information
@@ -538,6 +585,40 @@ public class CommonBorderPaddingBackground {
}
/**
+ * 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
*
* @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
@@ -601,9 +682,9 @@ public class CommonBorderPaddingBackground {
*/
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);
}
/**
@@ -615,18 +696,18 @@ public class CommonBorderPaddingBackground {
*/
public int getBPPaddingAndBorder(boolean discard, PercentBaseContext context) {
return getPaddingBefore(discard, context) + getPaddingAfter(discard, context)
- + getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
+ + getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
}
@Override
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";
}
/**
@@ -734,19 +815,19 @@ public class CommonBorderPaddingBackground {
if (this == obj) {
return true;
}
- if (!(obj instanceof CommonBorderPaddingBackground)) {
+ 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
+ && Arrays.equals(borderInfo, cbpb.borderInfo)
+ && Arrays.equals(padding, cbpb.padding));
+ } else {
return false;
}
-
- CommonBorderPaddingBackground other = (CommonBorderPaddingBackground) obj;
- return backgroundAttachment == other.backgroundAttachment
- && CompareUtil.equal(backgroundColor, other.backgroundColor)
- && CompareUtil.equal(backgroundImage, other.backgroundImage)
- && CompareUtil.equal(backgroundPositionHorizontal, backgroundPositionHorizontal)
- && CompareUtil.equal(backgroundPositionVertical, other.backgroundPositionVertical)
- && backgroundRepeat == other.backgroundRepeat
- && Arrays.equals(borderInfo, other.borderInfo)
- && Arrays.equals(padding, other.padding);
}
@Override
diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
index 0ae499478..c4aac1d8c 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);
+ BorderProps.Mode.SEPARATE, Trait.BORDER_START, context);
addBorderTrait(area, bpProps, isNotLast,
CommonBorderPaddingBackground.END,
- BorderProps.SEPARATE, Trait.BORDER_END);
+ BorderProps.Mode.SEPARATE, Trait.BORDER_END, context);
addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.BEFORE,
- BorderProps.SEPARATE, Trait.BORDER_BEFORE);
+ BorderProps.Mode.SEPARATE, Trait.BORDER_BEFORE, context);
addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.AFTER,
- BorderProps.SEPARATE, Trait.BORDER_AFTER);
+ BorderProps.Mode.SEPARATE, Trait.BORDER_AFTER, context);
}
/*
@@ -104,14 +104,16 @@ public final class TraitSetter {
*/
private static void addBorderTrait(Area area,
CommonBorderPaddingBackground bpProps,
- boolean discard, int side, int mode,
- Integer trait) {
- int borderWidth = bpProps.getBorderWidth(side, discard);
- if (borderWidth > 0) {
- area.addTrait(trait,
- new BorderProps(bpProps.getBorderStyle(side),
- borderWidth, bpProps.getBorderColor(side),
- mode));
+
+ boolean discard, int side, BorderProps.Mode mode,
+ Integer traitCode, PercentBaseContext context) {
+ 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));
+
}
}
@@ -126,19 +128,19 @@ public final class TraitSetter {
*/
public static void addBorders(Area area, CommonBorderPaddingBackground borderProps,
PercentBaseContext context) {
- BorderProps bps = getBorderProps(borderProps, CommonBorderPaddingBackground.BEFORE);
+ BorderProps bps = getBorderProps(borderProps, CommonBorderPaddingBackground.BEFORE, context);
if (bps != null) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.AFTER);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.AFTER, context);
if (bps != null) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.START);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.START, context);
if (bps != null) {
area.addTrait(Trait.BORDER_START, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.END);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.END, context);
if (bps != null) {
area.addTrait(Trait.BORDER_END, bps);
}
@@ -163,22 +165,23 @@ public final class TraitSetter {
boolean discardBefore, boolean discardAfter,
boolean discardStart, boolean discardEnd,
PercentBaseContext context) {
- BorderProps bps = getBorderProps(borderProps, CommonBorderPaddingBackground.BEFORE);
+ BorderProps bps = getBorderProps(borderProps, CommonBorderPaddingBackground.BEFORE, context);
if (bps != null && !discardBefore) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.AFTER);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.AFTER, context);
if (bps != null && !discardAfter) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.START);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.START, context);
if (bps != null && !discardStart) {
area.addTrait(Trait.BORDER_START, bps);
}
- bps = getBorderProps(borderProps, CommonBorderPaddingBackground.END);
+ bps = getBorderProps(borderProps, CommonBorderPaddingBackground.END, context);
if (bps != null && !discardEnd) {
area.addTrait(Trait.BORDER_END, bps);
}
+
}
/**
@@ -262,15 +265,14 @@ 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) {
- BorderProps bps;
- bps = new BorderProps(bordProps.getBorderStyle(side),
- width,
- bordProps.getBorderColor(side),
- BorderProps.SEPARATE);
- return bps;
+ int radiusStart = bordProps.getBorderRadiusStart(side, false, context);
+ int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context);
+ if (width != 0 || radiusStart != 0 || radiusEnd != 0) {
+ return new BorderProps(bordProps.getBorderStyle(side), width, radiusStart, radiusEnd,
+ bordProps.getBorderColor(side), BorderProps.Mode.SEPARATE);
} else {
return null;
}
@@ -280,13 +282,14 @@ 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;
}
}
+
/**
* 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/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
index 8a6b0a703..fbd753d58 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 cb66f2abb..b87f5c2ca 100644
--- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
@@ -32,7 +32,6 @@ import org.apache.batik.parser.AWTTransformProducer;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.util.QName;
-import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Area;
@@ -50,6 +49,7 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.traits.BorderProps;
+import org.apache.fop.util.UnitConv;
/**
* Abstract base class for renderers like PDF and PostScript where many painting operations
@@ -166,11 +166,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, backgroundArea.getBidiLevel());
+ 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) {
+ bg = backgroundTrait.getColor();
+ }
+
drawBorders(startx, starty, width, height,
- bpsBefore, bpsAfter, bpsStart, bpsEnd, borderArea.getBidiLevel());
+ bpsBefore, bpsAfter, bpsStart, bpsEnd, backgroundArea.getBidiLevel(), bg);
}
/**
@@ -247,14 +256,17 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
paddRectHeight -= bpsBottom.width / 1000f;
}
+ saveGraphicsState();
+
+ clipBackground(sx, sy, paddRectWidth, paddRectHeight, bpsTop, bpsBottom, bpsLeft, bpsRight);
+
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
@@ -290,13 +302,36 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
drawImage(back.getURL(), pos);
}
}
-
- restoreGraphicsState();
}
+ restoreGraphicsState();
}
}
/**
+ * 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.
+ *
+ * @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
+ * @param innerBackgroundColor the background color of the block
+ */
+ 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.
*
@@ -309,11 +344,12 @@ 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
*/
protected void drawBorders( // CSOK: ParameterNumber
float startx, float starty, float width, float height,
BorderProps bpsBefore, BorderProps bpsAfter,
- BorderProps bpsStart, BorderProps bpsEnd, int level) {
+ BorderProps bpsStart, BorderProps bpsEnd, int level, Color innerBackgroundColor) {
Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height);
BorderProps bpsTop = bpsBefore;
BorderProps bpsBottom = bpsAfter;
@@ -326,7 +362,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
bpsLeft = bpsEnd;
bpsRight = bpsStart;
}
- drawBorders(borderRect, bpsTop, bpsBottom, bpsLeft, bpsRight);
+ drawBorders(borderRect, bpsTop, bpsBottom, bpsLeft, bpsRight, innerBackgroundColor);
}
private static final int TOP = 0;
@@ -341,10 +377,12 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
* @param bpsBottom the border traits associated with bottom edge
* @param bpsLeft the border specification on the left edge
* @param bpsRight the border specification on the right edge
+ * @param innerBackgroundColor the background color of the block
*/
protected void drawBorders( // CSOK: MethodLength
Rectangle2D.Float borderRect,
- BorderProps bpsTop, BorderProps bpsBottom, BorderProps bpsLeft, BorderProps bpsRight) {
+ BorderProps bpsTop, BorderProps bpsBottom, BorderProps bpsLeft, BorderProps bpsRight,
+ Color innerBackgroundColor) {
//TODO generalize each of the four conditions into using a parameterized drawBorder()
boolean[] border = new boolean[] {
(bpsTop != null), (bpsRight != null),
@@ -391,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);
@@ -425,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);
@@ -459,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);
@@ -493,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);
@@ -513,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/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
index 35db42ac4..cd8304b4b 100644
--- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
+++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
@@ -24,8 +24,10 @@ import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.io.IOException;
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;
@@ -85,6 +87,13 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
private Map<String, PageSegmentDescriptor> pageSegmentMap
= new java.util.HashMap<String, PageSegmentDescriptor>();
+
+ // Rounded corners are cached at the document level
+ private Map<String, String> roundedCornerNameCache
+ = new HashMap<String, String>();
+
+ private int roundedCornerCount = 0;
+
private static enum Location {
ELSEWHERE, IN_DOCUMENT_HEADER, FOLLOWING_PAGE_SEQUENCE, IN_PAGE_HEADER
}
@@ -398,6 +407,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(Locale.ENGLISH);
+ 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);
+ }
+
+
private void handleNOP(AFPPageSetup nop) {
String content = nop.getContent();
if (content != null) {
diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandler.java b/src/java/org/apache/fop/render/afp/AFPImageHandler.java
index 118207d38..6404924a1 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 f60e271cb..2a0db08bb 100644
--- a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
+++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
@@ -85,9 +85,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/AFPImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerRawJPEG.java
index e318c49fb..7508c8ca0 100644
--- a/src/java/org/apache/fop/render/afp/AFPImageHandlerRawJPEG.java
+++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerRawJPEG.java
@@ -94,9 +94,9 @@ public class AFPImageHandlerRawJPEG extends AFPImageHandler implements ImageHand
AFPPaintingState paintingState = afpContext.getPaintingState();
// 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/AFPImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
index e5f41d232..3adbd68f6 100644
--- a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
+++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
@@ -111,9 +111,9 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
AFPPaintingState paintingState = afpContext.getPaintingState();
// set resource information
- setResourceInformation(imageObjectInfo,
+ imageObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
- afpContext.getForeignAttributes());
+ afpContext.getForeignAttributes()));
setDefaultResourceLevel(imageObjectInfo, afpContext.getResourceManager());
// Positioning
@@ -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/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java
index 7d659a8b4..3a41e5feb 100644
--- a/src/java/org/apache/fop/render/afp/AFPPainter.java
+++ b/src/java/org/apache/fop/render/afp/AFPPainter.java
@@ -21,23 +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.net.URI;
import java.net.URISyntaxException;
+import java.security.MessageDigest;
import java.util.Map;
import org.w3c.dom.Document;
+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.AFPEventProducer;
+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;
@@ -55,9 +69,11 @@ import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.Typeface;
+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;
@@ -70,12 +86,12 @@ import org.apache.fop.util.CharUtilities;
*/
public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
- //** 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 */
@@ -93,8 +109,9 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
public AFPPainter(AFPDocumentHandler documentHandler) {
super(documentHandler);
this.state = IFState.create();
- this.borderPainter = new AFPBorderPainterAdapter(
+ 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());
@@ -179,13 +196,13 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
/** {@inheritDoc} */
@Override
protected RenderingContext createRenderingContext() {
- AFPRenderingContext psContext = new AFPRenderingContext(
+ AFPRenderingContext renderingContext = new AFPRenderingContext(
getUserAgent(),
getDocumentHandler().getResourceManager(),
getPaintingState(),
getFontInfo(),
getContext().getForeignAttributes());
- return psContext;
+ return renderingContext;
}
/** {@inheritDoc} */
@@ -224,6 +241,32 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
}
/** {@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);
}
@@ -244,7 +287,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
}
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");
}
@@ -258,96 +301,576 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
}
}
- /** {@inheritDoc} */
@Override
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException {
+ BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top != null || bottom != null || left != null || right != null) {
- try {
- this.borderPainter.drawBorders(rect, top, bottom, left, right);
- } catch (IOException ife) {
- throw new IFException("IO error while painting borders", ife);
+ this.borderPainter.drawBorders(rect, top, bottom, left, right, innerBackgroundColor);
+ }
+ }
+
+
+ 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 AFPBorderPainter delegate;
+ private final class BorderImagePainter implements Graphics2DImagePainter {
+ private final double cornerCorrectionFactor;
+ 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;
+
+ /* TODO represent border related parameters in a class */
+ private BorderImagePainter(double cornerCorrectionFactor, Rectangle borderRect,
+ BorderProps bpsStart, BorderProps bpsEnd,
+ BorderProps bpsBefore, BorderProps bpsAfter,
+ boolean[] roundCorner, Color innerBackgroundColor) {
+ this.cornerCorrectionFactor = cornerCorrectionFactor;
+ 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[TOP_LEFT]) {
+ AffineTransform transform = new AffineTransform();
+ int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusStart());
+ int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusStart());
+
+ int beforeWidth = bpsBefore.width;
+ int startWidth = bpsStart.width;
+ int corner = TOP_LEFT;
+
+ background.subtract(makeCornerClip(beforeRadius, startRadius,
+ transform));
+
+ Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+ clip.transform(transform);
+ cornerRegion.add(clip);
+
+ cornerBorder[TOP].add(makeCornerBorderBPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+
+ cornerBorder[LEFT].add(makeCornerBorderIPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+ }
+
+ if (roundCorner[TOP_RIGHT]) {
+ AffineTransform transform
+ = new AffineTransform(-1, 0, 0, 1, borderRect.width, 0);
+
+ int beforeRadius = (int)(cornerCorrectionFactor * bpsBefore.getRadiusEnd());
+ int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusStart());
+
+ int beforeWidth = bpsBefore.width;
+ int startWidth = bpsEnd.width;
+ int corner = TOP_RIGHT;
+
+ background.subtract(makeCornerClip(beforeRadius, startRadius,
+ transform));
+
+ Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+ clip.transform(transform);
+ cornerRegion.add(clip);
+
+ cornerBorder[TOP].add(makeCornerBorderBPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+
+ cornerBorder[RIGHT].add(makeCornerBorderIPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+ }
+
+ if (roundCorner[BOTTOM_RIGHT]) {
+ AffineTransform transform = new AffineTransform(-1, 0, 0, -1,
+ borderRect.width, borderRect.height);
+
+ int beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusEnd());
+ int startRadius = (int)(cornerCorrectionFactor * bpsEnd.getRadiusEnd());
+
+ int beforeWidth = bpsAfter.width;
+ int startWidth = bpsEnd.width;
+ int corner = BOTTOM_RIGHT;
+
+ background.subtract(makeCornerClip(beforeRadius, startRadius,
+ transform));
+
+ Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+ clip.transform(transform);
+ cornerRegion.add(clip);
+
+ cornerBorder[BOTTOM].add(makeCornerBorderBPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+ cornerBorder[RIGHT].add(makeCornerBorderIPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+ }
+
+ if (roundCorner[BOTTOM_LEFT]) {
+ AffineTransform transform
+ = new AffineTransform(1, 0, 0, -1, 0, borderRect.height);
+
+ int beforeRadius = (int)(cornerCorrectionFactor * bpsAfter.getRadiusStart());
+ int startRadius = (int)(cornerCorrectionFactor * bpsStart.getRadiusEnd());
+
+ int beforeWidth = bpsAfter.width;
+ int startWidth = bpsStart.width;
+ int corner = BOTTOM_LEFT;
+
+ background.subtract(makeCornerClip(beforeRadius, startRadius,
+ transform));
+
+ Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
+ clip.transform(transform);
+ cornerRegion.add(clip);
+
+ cornerBorder[BOTTOM].add(makeCornerBorderBPD(beforeRadius,
+ startRadius, beforeWidth, startWidth, transform));
+ cornerBorder[LEFT].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[TOP]);
+ }
+
+ 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[RIGHT]);
+ }
+
+ 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[BOTTOM]);
+ }
+
+ 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[LEFT]);
+ }
+ }
- public AFPBorderPainterAdapter(AFPBorderPainter borderPainter) {
- this.delegate = borderPainter;
+ public Dimension getImageSize() {
+ return borderRect.getSize();
+ }
}
- @Override
- protected void clip() throws IOException {
- //not supported by AFP
+ private final AFPPainter painter;
+ private final AFPDocumentHandler documentHandler;
+
+ public AFPBorderPainterAdapter(GraphicsPainter graphicsPainter, AFPPainter painter,
+ AFPDocumentHandler documentHandler) {
+ super(graphicsPainter);
+ this.painter = painter;
+ this.documentHandler = documentHandler;
}
- @Override
- protected void closePath() throws IOException {
- //used for clipping only, so not implemented
+ public void drawBorders(final Rectangle borderRect,
+ final BorderProps bpsBefore, final BorderProps bpsAfter,
+ final BorderProps bpsStart, final BorderProps bpsEnd, Color innerBackgroundColor)
+ throws IFException {
+ drawRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd, innerBackgroundColor);
}
- @Override
- protected void moveTo(int x, int y) throws IOException {
- //used for clipping only, so not implemented
+ private boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) {
+ return !hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd);
}
- @Override
- protected void lineTo(int x, int y) throws IOException {
- //used for clipping only, so not implemented
+ 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));
}
- @Override
- protected void saveGraphicsState() throws IOException {
- //used for clipping only, so not implemented
+ 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 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
+ && 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]) {
+ 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(cornerCorrectionFactor, borderRect,
+ bpsStart, bpsEnd, bpsBefore, bpsAfter,
+ roundCorner, innerBackgroundColor);
+ }
+ paintCornersAsBitmap(painter, borderRect, name);
}
- @Override
- protected void restoreGraphicsState() throws IOException {
- //used for clipping only, so not implemented
+ private boolean isNotCollapseOuter(BorderProps bp) {
+ return !bp.isCollapseOuter();
}
- private float toPoints(int mpt) {
- return mpt / 1000f;
+ 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;
}
- @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);
+
+ 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);
+ cut.lineTo(startRadius, ((float) startRadius * beforeWidth) / startWidth);
+ cut.lineTo(startRadius, 0);
+ clip.intersect(new Area(cut));
+ clip.transform(transform);
+ return clip;
}
- @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");
+
+ 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);
+ cut.lineTo(startRadius, ((float) startRadius * beforeWidth) / startWidth);
+ cut.lineTo(startRadius, 0);
+ clip.subtract(new Area(cut));
+ clip.transform(transform);
+ return clip;
+ }
+
+ 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);
}
- //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);
+ 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",
+ Integer.valueOf(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 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");
+
+ }
}
/** {@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);
}
@@ -367,14 +890,14 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
// register font as necessary
Map<String, Typeface> 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);
@@ -398,7 +921,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
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 );
@@ -533,4 +1056,21 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
getPaintingState().restore();
}
+
+ /** {@inheritDoc} */
+ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) {
+ return borderPainter.isBackgroundRequired(bpsBefore, bpsAfter, 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/afp/AbstractAFPImageHandlerRawStream.java b/src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java
index b36646117..e2d779192 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 147b6d54b..2205aea4c 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
@@ -330,7 +330,7 @@ public abstract class AbstractIFPainter<T extends IFDocumentHandler> implements
/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException {
+ BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top != null) {
Rectangle b = new Rectangle(
rect.x, rect.y,
@@ -452,4 +452,10 @@ public abstract class AbstractIFPainter<T extends IFDocumentHandler> implements
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/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..dd28dd5c8
--- /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;
+}
diff --git a/src/java/org/apache/fop/render/intermediate/BorderPainter.java b/src/java/org/apache/fop/render/intermediate/BorderPainter.java
index cc28028ef..19e30b50f 100644
--- a/src/java/org/apache/fop/render/intermediate/BorderPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/BorderPainter.java
@@ -20,17 +20,47 @@
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 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;
+
+ public BorderPainter(GraphicsPainter graphicsPainter) {
+ this.graphicsPainter = graphicsPainter;
+ }
/**
* Draws borders.
@@ -39,59 +69,96 @@ public abstract class BorderPainter {
* @param bpsBottom the border specification on the bottom side
* @param bpsLeft the border specification on the left side
* @param bpsRight the border specification on the end side
- * @throws IOException if an I/O error occurs while creating the borders
+ * @param innerBackgroundColor the inner background color
+ * @throws IFException if an error occurs while drawing the borders
*/
public void drawBorders(Rectangle borderRect, // CSOK: MethodLength
BorderProps bpsTop, BorderProps bpsBottom,
+ BorderProps bpsLeft, BorderProps bpsRight, Color innerBackgroundColor)
+ throws IFException {
+ try {
+ drawRoundedBorders(borderRect, bpsTop, bpsBottom, bpsLeft, bpsRight);
+ } 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;
+ }
+
+ /**
+ * TODO merge with drawRoundedBorders()?
+ * @param borderRect the border rectangle
+ * @param bpsTop the border specification on the top side
+ * @param bpsBottom the border specification on the bottom side
+ * @param bpsLeft the border specification on the left side
+ * @param bpsRight the border specification on the end side
+ * @throws IOException
+ */
+ protected void drawRectangularBorders(Rectangle borderRect,
+ BorderProps bpsTop, BorderProps bpsBottom,
BorderProps bpsLeft, BorderProps bpsRight) throws IOException {
+
+ bpsTop = sanitizeBorderProps(bpsTop);
+ bpsBottom = sanitizeBorderProps(bpsBottom);
+ bpsLeft = sanitizeBorderProps(bpsLeft);
+ bpsRight = sanitizeBorderProps(bpsRight);
+
+
int startx = borderRect.x;
int starty = borderRect.y;
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[0] ? sx1 + bw[3] - clipw[3] : sx1);
+ int sx2 = (slant[TOP_LEFT] ? sx1 + bw[LEFT] - clipw[LEFT] : 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[TOP_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1);
+ int outery = starty - clipw[TOP];
+ int clipy = outery + clipw[TOP];
+ int innery = outery + bw[TOP];
saveGraphicsState();
moveTo(sx1, clipy);
+
+
int sx1a = sx1;
int ex1a = ex1;
- if (bpsTop.mode == BorderProps.COLLAPSE_OUTER) {
- if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) {
- sx1a -= clipw[3];
+ if (isCollapseOuter(bpsTop)) {
+ if (isCollapseOuter(bpsLeft)) {
+ sx1a -= clipw[LEFT];
}
- if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) {
- ex1a += clipw[1];
+ if (isCollapseOuter(bpsRight)) {
+ ex1a += clipw[RIGHT];
}
lineTo(sx1a, outery);
lineTo(ex1a, outery);
@@ -107,23 +174,23 @@ public abstract class BorderPainter {
}
if (bpsRight != null) {
int sy1 = starty;
- int sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1);
+ int sy2 = (slant[TOP_RIGHT] ? sy1 + bw[TOP] - clipw[TOP] : 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[BOTTOM_RIGHT] ? ey1 - bw[BOTTOM] + clipw[BOTTOM] : ey1);
+ int outerx = startx + width + clipw[RIGHT];
+ int clipx = outerx - clipw[RIGHT];
+ int innerx = outerx - bw[RIGHT];
saveGraphicsState();
moveTo(clipx, sy1);
int sy1a = sy1;
int ey1a = ey1;
- if (bpsRight.mode == BorderProps.COLLAPSE_OUTER) {
- if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) {
- sy1a -= clipw[0];
+
+ if (isCollapseOuter(bpsRight)) {
+ if (isCollapseOuter(bpsTop)) {
+ sy1a -= clipw[TOP];
}
- if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) {
- ey1a += clipw[2];
+ if (isCollapseOuter(bpsBottom)) {
+ ey1a += clipw[BOTTOM];
}
lineTo(outerx, sy1a);
lineTo(outerx, ey1a);
@@ -139,23 +206,22 @@ public abstract class BorderPainter {
}
if (bpsBottom != null) {
int sx1 = startx;
- int sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1);
+ int sx2 = (slant[BOTTOM_LEFT] ? sx1 + bw[LEFT] - clipw[LEFT] : 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[BOTTOM_RIGHT] ? ex1 - bw[RIGHT] + clipw[RIGHT] : ex1);
+ int outery = starty + height + clipw[BOTTOM];
+ int clipy = outery - clipw[BOTTOM];
+ int innery = outery - bw[BOTTOM];
saveGraphicsState();
moveTo(ex1, clipy);
int sx1a = sx1;
int ex1a = ex1;
- if (bpsBottom.mode == BorderProps.COLLAPSE_OUTER) {
- if (bpsLeft != null && bpsLeft.mode == BorderProps.COLLAPSE_OUTER) {
- sx1a -= clipw[3];
+ if (isCollapseOuter(bpsBottom)) {
+ if (isCollapseOuter(bpsLeft)) {
+ sx1a -= clipw[LEFT];
}
- if (bpsRight != null && bpsRight.mode == BorderProps.COLLAPSE_OUTER) {
- ex1a += clipw[1];
+ if (isCollapseOuter(bpsRight)) {
+ ex1a += clipw[RIGHT];
}
lineTo(ex1a, outery);
lineTo(sx1a, outery);
@@ -171,23 +237,25 @@ public abstract class BorderPainter {
}
if (bpsLeft != null) {
int sy1 = starty;
- int sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
+ int sy2 = (slant[TOP_LEFT] ? sy1 + bw[TOP] - clipw[TOP] : 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[BOTTOM_LEFT] ? ey1 - bw[BOTTOM] + clipw[BOTTOM] : ey1);
+ int outerx = startx - clipw[LEFT];
+ int clipx = outerx + clipw[LEFT];
+ int innerx = outerx + bw[LEFT];
saveGraphicsState();
+
moveTo(clipx, ey1);
+
int sy1a = sy1;
int ey1a = ey1;
- if (bpsLeft.mode == BorderProps.COLLAPSE_OUTER) {
- if (bpsTop != null && bpsTop.mode == BorderProps.COLLAPSE_OUTER) {
- sy1a -= clipw[0];
+ if (isCollapseOuter(bpsLeft)) {
+ if (isCollapseOuter(bpsTop)) {
+ sy1a -= clipw[TOP];
}
- if (bpsBottom != null && bpsBottom.mode == BorderProps.COLLAPSE_OUTER) {
- ey1a += clipw[2];
+ if (isCollapseOuter(bpsBottom)) {
+ ey1a += clipw[BOTTOM];
}
lineTo(outerx, ey1a);
lineTo(outerx, sy1a);
@@ -202,77 +270,648 @@ public abstract class BorderPainter {
}
}
+ private boolean isCollapseOuter(BorderProps bp) {
+ return bp != null && bp.isCollapseOuter();
+ }
- /**
- * Draws a border line.
- * @param x1 X coordinate of the upper left corner
- * of the line's bounding rectangle (in millipoints)
- * @param y1 start Y coordinate of the upper left corner
- * of the line's bounding rectangle (in millipoints)
- * @param x2 end X coordinate of the lower right corner
- * of the line's bounding rectangle (in millipoints)
- * @param y2 end y coordinate of the lower right corner
- * of the line's bounding rectangle (in millipoints)
- * @param horz true if it is a horizontal line
- * @param startOrBefore true if the line is the start or end edge of a border box
- * @param style the border style
- * @param color the border color
- * @throws IOException if an I/O error occurs
- */
- protected abstract void drawBorderLine( // CSOK: ParameterNumber
- int x1, int y1, int x2, int y2,
- boolean horz, boolean startOrBefore, int style, Color color) throws IOException;
+ /** 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 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;
+ }
+ 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 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);
+ }
- /**
- * 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;
+ 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 = start.getWidth() - start.getClippedWidth();
+ final int ex1 = width;
+ final int ex2 = ex1 - end.getWidth() + end.getClippedWidth();
+ final int outery = -before.getClippedWidth();
+ final int innery = outery + before.getWidth();
+ 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) {
+ 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();
+ }
+ }
- /**
- * 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;
+ private static int correctRadius(double cornerCorrectionFactor, int radius) {
+ return (int) (Math.round(cornerCorrectionFactor * radius));
+ }
+
+ private static BorderSegment borderSegmentForBefore(BorderProps before) {
+ return AbstractBorderSegment.asBorderSegment(before);
+ }
+
+ 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);
+ }
+
+ private interface BorderSegment {
+
+ Color getColor();
+
+ int getStyle();
+
+ int getWidth();
+
+ int getClippedWidth();
+
+ int getRadiusStart();
+
+ int getRadiusEnd();
+
+ boolean isCollapseOuter();
+
+ boolean isSpecified();
+ }
+
+ private abstract static class AbstractBorderSegment implements BorderSegment {
+
+ 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);
+ }
+
+ public boolean isSpecified() {
+ return !(this instanceof NullBorderSegment);
+ }
+
+ private static class WrappingBorderSegment extends AbstractBorderSegment {
+
+ protected final BorderProps borderProps;
+
+ private final int clippedWidth;
+
+ WrappingBorderSegment(BorderProps borderProps) {
+ this.borderProps = borderProps;
+ clippedWidth = BorderProps.getClippedWidth(borderProps);
+ }
+
+ public int getStyle() {
+ return borderProps.style;
+ }
+
+ public Color getColor() {
+ return borderProps.color;
+ }
+
+ public int getWidth() {
+ return borderProps.width;
+ }
+
+ public int getClippedWidth() {
+ return clippedWidth;
+ }
+ public boolean isCollapseOuter() {
+ return borderProps.isCollapseOuter();
+ }
+
+ public int getRadiusStart() {
+ return borderProps.getRadiusStart();
+ }
+
+ public int getRadiusEnd() {
+ return borderProps.getRadiusEnd();
+ }
+ }
+
+ private static class FlippedBorderSegment extends WrappingBorderSegment {
+
+ FlippedBorderSegment(BorderProps borderProps) {
+ super(borderProps);
+ }
+
+ public int getRadiusStart() {
+ return borderProps.getRadiusEnd();
+ }
+
+ public int getRadiusEnd() {
+ return borderProps.getRadiusStart();
+ }
+ }
+
+ private static final class NullBorderSegment extends AbstractBorderSegment {
+
+ public static final NullBorderSegment INSTANCE = new NullBorderSegment();
+
+ private NullBorderSegment() {
+ }
+
+ public int getWidth() {
+ return 0;
+ }
+
+ public int getClippedWidth() {
+ return 0;
+ }
+
+ public int getRadiusStart() {
+ return 0;
+ }
+
+ 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 {
+ return new double[]{0, ellipseCenterY, 0};
+ }
+ }
+
+ private double[] getCornerBorderJoinMetrics(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))};
+ }
/**
- * 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)
+ * Clip the background to the inner border
+ * @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
*/
- protected abstract void lineTo(int x, int y) throws IOException;
+ 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();
+ }
+
+
/**
- * Closes the current path.
- * @throws IOException if an I/O error occurs
+ * The four corners
+ * SB - Start-Before
+ * BE - Before-End
+ * EA - End-After
+ * AS - After-Start
+ *
+ * 0 --> x
+ * |
+ * v
+ * y
+ *
+ * SB BE
+ * *----*
+ * | |
+ * | |
+ * *----*
+ * AS EA
+ *
*/
- protected abstract void closePath() throws IOException;
+ 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;
+ }
+
+ }
+
+ private static final class Corner {
+
+ private static final Corner SQUARE = new Corner(0, 0, null, 0, 0, 0, 0);
+
+ /** The radius of the elliptic corner in the x direction */
+ private final int radiusX;
+
+ /** The radius of the elliptic corner in the y direction */
+ private final int radiusY;
+
+ /** The start and end angles of the corner ellipse */
+ private final CornerAngles angles;
+
+ /** The offset in the x direction of the center of the ellipse relative to the starting point */
+ private final int centerX;
+
+ /** The offset in the y direction of the center of the ellipse relative to the starting point */
+ private final int centerY;
+
+ /** The value in the x direction that the corner extends relative to the starting point */
+ private final int incrementX;
+
+ /** The value in the y direction that the corner extends relative to the starting point */
+ private final int incrementY;
+
+ 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;
+ }
+
+ private static int extentFromRadiusStart(BorderSegment border, double correctionFactor) {
+ return extentFromRadius(border.getRadiusStart(), border, correctionFactor);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+ }
/**
- * Reduces the current clipping region to the current path.
- * @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 clip() throws IOException;
+ private final class PathPainter {
+
+ /** Current x position */
+ private int x;
+
+ /** Current y position */
+ private int y;
+
+ PathPainter(int x, int y) throws IOException {
+ moveTo(x, y);
+ }
+
+ private void moveTo(int x, int y) throws IOException {
+ this.x += x;
+ this.y += y;
+ BorderPainter.this.moveTo(this.x, this.y);
+ }
+
+ public PathPainter lineTo(int x, int y) throws IOException {
+ this.x += x;
+ this.y += y;
+ BorderPainter.this.lineTo(this.x, this.y);
+ return this;
+ }
+
+ public PathPainter lineHorizTo(int x) throws IOException {
+ return lineTo(x, 0);
+ }
+
+ public PathPainter lineVertTo(int y) throws IOException {
+ return lineTo(0, y);
+ }
+
+ 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;
+ }
+ }
/**
- * Save the graphics state on the stack.
- * @throws IOException if an I/O error occurs
+ * 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 abstract void saveGraphicsState() throws IOException;
+ 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));
+ }
/**
- * Restore the last graphics state from the stack.
- * @throws IOException if an I/O error occurs
+ * 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 abstract void restoreGraphicsState() throws IOException;
+ 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);
+ }
+
+ private static final class CornerScaleCorrectionCalculator {
+
+ private double correctionFactor = 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);
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ 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 void moveTo(int x, int y) throws IOException {
+ graphicsPainter.moveTo(x, y);
+ }
+
+ private void lineTo(int x, int y) throws IOException {
+ graphicsPainter.lineTo(x, y);
+ }
+
+ 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);
+ }
+
+ private void rotateCoordinates(double angle) throws IOException {
+ graphicsPainter.rotateCoordinates(angle);
+ }
+
+ private void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
+ graphicsPainter.translateCoordinates(xTranslate, yTranslate);
+ }
+
+ private void closePath() throws IOException {
+ graphicsPainter.closePath();
+ }
+
+ private void clip() throws IOException {
+ graphicsPainter.clip();
+ }
+
+ private void saveGraphicsState() throws IOException {
+ graphicsPainter.saveGraphicsState();
+ }
+
+ 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/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
index 06dfbd181..5d2beb65c 100644
--- a/src/java/org/apache/fop/render/intermediate/IFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -168,6 +168,34 @@ 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;
+
+
+ /**
+ * 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);
+
/**
* Fills a rectangular area.
* @param rect the rectangle's coordinates and extent
@@ -184,11 +212,12 @@ public interface IFPainter {
* @param bottom the border segment on the bottom edge
* @param left the border segment on the left edge
* @param right the border segment on the right edge
+ * @param innerBackgroundColor the color of the inner background
* @throws IFException if an error occurs while handling this event
*/
void drawBorderRect(Rectangle rect,
BorderProps top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException;
+ BorderProps left, BorderProps right, 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 d1fa50737..24b7a924c 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -650,6 +650,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));
}
@@ -668,7 +681,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);
}
}
@@ -709,9 +741,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 fe3bbf72c..592a57ebe 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -1327,18 +1327,44 @@ 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, int level) {
+ BorderProps bpsStart, BorderProps bpsEnd, int level, Color innerBackgroundColor) {
Rectangle rect = toMillipointRectangle(startx, starty, width, height);
try {
BorderProps bpsTop = bpsBefore;
@@ -1352,7 +1378,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
bpsLeft = bpsEnd;
bpsRight = bpsStart;
}
- painter.drawBorderRect(rect, bpsTop, bpsBottom, bpsLeft, bpsRight);
+ painter.drawBorderRect(rect, bpsTop, bpsBottom, bpsLeft, bpsRight, 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 41d1b01ef..24239e5f4 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -519,6 +519,37 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
}
/** {@inheritDoc} */
+ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ 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, "top", bpsBefore.toString());
+ }
+ if (bpsAfter != null) {
+ addAttribute(atts, "bottom", bpsAfter.toString());
+ }
+ if (bpsStart != null) {
+ addAttribute(atts, "left", bpsStart.toString());
+ }
+ if (bpsEnd != null) {
+ addAttribute(atts, "right", bpsEnd.toString());
+ }
+ }
+ handler.element(EL_CLIP_RECT, atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in clipRect()", e);
+ }
+ }
+
+
+ /** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
return;
@@ -536,9 +567,38 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
}
}
+ //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 top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException {
+ BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top == null && bottom == null && left == null && right == null) {
return;
}
@@ -560,6 +620,12 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
if (right != null) {
addAttribute(atts, "right", right.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);
@@ -856,4 +922,10 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
throw new IFException("SAX error serializing object", e);
}
}
+
+ /** {@inheritDoc} */
+ public boolean isBackgroundRequired(BorderProps bpsTop, BorderProps bpsBottom,
+ BorderProps bpsLeft, BorderProps bpsRight) {
+ return true;
+ }
}
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 b2b29188b..000000000
--- a/src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java
+++ /dev/null
@@ -1,318 +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 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;
- }
-
-}
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..588d417fa
--- /dev/null
+++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsPainter.java
@@ -0,0 +1,332 @@
+/*
+ * 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;
+ }
+}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
index 89031787a..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<Java2DDocumentHandler> {
/** 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<Java2DDocumentHandler> {
}
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} */
@@ -174,6 +179,13 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> {
}
/** {@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) {
return;
@@ -188,19 +200,18 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> {
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
BorderProps left, BorderProps right) throws IFException {
if (top != null || bottom != null || left != null || right != null) {
- try {
- this.borderPainter.drawBorders(rect, top, bottom, left, right);
- } catch (IOException e) {
- //Won't happen with Java2D
- throw new IllegalStateException("Unexpected I/O error");
- }
+ this.borderPainter.drawBorders(rect, top, bottom, left, right, null);
}
}
/** {@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} */
@@ -263,4 +274,6 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> {
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 614ebf1d2..179a4f527 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -174,6 +174,14 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
}
/** {@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) {
return;
@@ -201,7 +209,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
final BorderProps top, final BorderProps bottom,
final BorderProps left, final BorderProps right) throws IFException {
if (isSpeedOptimized()) {
- super.drawBorderRect(rect, top, bottom, left, right);
+ super.drawBorderRect(rect, top, bottom, left, right, null);
return;
}
if (top != null || bottom != null || left != null || right != null) {
@@ -525,4 +533,6 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
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
deleted file mode 100644
index f8090fdeb..000000000
--- a/src/java/org/apache/fop/render/pdf/PDFBorderPainter.java
+++ /dev/null
@@ -1,331 +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 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) {
- 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) {
- 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");
- }
-
-}
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..ff1ffb5ff
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFGraphicsPainter.java
@@ -0,0 +1,495 @@
+/*
+ * 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;
+ float colFactor;
+ 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:
+ 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:
+ 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);
+ 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 fa1c50318..7b636fddd 100644
--- a/src/java/org/apache/fop/render/pdf/PDFPainter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -43,6 +43,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.IFContext;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
@@ -61,7 +63,9 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
/** The current content generator */
protected PDFContentGenerator generator;
- private final PDFBorderPainter borderPainter;
+ private final GraphicsPainter graphicsPainter;
+
+ private final BorderPainter borderPainter;
private boolean accessEnabled;
@@ -114,7 +118,8 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
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();
languageAvailabilityChecker = accessEnabled
@@ -250,6 +255,20 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
}
/** {@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) {
return;
@@ -283,23 +302,26 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
/** {@inheritDoc} */
@Override
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException {
+ BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top != null || bottom != null || left != null || right != null) {
generator.endTextObject();
- try {
- this.borderPainter.drawBorders(rect, top, bottom, left, right);
- } catch (IOException ioe) {
- throw new IFException("I/O error while drawing borders", ioe);
- }
+ this.borderPainter.drawBorders(rect, top, bottom, left, right, innerBackgroundColor);
}
}
+
+
+
/** {@inheritDoc} */
@Override
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 a52974d36..000000000
--- a/src/java/org/apache/fop/render/ps/PSBorderPainter.java
+++ /dev/null
@@ -1,333 +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();
- }
-
-}
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..ab766f701
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSGraphicsPainter.java
@@ -0,0 +1,386 @@
+/*
+ * 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.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 63e947e55..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<PSDocumentHandler> {
/** 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<PSDocumentHandler> {
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;
}
@@ -199,6 +204,20 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
}
/** {@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) {
return;
@@ -225,15 +244,15 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
- BorderProps left, BorderProps right) throws IFException {
+ BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top != null || bottom != null || left != null || right != null) {
try {
endTextObject();
if (getDocumentHandler().getPSUtil().getRenderingMode() == PSRenderingMode.SIZE
&& hasOnlySolidBorders(top, bottom, left, right)) {
- super.drawBorderRect(rect, top, bottom, left, right);
+ super.drawBorderRect(rect, top, bottom, left, right, innerBackgroundColor);
} else {
- this.borderPainter.drawBorders(rect, top, bottom, left, right);
+ this.borderPainter.drawBorders(rect, top, bottom, left, right, innerBackgroundColor);
}
} catch (IOException ioe) {
throw new IFException("I/O error in drawBorderRect()", ioe);
@@ -246,7 +265,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
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 ae7a9a434..e274517d9 100644
--- a/src/java/org/apache/fop/traits/BorderProps.java
+++ b/src/java/org/apache/fop/traits/BorderProps.java
@@ -34,47 +34,90 @@ import org.apache.fop.util.ColorUtil;
*/
public class BorderProps implements Serializable {
- private static final long serialVersionUID = -886871454032189183L;
+ private static final long serialVersionUID = 8022237892391068187L;
- /** 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
- /** Border mode (one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */
- public int mode; // CSOK: VisibilityModifier
+ public final int width; // 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);
+ }
+
+ /**
+ *
+ * @return the radius of the corner adjacent to the before or start border
+ */
+ public int getRadiusStart() {
+ return radiusStart;
+ }
+
+ /**
+ * @return the radius of the corner adjacent to the after or end border
+ */
+ public int getRadiusEnd() {
+ return radiusEnd;
}
/**
@@ -82,11 +125,7 @@ public class BorderProps implements Serializable {
* @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() {
@@ -97,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() {
@@ -112,12 +155,14 @@ 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)
&& width == other.width
- && mode == other.mode;
+ && mode == other.mode
+ && radiusStart == other.radiusStart
+ && radiusEnd == other.radiusEnd;
}
}
return false;
@@ -131,60 +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());
- }
-
- return new BorderProps(style, width, c, mode);
- } 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) {
- sbuf.append(',');
- 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.equals(Mode.SEPARATE)) {
+ // Because of the corner radii properties the mode must be set
+ // so that the parameter index is consistent
+ sbuf.append(",").append(Mode.SEPARATE.value);
}
+ sbuf.append(',').append(radiusStart)
+ .append(',').append(radiusEnd);
}
sbuf.append(')');
return sbuf.toString();
}
+ private static final 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");
+ }
+ }
+ }
+
}