aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/fo/advanced/rounded-corners.fo126
-rw-r--r--findbugs-exclude.xml2
-rw-r--r--src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd13
-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
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainter.java16
-rw-r--r--status.xml3
-rw-r--r--test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java10
-rw-r--r--test/java/org/apache/fop/render/afp/AFPPainterTestCase.java131
-rw-r--r--test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java6
-rw-r--r--test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java79
-rw-r--r--test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java578
-rw-r--r--test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java170
-rw-r--r--test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java66
-rw-r--r--test/java/org/apache/fop/render/ps/PSPainterTestCase.java32
-rw-r--r--test/java/org/apache/fop/traits/BorderPropsTestCase.java44
-rw-r--r--test/pdf/accessibility/pdf/complete.pdfbin94714 -> 94698 bytes
52 files changed, 5178 insertions, 1452 deletions
diff --git a/examples/fo/advanced/rounded-corners.fo b/examples/fo/advanced/rounded-corners.fo
new file mode 100644
index 000000000..7b275ae17
--- /dev/null
+++ b/examples/fo/advanced/rounded-corners.fo
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="main" page-width="21cm" page-height="29.7cm" margin-top="5mm">
+ <fo:region-body margin-bottom="30pt" margin-right="60pt" margin-left="60pt" margin-top="30pt"/>
+ <fo:region-after extent="4in"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="main">
+ <fo:flow flow-name="xsl-region-body" font-size="12pt">
+ <fo:block font-size="14pt" font-weight="bold">
+ Examples using Rounded Corners
+ </fo:block>
+ <fo:block margin="20pt 0 40pt 0">
+ <fo:block>The "border-radius" property in the http://xmlgraphics.apache.org/fop/extensions namespace is a shorthand property for setting appropriate values for "border-*-radius" (see below), and is used to specify round corners on block areas. The property takes one or two values:
+ </fo:block>
+ <fo:block-container margin="10 20pt 20pt 20pt">
+ <fo:block border-style="solid" border-width="10" border-color="#00f" fox:border-radius="20pt" padding="20pt" text-align="center" margin-bottom="10pt"> Circular corners are specified when 'border-radius' is set to a single value. This value is the radius of the outer quadrant of the corner.</fo:block>
+ <fo:block border-style="solid" border-width="10" border-color="#00f" fox:border-radius="20pt 40pt" padding="20pt" text-align="center" margin-bottom="10pt">Quarter-ellipse corners can be generated by setting two values for the property. The first value is the radius in the Inline Progression Direction. The second value is the radius in the Block Progression Direction.
+ </fo:block>
+ </fo:block-container>
+ </fo:block>
+ <fo:block margin="20pt 0 40pt 0">
+ <fo:block>The radii for a specific corner can be specified for each <fo:inline font-style="italic">CORNER</fo:inline> (in 'before-start', 'before-end', 'after-start', 'after-end') with the property "border-<fo:inline font-style="italic">CORNER</fo:inline>-radius". These properties
+ have the same interpretation as the "border-radius" property. </fo:block>
+ <fo:table border-collapse="separate" margin-top="10pt">
+ <fo:table-column column-width="200pt"/>
+ <fo:table-column column-width="200pt"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block margin-left="50pt" border-style="solid" border-width="50pt 0 0 50pt" border-color="#00f" fox:border-before-start-radius="100pt" height="100pt">
+ <fo:block padding="50pt"/>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block margin-left="50pt" border-style="solid" border-width="50pt 0 0 50pt" border-color="#00f" fox:border-before-start-radius="200pt 100pt" height="100pt">
+ <fo:block padding="50pt"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:block>
+ <fo:block margin="20pt">
+ <fo:block border-style="solid" background-color="#aaf" border-width="10" border-color="#00f" fox:border-radius="50pt" padding="20pt" text-align="center" margin-bottom="20pt">Backgrounds are cropped&#8230;</fo:block>
+ <fo:block break-after="page" border-style="solid" background-color="#aaf" border-width="0" border-color="#00f" fox:border-radius="50pt" padding="20pt" text-align="center" margin-bottom="20pt">&#8230;even with zero width borders.</fo:block>
+ </fo:block>
+ <fo:block>There are two canonical cases for rounded corners distinguished by the following condition: The width of each border segment is less than the corner radius perpendicular to that segment. This is demonstrated in Case <fo:inline font-style="italic">I</fo:inline> when this condition is <fo:inline font-style="italic">true</fo:inline> and Case <fo:inline font-style="italic">II</fo:inline> when <fo:inline font-style="italic">false</fo:inline>.</fo:block>
+ <fo:table border-collapse="separate" margin-bottom="25pt">
+ <fo:table-column column-width="200pt"/>
+ <fo:table-column column-width="200pt"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell height="100pt">
+ <fo:block margin-left="50pt" border-style="solid" border-width="70pt 0 0 70pt" border-color="#00f" fox:border-before-start-radius="100pt" height="100pt">
+ <fo:block padding="50pt" text-align="center">
+ <fo:inline font-style="italic">I</fo:inline>
+ </fo:block>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell height="100pt">
+ <fo:block margin-left="50pt" border-style="solid" border-width="70pt 0 0 70pt" border-color="#00f" fox:border-before-start-radius="50pt" height="100pt">
+ <fo:block padding="50pt" text-align="center">
+ <fo:inline font-style="italic">II</fo:inline>
+ </fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:table border-collapse="separate" margin="40pt 0 20pt 0" break-after="page">
+ <fo:table-column column-width="150pt"/>
+ <fo:table-column column-width="150pt"/>
+ <fo:table-column column-width="150pt"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell number-columns-spanned="3">
+ <fo:block margin-bottom="20pt">The border segments join at a line that coincides with the one that intersects the inner and outer corners of the equivalent rectangular corner. This is clearly demonstrated below where rounded borders are overlayed on top of rectangular borders.</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="50pt" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="100pt" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="150pt 100pt" border-before-width="50pt" border-start-width="50pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="50pt" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="100pt" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ <fo:table-cell border-style="solid" border-width="0" border-before-color="#afa" border-start-color="#aaf" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-top="-50pt" border-style="solid" border-width="0" border-before-color="#0f0" border-start-color="#00f" fox:border-before-start-radius="150pt 100pt" border-before-width="50pt" border-start-width="100pt" height="100pt">
+ <fo:block margin-bottom="100pt"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block margin="20pt 0 40pt 0">
+ <fo:block> </fo:block>
+ <fo:block-container margin="10 20pt 20pt 20pt">
+ <fo:block border-style="solid" border-width="10" border-color="#00f" fox:border-radius="2000pt" padding="20pt" text-align="center" margin-bottom="10pt">If border radii are specified that would produce overlapping curves (as is the case for the block containg <fo:inline font-style="italic">this</fo:inline> text), then all border radii are proportionally reduced until none of them overlap. This is in accordance to the procedure described at http://www.w3.org/TR/css3-background/#corner-overlap.</fo:block>
+ </fo:block-container>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index 2bec97462..dd13797f8 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -4438,7 +4438,7 @@
<Bug pattern="SF_SWITCH_FALLTHROUGH"/>
</Match>
<Match>
- <Class name="org.apache.fop.render.java2d.Java2DBorderPainter"/>
+ <Class name="org.apache.fop.render.java2d.Java2DGraphicsPainter"/>
<Method name="drawLine"/>
<Bug pattern="SF_SWITCH_FALLTHROUGH"/>
</Match>
diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
index eae7a88af..5e58c8208 100644
--- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
+++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
@@ -81,6 +81,7 @@
<xs:complexType>
<xs:attributeGroup ref="mf:rectAtts"/>
<xs:attributeGroup ref="mf:fillAtts"/>
+ <xs:attributeGroup ref="mf:borderAtts"/>
</xs:complexType>
</xs:element>
<xs:element name="line">
@@ -97,10 +98,8 @@
<xs:element name="border-rect">
<xs:complexType>
<xs:attributeGroup ref="mf:rectAtts"/>
- <xs:attribute name="left" type="mf:borderDef"/>
- <xs:attribute name="right" type="mf:borderDef"/>
- <xs:attribute name="top" type="mf:borderDef"/>
- <xs:attribute name="bottom" type="mf:borderDef"/>
+ <xs:attribute name="inner-background-color" type="mf:colorType"/>
+ <xs:attributeGroup ref="mf:borderAtts"/>
</xs:complexType>
</xs:element>
<xs:element name="image">
@@ -128,6 +127,12 @@
<xs:attributeGroup ref="mf:posAtts"/>
<xs:attributeGroup ref="mf:sizeAtts"/>
</xs:attributeGroup>
+ <xs:attributeGroup name="borderAtts">
+ <xs:attribute name="left" type="mf:borderDef"/>
+ <xs:attribute name="right" type="mf:borderDef"/>
+ <xs:attribute name="top" type="mf:borderDef"/>
+ <xs:attribute name="bottom" type="mf:borderDef"/>
+ </xs:attributeGroup>
<xs:attributeGroup name="fillAtts">
<xs:attribute name="fill" type="xs:string" default="none"/>
</xs:attributeGroup>
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");
+ }
+ }
+ }
+
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
index cdb6297b5..ebd143a69 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -260,6 +260,13 @@ public class SVGPainter extends AbstractIFPainter<AbstractSVGDocumentHandler>
}
/** {@inheritDoc} */
+ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ //TODO Implement me!!!
+
+ }
+
+ /** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
return;
@@ -399,4 +406,13 @@ public class SVGPainter extends AbstractIFPainter<AbstractSVGDocumentHandler>
}
}
+ /** {@inheritDoc} */
+ public void fillBackground(Rectangle rect, Paint fill, BorderProps bpsBefore,
+ BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ // Not supported in SVG
+
+ }
+
+
+
}
diff --git a/status.xml b/status.xml
index f565f3eec..e08a24372 100644
--- a/status.xml
+++ b/status.xml
@@ -59,6 +59,9 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Code" dev="PH" type="add" fixes-bug="54041">
+ Added support for Rounded Corners on block areas.
+ </action>
<action context="Renderers" dev="VH" type="add" fixes-bug="54038">
Added PDF/A-2 output option.
</action>
diff --git a/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java
index a370e5dd5..c2fc84551 100644
--- a/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java
+++ b/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java
@@ -23,10 +23,6 @@ import java.awt.Color;
import org.junit.Test;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableCell;
@@ -41,6 +37,10 @@ import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
public class TableCellLayoutManagerTestCase {
// this test aims to check that the first call to addAreas() calls
@@ -57,7 +57,7 @@ public class TableCellLayoutManagerTestCase {
CondLengthProperty clp = mock(CondLengthProperty.class);
when(clp.getLengthValue()).thenReturn(0);
// real border info
- BorderInfo bi = BorderInfo.getInstance(0, clp, Color.BLACK);
+ BorderInfo bi = BorderInfo.getInstance(0, clp, Color.BLACK, clp, clp);
// mock column
TableColumn tcol = mock(TableColumn.class);
when(tcol.getCommonBorderPaddingBackground()).thenReturn(cbpb);
diff --git a/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
new file mode 100644
index 000000000..fd6209bf1
--- /dev/null
+++ b/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.afp;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.impl.DefaultImageContext;
+import org.apache.xmlgraphics.image.loader.impl.DefaultImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageBuffered;
+
+import org.apache.fop.afp.AFPEventProducer;
+import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPResourceManager;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.ImageHandlerRegistry;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.traits.BorderProps;
+
+public class AFPPainterTestCase {
+
+ @Test
+ public void testDrawBorderRect() {
+ // the goal of this test is to check that the drawing of rounded corners in AFP uses a bitmap of the
+ // rounded corners (in fact the whole rectangle with rounded corners). the check is done by verifying
+ // that the AFPImageHandlerRenderedImage.handleImage() method is called
+ // mock
+ AFPPaintingState afpPaintingState = mock(AFPPaintingState.class);
+ when(afpPaintingState.getResolution()).thenReturn(72);
+ // mock
+ EventBroadcaster eventBroadcaster = mock(EventBroadcaster.class);
+ // mock
+ DefaultImageContext defaultImageContext = mock(DefaultImageContext.class);
+ when(defaultImageContext.getSourceResolution()).thenReturn(72000f);
+ // mock
+ DefaultImageSessionContext defaultImageSessionContxt = mock(DefaultImageSessionContext.class);
+ when(defaultImageSessionContxt.getParentContext()).thenReturn(defaultImageContext);
+ when(defaultImageSessionContxt.getTargetResolution()).thenReturn(72000f);
+ // mock
+ ImageBuffered imageBuffered = mock(ImageBuffered.class);
+ // mock
+ ImageManager imageManager = mock(ImageManager.class);
+ // mock
+ AFPImageHandlerRenderedImage afpImageHandlerRenderedImage = mock(AFPImageHandlerRenderedImage.class);
+ // mock
+ ImageHandlerRegistry imageHandlerRegistry = mock(ImageHandlerRegistry.class);
+ when(imageHandlerRegistry.getHandler(any(AFPRenderingContext.class), any(Image.class))).thenReturn(
+ afpImageHandlerRenderedImage);
+ // mock
+ FOUserAgent foUserAgent = mock(FOUserAgent.class);
+ when(foUserAgent.getEventBroadcaster()).thenReturn(eventBroadcaster);
+ when(foUserAgent.getImageSessionContext()).thenReturn(defaultImageSessionContxt);
+ when(foUserAgent.getImageManager()).thenReturn(imageManager);
+ when(foUserAgent.getImageHandlerRegistry()).thenReturn(imageHandlerRegistry);
+ // mock
+ AFPEventProducer afpEventProducer = mock(AFPEventProducer.class);
+ when(AFPEventProducer.Provider.get(eventBroadcaster)).thenReturn(afpEventProducer);
+ // mock
+ AFPResourceManager afpResourceManager = mock(AFPResourceManager.class);
+ when(afpResourceManager.isObjectCached(null)).thenReturn(false);
+ // mock
+ IFContext ifContext = mock(IFContext.class);
+ when(ifContext.getUserAgent()).thenReturn(foUserAgent);
+ // mock
+ AFPDocumentHandler afpDocumentHandler = mock(AFPDocumentHandler.class);
+ when(afpDocumentHandler.getPaintingState()).thenReturn(afpPaintingState);
+ when(afpDocumentHandler.getContext()).thenReturn(ifContext);
+ when(afpDocumentHandler.getResourceManager()).thenReturn(afpResourceManager);
+ when(afpDocumentHandler.cacheRoundedCorner("a2a48964ba2d")).thenReturn("RC000000");
+ // real instance, no mock
+ AFPPainter afpPainter = new AFPPainter(afpDocumentHandler);
+ // build rectangle 200 x 50 (points, which are converted to millipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties
+ int style = Constants.EN_SOLID;
+ BorderProps.Mode mode = BorderProps.Mode.SEPARATE;
+ Color color = Color.BLACK;
+ int borderWidth = 4000;
+ int radiusStart = 30000;
+ int radiusEnd = 30000;
+ BorderProps border1 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border2 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border3 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ BorderProps border4 = new BorderProps(style, borderWidth, radiusStart, radiusEnd, color, mode);
+ try {
+ when(imageManager.convertImage(any(Image.class), any(ImageFlavor[].class), any(Map.class)))
+ .thenReturn(imageBuffered);
+ afpPainter.drawBorderRect(rectangle, border1, border2, border3, border4, Color.WHITE);
+ // note: here we would really like to verify that the second and third arguments passed to
+ // handleImage() are the instances ib and rect declared above but that causes mockito to throw
+ // an exception, probably because we cannot declare the AFPRenderingContext and are forced to
+ // use any(), which forces the use of any() for all arguments
+ verify(afpImageHandlerRenderedImage).handleImage(any(AFPRenderingContext.class),
+ any(Image.class), any(Rectangle.class));
+ } catch (Exception e) {
+ fail("something broke...");
+ }
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
index ebe7f17b5..592335648 100644
--- a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
+++ b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
@@ -29,6 +29,7 @@ import org.w3c.dom.Document;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.render.RenderingContext;
+import org.apache.fop.traits.BorderProps;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
@@ -59,6 +60,10 @@ public class AbstractIFPainterTestCase {
public void clipRect(Rectangle rect) throws IFException {
}
+ public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
+ }
+
public void fillRect(Rectangle rect, Paint fill) throws IFException {
}
@@ -76,6 +81,7 @@ public class AbstractIFPainterTestCase {
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[][] dp,
String text) throws IFException {
}
+
};
FontInfo fontInfo = mock(FontInfo.class);
when(handler.getFontInfo()).thenReturn(fontInfo);
diff --git a/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java
new file mode 100644
index 000000000..61093c629
--- /dev/null
+++ b/test/java/org/apache/fop/render/intermediate/ArcToBezierCurveTransformerTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fop.render.intermediate;
+
+import java.io.IOException;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class ArcToBezierCurveTransformerTestCase {
+
+ @Test
+ public void arcTo() throws Exception {
+ testArcTo(Math.PI / 3, Math.PI / 2, 100, 200, 1000, 1000);
+ }
+
+ private void testArcTo(double startAngle, double endAngle, int xCenter, int yCenter, int width,
+ int height) throws IOException {
+ assertAngleWithinFirstQuadrant(startAngle);
+ assertAngleWithinFirstQuadrant(endAngle);
+ BezierCurvePainter bezierCurvePainter = mock(BezierCurvePainter.class);
+ ArcToBezierCurveTransformer sut = new ArcToBezierCurveTransformer(bezierCurvePainter);
+ sut.arcTo(startAngle, endAngle, xCenter, yCenter, width, height);
+ double tan1 = Math.tan(startAngle);
+ double tan2 = Math.tan(endAngle);
+ double lambda1 = Math.atan(height * tan1 / width);
+ double lambda2 = Math.atan(height * tan2 / width);
+ double xStart = width * Math.cos(lambda1) + xCenter;
+ double yStart = height * Math.sin(lambda1) + yCenter;
+ double xEnd = width * Math.cos(lambda2) + xCenter;
+ double yEnd = height * Math.sin(lambda2) + yCenter;
+ ArgumentCaptor<Integer> xP1Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP1Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> xP2Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP2Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> xP3Captor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> yP3Captor = ArgumentCaptor.forClass(Integer.class);
+ verify(bezierCurvePainter).cubicBezierTo(xP1Captor.capture(), yP1Captor.capture(),
+ xP2Captor.capture(), yP2Captor.capture(), xP3Captor.capture(), yP3Captor.capture());
+ int xP1 = xP1Captor.getValue();
+ int yP1 = yP1Captor.getValue();
+ int xP2 = xP2Captor.getValue();
+ int yP2 = yP2Captor.getValue();
+ int xP3 = xP3Captor.getValue();
+ int yP3 = yP3Captor.getValue();
+ // TODO do more than check the direction of the tangents at the end
+ // points
+ assertEquals((yP1 - yStart) / (xP1 - xStart), -width * width / height / height / tan1, 0.01);
+ assertEquals((yP2 - yEnd) / (xP2 - xEnd), -width * width / height / height / tan2, 0.01);
+ assertEquals((int) xEnd, xP3);
+ assertEquals((int) yEnd, yP3);
+ }
+
+ private void assertAngleWithinFirstQuadrant(double angle) {
+ if (angle <= 0 || angle > Math.PI / 2) {
+ fail("Angle " + angle + " is in (0, " + Math.PI / 2 + ")");
+ }
+ }
+} \ No newline at end of file
diff --git a/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java
new file mode 100644
index 000000000..ab5c18c9e
--- /dev/null
+++ b/test/java/org/apache/fop/render/intermediate/BorderPainterTestCase.java
@@ -0,0 +1,578 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fop.render.intermediate;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.BorderProps.Mode;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class BorderPainterTestCase {
+
+ private static final BorderProps BORDER_PROPS = new BorderProps(Constants.EN_SOLID, 10, 50, 50,
+ Color.BLACK, BorderProps.Mode.SEPARATE);
+
+ @Test
+ public void clipBackground() throws Exception {
+ // Rectangular borders
+ test(new ClipBackgroundTester(0, 0, 10, 10));
+ test(new ClipBackgroundTester(5, 10, 10, 10));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setBorderWidth(1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester());
+ // Rounded corners
+ test(new ClipBackgroundTester(0, 0, 10, 10).setEndBefore(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setEndAfter(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setStartAfter(1, 1));
+ test(new ClipBackgroundTester(0, 0, 10, 10).setStartBefore(1, 1));
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester()
+ .startBorder().setWidth(5).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(10).tester()
+ .startBorder().setWidth(10).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester());
+ test(new ClipBackgroundTester(0, 0, 100, 100)
+ .setCornerRadii(10)
+ .setStartBefore(10, 10)
+ .beforeBorder().setWidth(10).tester());
+ }
+
+ private void test(BorderPainterTester<?> tester) throws IOException {
+ tester.test();
+ }
+
+ @Test (expected = IFException.class)
+ public void drawBordersThrowsIFException() throws Exception {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ doThrow(new IOException()).when(graphicsPainter).saveGraphicsState();
+ new BorderPainter(graphicsPainter).drawBorders(new Rectangle(0, 0, 1000, 1000), BORDER_PROPS,
+ BORDER_PROPS, BORDER_PROPS, BORDER_PROPS, Color.WHITE);
+ }
+
+ @Test
+ public void testDrawRectangularBorders() throws IOException {
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10));
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000));
+ test(new DrawRectangularBordersTester(0, 0, 1000, 1000).setBorderWidth(10)
+ .beforeBorder().setWidth(0).tester());
+ }
+
+ @Test
+ public void testDrawRectangularBordersWithNullBorders() throws IOException, IFException {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ BorderProps nullBorderProps = null;
+ new BorderPainter(graphicsPainter).drawRectangularBorders(new Rectangle(0, 0, 1000, 1000),
+ nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps);
+ verifyZeroInteractions(graphicsPainter);
+ }
+
+ @Test
+ public void drawRoundedBorders() throws Exception {
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10));
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).beforeBorder().setWidth(10).tester());
+ test(new DrawRoundedBordersTester(0, 0, 10, 10).setBorderWidth(10).setCornerRadii(5)
+ .beforeBorder().setWidth(0).tester());
+ test(new DrawRoundedBordersTester(0, 0, 10, 10)
+ .beforeBorder().setWidth(10).tester().endBorder().setWidth(10).tester());
+ test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10));
+ test(new DrawRoundedBordersTester(0, 0, 100, 100).setBorderWidth(15).setCornerRadii(10)
+ .beforeBorder().setWidth(5).tester());
+ test(new DrawRoundedBordersTester(0, 0, 60, 60).setBorderWidth(4).setCornerRadii(30));
+ }
+
+ @Test
+ public void testDrawRoundedBordersWithNullBorders() throws IOException, IFException {
+ GraphicsPainter graphicsPainter = mock(GraphicsPainter.class);
+ BorderProps nullBorderProps = null;
+ new BorderPainter(graphicsPainter).drawRoundedBorders(new Rectangle(0, 0, 1000, 1000),
+ nullBorderProps, nullBorderProps, nullBorderProps, nullBorderProps);
+ verifyZeroInteractions(graphicsPainter);
+ }
+
+ @Test
+ public void testCalculateCornerCorrectionFactor() {
+ calculateCornerCorrectionFactorHelper(30000, 500000);
+ calculateCornerCorrectionFactorHelper(30000, 10000);
+ }
+
+ private void calculateCornerCorrectionFactorHelper(int radius, int rectWidth) {
+ BorderProps borderProps = new BorderProps(Constants.EN_SOLID, 4000, radius, radius, Color.BLACK,
+ BorderProps.Mode.SEPARATE);
+ int rectHeight = rectWidth + 100;
+ double expected = (2 * radius > rectWidth) ? (double) rectWidth / (2 * radius) : 1.0;
+ double actual = BorderPainter.calculateCornerCorrectionFactor(rectWidth, rectHeight, borderProps,
+ borderProps, borderProps, borderProps);
+ assertEquals(expected, actual, 0);
+ }
+
+ private abstract static class BorderPainterTester<T extends BorderPainterTester<?>> {
+
+ protected final Rectangle borderExtent;
+
+ protected BorderProps before;
+
+ protected BorderProps after;
+
+ protected BorderProps start;
+
+ protected BorderProps end;
+
+ protected final GraphicsPainter graphicsPainter;
+
+ protected final BorderPainter sut;
+
+ private final BorderPropsBuilder<T> beforeBuilder;
+
+ private final BorderPropsBuilder<T> afterBuilder;
+
+ private final BorderPropsBuilder<T> startBuilder;
+
+ private final BorderPropsBuilder<T> endBuilder;
+
+ public BorderPainterTester(int xOrigin, int yOrigin, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Cannot test degenerate borders");
+ }
+ beforeBuilder = new BorderPropsBuilder<T>(getThis());
+ afterBuilder = new BorderPropsBuilder<T>(getThis());
+ startBuilder = new BorderPropsBuilder<T>(getThis());
+ endBuilder = new BorderPropsBuilder<T>(getThis());
+ this.borderExtent = new Rectangle(xOrigin, yOrigin, width, height);
+ this.graphicsPainter = mock(GraphicsPainter.class);
+ this.sut = new BorderPainter(graphicsPainter);
+ }
+
+ protected abstract T getThis();
+
+ public BorderPropsBuilder<T> beforeBorder() {
+ return beforeBuilder;
+ }
+
+ public BorderPropsBuilder<T> afterBorder() {
+ return afterBuilder;
+ }
+
+ public BorderPropsBuilder<T> startBorder() {
+ return startBuilder;
+ }
+
+ public BorderPropsBuilder<T> endBorder() {
+ return endBuilder;
+ }
+
+ public T setBorderWidth(int width) {
+ beforeBuilder.setWidth(width);
+ endBuilder.setWidth(width);
+ afterBuilder.setWidth(width);
+ startBuilder.setWidth(width);
+ return getThis();
+ }
+
+ public T setCornerRadii(int radius) {
+ return setCornerRadii(radius, radius);
+ }
+
+ public T setCornerRadii(int xRadius, int yRadius) {
+ setStartBefore(xRadius, yRadius);
+ setEndBefore(xRadius, yRadius);
+ setEndAfter(xRadius, yRadius);
+ setStartAfter(xRadius, yRadius);
+ return getThis();
+ }
+
+ public T setStartBefore(int xRadius, int yRadius) {
+ startBuilder.setRadiusStart(xRadius);
+ beforeBuilder.setRadiusStart(yRadius);
+ return getThis();
+ }
+
+ public T setEndBefore(int xRadius, int yRadius) {
+ endBuilder.setRadiusStart(xRadius);
+ beforeBuilder.setRadiusEnd(yRadius);
+ return getThis();
+ }
+
+ public T setEndAfter(int xRadius, int yRadius) {
+ endBuilder.setRadiusEnd(xRadius);
+ afterBuilder.setRadiusEnd(yRadius);
+ return getThis();
+ }
+
+ public T setStartAfter(int xRadius, int yRadius) {
+ startBuilder.setRadiusEnd(xRadius);
+ afterBuilder.setRadiusStart(yRadius);
+ return getThis();
+ }
+
+ public final void test() throws IOException {
+ before = beforeBuilder.build();
+ after = afterBuilder.build();
+ end = endBuilder.build();
+ start = startBuilder.build();
+ testMethod();
+ }
+
+ protected abstract void testMethod() throws IOException;
+
+ protected static int numberOfNonZeroBorders(BorderProps first, BorderProps... borders) {
+ int i = first.width == 0 ? 0 : 1;
+ for (BorderProps borderProp : borders) {
+ if (borderProp.width > 0) {
+ i++;
+ }
+ }
+ return i;
+ }
+
+ protected int numberOfNonZeroBorders() {
+ return numberOfNonZeroBorders(before, end, after, start);
+ }
+
+ }
+
+ private static class BorderPropsBuilder<T extends BorderPainterTester<?>> {
+
+ private final int style = 0;
+
+ private final Color color = null;
+
+ private final Mode mode = BorderProps.Mode.SEPARATE;
+
+ private int width;
+
+ private int radiusStart;
+
+ private int radiusEnd;
+
+ private final T tester;
+
+ public BorderPropsBuilder(T tester) {
+ this.tester = tester;
+ }
+
+ public T tester() {
+ return tester;
+ }
+
+ public BorderPropsBuilder<T> setWidth(int width) {
+ this.width = width;
+ return this;
+ }
+
+ public BorderPropsBuilder<T> setRadiusStart(int radiusStart) {
+ this.radiusStart = radiusStart;
+ return this;
+ }
+
+ public BorderPropsBuilder<T> setRadiusEnd(int radiusEnd) {
+ this.radiusEnd = radiusEnd;
+ return this;
+ }
+
+ public BorderProps build() {
+ return new BorderProps(style, width, radiusStart, radiusEnd, color, mode);
+ }
+ }
+
+ private static final class DrawRectangularBordersTester
+ extends BorderPainterTester<DrawRectangularBordersTester> {
+
+ public DrawRectangularBordersTester(int xOrigin, int yOrigin, int width, int height)
+ throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public DrawRectangularBordersTester setStartBefore(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setEndBefore(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setEndAfter(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ public DrawRectangularBordersTester setStartAfter(int xRadius, int yRadius) {
+ return notSupported();
+ }
+
+ private DrawRectangularBordersTester notSupported() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void testMethod() throws IOException {
+ sut.drawRectangularBorders(borderExtent, before, after, start, end);
+ verifyDrawing();
+ }
+
+ private void verifyDrawing() throws IOException {
+ final int rectX = borderExtent.x;
+ final int rectY = borderExtent.y;
+ final int rectWidth = borderExtent.width;
+ final int rectHeight = borderExtent.height;
+ if (before.width > 0) {
+ verify(graphicsPainter).moveTo(rectX, rectY);
+ verify(graphicsPainter).lineTo(rectWidth, rectY);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(before, end)))
+ .lineTo(rectWidth - end.width, rectY + before.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(before, start)))
+ .lineTo(rectX + start.width, rectY + before.width);
+ }
+ if (end.width > 0) {
+ verify(graphicsPainter).moveTo(rectWidth, rectY);
+ verify(graphicsPainter).lineTo(rectWidth, rectHeight);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(end, after)))
+ .lineTo(rectWidth - end.width, rectHeight - after.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(end, before)))
+ .lineTo(rectWidth - end.width, rectY + before.width);
+ }
+ if (after.width > 0) {
+ verify(graphicsPainter).moveTo(rectWidth, rectHeight);
+ verify(graphicsPainter).lineTo(rectX, rectHeight);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(after, end)))
+ .lineTo(rectX + start.width, rectHeight - after.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(after, start)))
+ .lineTo(rectWidth - end.width, rectHeight - after.width);
+ }
+ if (start.width > 0) {
+ verify(graphicsPainter).moveTo(rectX, rectHeight);
+ verify(graphicsPainter).lineTo(rectX, rectY);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(start, before)))
+ .lineTo(rectX + start.width, rectY + before.width);
+ verify(graphicsPainter, times(numberOfNonZeroBorders(start, after)))
+ .lineTo(rectX + start.width, rectHeight - after.width);
+ }
+ int numBorders = numberOfNonZeroBorders();
+ verify(graphicsPainter, times(numBorders)).saveGraphicsState();
+ verify(graphicsPainter, times(numBorders)).closePath();
+ verify(graphicsPainter, times(numBorders)).restoreGraphicsState();
+ verify(graphicsPainter, times(numBorders)).clip();
+ }
+
+ @Override
+ protected DrawRectangularBordersTester getThis() {
+ return this;
+ }
+ }
+
+ private static final class DrawRoundedBordersTester extends BorderPainterTester<DrawRoundedBordersTester> {
+
+ public DrawRoundedBordersTester(int xOrigin, int yOrigin, int width, int height) throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public void testMethod() throws IOException {
+ sut.drawRoundedBorders(borderExtent, before, after, start, end);
+ verifyDrawing();
+ }
+
+ private void verifyDrawing() throws IOException {
+ int numBorders = numberOfNonZeroBorders();
+ final int rectWidth = borderExtent.width;
+ final int rectHeight = borderExtent.height;
+ if (before.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - end.getRadiusStart(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, before.width,
+ start.getRadiusStart(), before.getRadiusStart()), before.width);
+ }
+ if (end.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusEnd(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, end.width,
+ before.getRadiusEnd(), end.getRadiusStart()), end.width);
+ }
+ if (after.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectWidth - start.getRadiusEnd(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(start.width, after.width,
+ start.getRadiusEnd(), after.getRadiusStart()), after.width);
+ }
+ if (start.width > 0) {
+ verify(graphicsPainter, atLeastOnce()).lineTo(rectHeight - after.getRadiusStart(), 0);
+ verify(graphicsPainter, atLeastOnce()).lineTo(calcLineEnd(before.width, start.width,
+ before.getRadiusStart(), before.getRadiusStart()), start.width);
+ }
+ // verify the drawing of the symmetric rounded corners (the ones that are a quarter of a circle)
+ // verification is restricted to those since it is too complex in the general case
+ if (before.width == end.width && before.getRadiusStart() == before.getRadiusEnd()
+ && end.getRadiusStart() == end.getRadiusEnd()
+ && before.getRadiusEnd() == end.getRadiusStart() && end.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ before.getRadiusStart(), end.getRadiusEnd(), before.getRadiusStart(),
+ end.getRadiusEnd());
+ }
+ if (end.width == after.width && end.getRadiusStart() == end.getRadiusEnd()
+ && after.getRadiusStart() == after.getRadiusEnd()
+ && end.getRadiusEnd() == after.getRadiusStart() && after.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ end.getRadiusStart(), after.getRadiusEnd(), end.getRadiusStart(),
+ after.getRadiusEnd());
+ }
+ if (after.width == start.width && after.getRadiusStart() == after.getRadiusEnd()
+ && start.getRadiusStart() == start.getRadiusEnd()
+ && after.getRadiusEnd() == start.getRadiusStart() && start.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ after.getRadiusStart(), start.getRadiusEnd(), after.getRadiusStart(),
+ start.getRadiusEnd());
+ }
+ if (start.width == before.width && start.getRadiusStart() == start.getRadiusEnd()
+ && before.getRadiusStart() == before.getRadiusEnd()
+ && start.getRadiusEnd() == before.getRadiusStart() && before.getRadiusStart() > 0) {
+ verify(graphicsPainter, atLeastOnce()).arcTo(Math.PI * 5 / 4, Math.PI * 3 / 2,
+ start.getRadiusStart(), before.getRadiusEnd(), start.getRadiusStart(),
+ before.getRadiusEnd());
+ }
+ verify(graphicsPainter, times(numBorders)).saveGraphicsState();
+ verify(graphicsPainter, times(numBorders)).closePath();
+ verify(graphicsPainter, times(numBorders)).restoreGraphicsState();
+ verify(graphicsPainter, times(numBorders)).clip();
+ }
+
+ private int calcLineEnd(int xWidth, int yWidth, int xRadius, int yRadius) {
+ return yWidth > yRadius ? yWidth : xWidth > 0 ? Math.max(xRadius, xWidth) : 0;
+ }
+
+ @Override
+ protected DrawRoundedBordersTester getThis() {
+ return this;
+ }
+
+ }
+
+ private static final class ClipBackgroundTester extends BorderPainterTester<ClipBackgroundTester> {
+
+ public ClipBackgroundTester(int xOrigin, int yOrigin, int width, int height) throws IOException {
+ super(xOrigin, yOrigin, width, height);
+ }
+
+ public void testMethod() throws IOException {
+ sut.clipBackground(borderExtent, before, after, start, end);
+ verifyClipping();
+ }
+
+ private void verifyClipping() throws IOException {
+ int xOrigin = borderExtent.x;
+ int yOrigin = borderExtent.y;
+ int xEnd = xOrigin + borderExtent.width;
+ int yEnd = yOrigin + borderExtent.height;
+
+ Corner startBeforeCorner = Corner.createStartBeforeCorner(getInnerRadiusStart(start),
+ getInnerRadiusStart(before));
+ Corner endBeforeCorner = Corner.createEndBeforeCorner(getInnerRadiusStart(end), getRadiusEnd(before));
+ Corner endAfterCorner = Corner.createEndAfterCorner(getRadiusEnd(end), getRadiusEnd(after));
+ Corner startAfterCorner = Corner.createStartAfterCorner(getRadiusEnd(start),
+ getInnerRadiusStart(after));
+ verify(graphicsPainter, times(1)).moveTo(xOrigin + startBeforeCorner.xRadius, yOrigin);
+ verify(graphicsPainter, times(1)).lineTo(xEnd - endBeforeCorner.xRadius, yOrigin);
+ endBeforeCorner.verifyCornerDrawn(graphicsPainter, xEnd - endBeforeCorner.xRadius,
+ yOrigin + endBeforeCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xEnd, yEnd - endAfterCorner.yRadius);
+ endAfterCorner.verifyCornerDrawn(graphicsPainter, xEnd - endAfterCorner.xRadius,
+ yEnd - endAfterCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xOrigin + startAfterCorner.xRadius, yEnd);
+ startAfterCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startAfterCorner.xRadius,
+ yEnd - startAfterCorner.yRadius);
+ verify(graphicsPainter, times(1)).lineTo(xOrigin, yOrigin + startBeforeCorner.yRadius);
+ startBeforeCorner.verifyCornerDrawn(graphicsPainter, xOrigin + startBeforeCorner.xRadius,
+ yOrigin + startBeforeCorner.yRadius);
+ verify(graphicsPainter, times(1)).clip();
+ }
+
+ private int getInnerRadiusStart(BorderProps borderProps) {
+ return getInnerRadius(borderProps.getRadiusStart(), borderProps.width);
+ }
+
+ private int getRadiusEnd(BorderProps borderProps) {
+ return getInnerRadius(borderProps.getRadiusEnd(), borderProps.width);
+ }
+
+ private int getInnerRadius(int radius, int borderWidth) {
+ return Math.max(radius - borderWidth, 0);
+ }
+
+ private static class Corner {
+
+ public final int xRadius;
+
+ public final int yRadius;
+
+ private final double startAngle;
+
+ private final double endAngle;
+
+ public Corner(int xRadius, int yRadius, double startAngle, double endAngle) {
+ this.xRadius = xRadius;
+ this.yRadius = yRadius;
+ this.startAngle = startAngle;
+ this.endAngle = endAngle;
+ }
+
+ public static Corner createStartBeforeCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI, Math.PI * 3 / 2);
+ }
+
+ public static Corner createEndBeforeCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI * 3 / 2, 0);
+ }
+
+ public static Corner createEndAfterCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, 0, Math.PI / 2);
+ }
+
+ public static Corner createStartAfterCorner(int xRadius, int yRadius) {
+ return new Corner(xRadius, yRadius, Math.PI / 2, Math.PI);
+ }
+
+ public void verifyCornerDrawn(GraphicsPainter graphicsPainter, int xCenter, int yCenter)
+ throws IOException {
+ if (xRadius != 0 && yRadius != 0) {
+ verify(graphicsPainter, times(1)).arcTo(startAngle, endAngle,
+ xCenter, yCenter, xRadius, yRadius);
+ } else {
+ verify(graphicsPainter, never()).arcTo(startAngle, endAngle,
+ xCenter, yCenter, xRadius, yRadius);
+ }
+ }
+ }
+
+ @Override
+ protected ClipBackgroundTester getThis() {
+ return this;
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java
new file mode 100644
index 000000000..4f3a5e628
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/PDFGraphicsPainterTestCase.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.fop.pdf.PDFNumber;
+
+import static org.mockito.Matchers.endsWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class PDFGraphicsPainterTestCase {
+
+ private PDFGraphicsPainter sut;
+
+ private PDFContentGenerator generator;
+
+ @Before
+ public void setup() {
+ generator = mock(PDFContentGenerator.class);
+ sut = new PDFGraphicsPainter(generator);
+ }
+
+ @Test
+ public void moveTo() {
+ int x = 10;
+ int y = 20;
+ sut.moveTo(x, y);
+ verify(generator).add(op("m", x, y));
+ }
+
+ @Test
+ public void lineTo() {
+ int x = 10;
+ int y = 20;
+ sut.lineTo(x, y);
+ verify(generator).add(op("l", x, y));
+ }
+
+ @Test
+ public void arcTo() throws IOException {
+ int width = 10;
+ int height = 10;
+ int x = 0;
+ int y = 0;
+ double startAngle = 0;
+ double endAngle = Math.PI / 2;
+ sut.arcTo(startAngle, endAngle, x, y, width, height);
+ //TODO stricter verification
+ verify(generator).add(endsWith(" c "));
+ }
+
+ @Test
+ public void closePath() {
+ sut.closePath();
+ verify(generator).add(op("h"));
+ }
+
+ @Test
+ public void clip() {
+ sut.clip();
+ verify(generator).add(opln("W\nn"));
+ }
+
+ @Test
+ public void saveGraphicsState() {
+ sut.saveGraphicsState();
+ verify(generator).add(opln("q"));
+ }
+
+ @Test
+ public void restoreGraphicsState() {
+ sut.restoreGraphicsState();
+ verify(generator).add(opln("Q"));
+ }
+
+ @Test
+ public void rotateCoordinates() throws IOException {
+ double angle = 0;
+ float s = (float) Math.sin(angle);
+ float c = (float) Math.cos(angle);
+ sut.rotateCoordinates(angle);
+ testTransformCoordinatesF(c, s, -s, c, 0, 0);
+ }
+
+ @Test
+ public void translateCoordinates() throws IOException {
+ int x = 10;
+ int y = 20;
+ sut.translateCoordinates(x, y);
+ testTransformCoordinates(1000, 0, 0, 1000, x, y);
+ }
+
+ @Test
+ public void scaleCoordinates() throws IOException {
+ float xScaleFactor = 10f;
+ float yScaleFactor = 2f;
+ sut.scaleCoordinates(xScaleFactor, yScaleFactor);
+ testTransformCoordinatesF(xScaleFactor, 0f, 0f, yScaleFactor, 0f, 0f);
+ }
+
+ @Test
+ public void cubicBezierTo() {
+ int[] args = new int[]{1, 2, 3, 4, 5, 6};
+ sut.cubicBezierTo(args[0], args[1], args[2], args[3], args[4], args[5]);
+ verify(generator).add(op("c", args));
+ }
+
+ private void testTransformCoordinatesF(float... args) {
+ verify(generator).add(opf("cm", args));
+ }
+
+ private void testTransformCoordinates(int... args) {
+ verify(generator).add(op("cm", args));
+ }
+
+ private String opf(String op, float... args) {
+ return opf(op, " ", args);
+ }
+
+ private String op(String op, int... args) {
+ return op(op, " ", args);
+ }
+
+ private String opln(String op, int... args) {
+ return op(op, "\n", args);
+ }
+
+ private String opf(String op, String ending, float... args) {
+ StringBuilder sb = new StringBuilder();
+ for (float arg : args) {
+ sb.append("" + PDFNumber.doubleOut(arg) + " ");
+ }
+ return sb.append(op.trim()).append(ending).toString();
+ }
+
+ private String op(String op, String ending, int... args) {
+ float[] formattedArgs = new float[args.length];
+ for (int i = 0; i < args.length; i++) {
+ formattedArgs[i] = format(args[i]);
+ }
+ return opf(op, ending, formattedArgs);
+ }
+
+ private float format(int i) {
+ return (float) i / 1000;
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
new file mode 100644
index 000000000..f431b3ee9
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.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.render.pdf;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+
+import org.junit.Test;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.traits.BorderProps;
+
+import static org.mockito.Matchers.endsWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class PDFPainterTestCase {
+
+ @Test
+ public void testDrawBorderRect() throws Exception {
+ // the goal of this test is to check that the drawing of rounded corners in PDF calls
+ // PDFGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a " c " command is written
+ // to the PDFContentGenerator
+ PDFContentGenerator pdfContentGenerator = mock(PDFContentGenerator.class);
+ FOUserAgent foUserAgent = mock(FOUserAgent.class);
+ when(foUserAgent.isAccessibilityEnabled()).thenReturn(false);
+ IFContext ifContext = mock(IFContext.class);
+ when(ifContext.getUserAgent()).thenReturn(foUserAgent);
+ PDFDocumentHandler pdfDocumentHandler = mock(PDFDocumentHandler.class);
+ when(pdfDocumentHandler.getGenerator()).thenReturn(pdfContentGenerator);
+ when(pdfDocumentHandler.getContext()).thenReturn(ifContext);
+ PDFLogicalStructureHandler pdfLogicalStructureHandler = mock(PDFLogicalStructureHandler.class);
+ PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, pdfLogicalStructureHandler);
+ // build rectangle 200 x 50 (points, which are converted to milipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties: width 4pt, radius 30pt
+ BorderProps border = new BorderProps(Constants.EN_SOLID, 4000, 30000, 30000, Color.BLACK,
+ BorderProps.Mode.SEPARATE);
+ pdfPainter.drawBorderRect(rectangle, border, border, border, border, Color.WHITE);
+ // since we cannot mock the PDFContentGenerator.format() static method we have to restrict the
+ // verification to commands that end with " c ".
+ verify(pdfContentGenerator, times(16)).add(endsWith(" c "));
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
index 90db3b98f..0a9f552ff 100644
--- a/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
+++ b/test/java/org/apache/fop/render/ps/PSPainterTestCase.java
@@ -16,6 +16,8 @@
*/
package org.apache.fop.render.ps;
+import java.awt.Color;
+import java.awt.Rectangle;
import java.io.IOException;
import java.util.Collections;
@@ -26,9 +28,13 @@ import org.mockito.verification.VerificationMode;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.Constants;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.traits.BorderProps;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyFloat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -75,4 +81,30 @@ public class PSPainterTestCase {
}
verify(gen, test).useColor(state.getTextColor());
}
+
+ @Test
+ public void testDrawBorderRect() {
+ // the goal of this test is to check that the drawing of rounded corners in PS calls
+ // PSGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a curveto command is written
+ // to the PSGenerator
+ PSGenerator psGenerator = mock(PSGenerator.class);
+ when(psGenerator.formatDouble(anyFloat())).thenReturn("20.0"); // simplify!
+ PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class);
+ PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class);
+ when(psDocumentHandler.getGenerator()).thenReturn(psGenerator);
+ when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil);
+ PSPainter psPainter = new PSPainter(psDocumentHandler);
+ // build rectangle 200 x 50 (points, which are converted to milipoints)
+ Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
+ // build border properties: width 4pt, radius 30pt
+ BorderProps border = new BorderProps(Constants.EN_SOLID, 4000, 30000, 30000, Color.BLACK,
+ BorderProps.Mode.SEPARATE);
+ try {
+ psPainter.drawBorderRect(rectangle, border, border, border, border, Color.WHITE);
+ verify(psGenerator, times(16)).writeln("20.0 20.0 20.0 20.0 20.0 20.0 curveto ");
+ } catch (Exception e) {
+ fail("something broke...");
+ }
+ }
+
}
diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
index 25227867b..ec93d708e 100644
--- a/test/java/org/apache/fop/traits/BorderPropsTestCase.java
+++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
@@ -19,16 +19,17 @@
package org.apache.fop.traits;
-import static org.junit.Assert.assertEquals;
-
import java.awt.Color;
+import org.junit.Test;
+
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
import org.apache.fop.fo.Constants;
import org.apache.fop.util.ColorUtil;
-import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
/**
* Tests the BorderProps class.
@@ -44,23 +45,38 @@ public class BorderPropsTestCase {
Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
//Normalize: Avoid false alarms due to color conversion (rounding)
col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col));
-
- BorderProps b1 = new BorderProps(Constants.EN_DOUBLE, 1250,
- col, BorderProps.COLLAPSE_OUTER);
- String ser = b1.toString();
- BorderProps b2 = BorderProps.valueOf(null, ser);
- assertEquals(b1, b2);
+ BorderProps sut = BorderProps.makeRectangular(Constants.EN_DOUBLE, 1250, col,
+ BorderProps.Mode.COLLAPSE_OUTER);
+ testSerialization(sut);
float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f};
col = DeviceCMYKColorSpace.createCMYKColor(cmyk);
//Convert to sRGB with CMYK alternative as constructed by the cmyk() function
float[] rgb = col.getRGBColorComponents(null);
col = new ColorWithAlternatives(rgb[0], rgb[1], rgb[2], new Color[] {col});
- b1 = new BorderProps(Constants.EN_INSET, 9999,
- col, BorderProps.SEPARATE);
- ser = b1.toString();
- b2 = BorderProps.valueOf(null, ser);
- assertEquals(b1, b2);
+ sut = BorderProps.makeRectangular(Constants.EN_INSET, 9999, col, BorderProps.Mode.SEPARATE);
+ testSerialization(sut);
+ }
+
+ /**
+ * Test serialization and deserialization to/from String.
+ * @throws Exception if an error occurs
+ */
+ @Test
+ public void testSerializationWithCornerRadii() throws Exception {
+ Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
+ //Normalize: Avoid false alarms due to color conversion (rounding)
+ col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col));
+ for(BorderProps.Mode mode : BorderProps.Mode.values()) {
+ BorderProps sut = BorderProps.makeRectangular(Constants.EN_SOLID, 10, col, mode);
+ testSerialization(sut);
+ sut = new BorderProps(Constants.EN_SOLID, 10, 4, 3, col, mode);
+ testSerialization(sut);
+ }
+ }
+
+ private void testSerialization(BorderProps borderProp) {
+ assertEquals(borderProp, BorderProps.valueOf(null, borderProp.toString()));
}
}
diff --git a/test/pdf/accessibility/pdf/complete.pdf b/test/pdf/accessibility/pdf/complete.pdf
index 38d9bc499..a9bb3df77 100644
--- a/test/pdf/accessibility/pdf/complete.pdf
+++ b/test/pdf/accessibility/pdf/complete.pdf
Binary files differ