Browse Source

Bugzilla #49913:

Initial code drop for support of rounded corners.
Submitted by: Peter Hancock <peter.hancock.at.gmail.com>

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

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1003020 13f79535-47bb-0310-9956-ffa450edef68
pull/29/head
Jeremias Maerki 13 years ago
parent
commit
c67e3522ea
33 changed files with 2511 additions and 203 deletions
  1. 1
    0
      src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
  2. 113
    30
      src/java/org/apache/fop/afp/AFPResourceManager.java
  3. 1
    1
      src/java/org/apache/fop/afp/AFPStreamer.java
  4. 3
    2
      src/java/org/apache/fop/area/Trait.java
  5. 23
    1
      src/java/org/apache/fop/fo/Constants.java
  6. 71
    0
      src/java/org/apache/fop/fo/FOPropertyMapping.java
  7. 11
    0
      src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
  8. 66
    0
      src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java
  9. 134
    44
      src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
  10. 37
    20
      src/java/org/apache/fop/layoutmgr/TraitSetter.java
  11. 49
    8
      src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
  12. 51
    0
      src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
  13. 47
    5
      src/java/org/apache/fop/render/afp/AFPImageHandler.java
  14. 3
    4
      src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
  15. 2
    2
      src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java
  16. 855
    13
      src/java/org/apache/fop/render/afp/AFPPainter.java
  17. 4
    2
      src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java
  18. 7
    1
      src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
  19. 658
    46
      src/java/org/apache/fop/render/intermediate/BorderPainter.java
  20. 12
    0
      src/java/org/apache/fop/render/intermediate/IFException.java
  21. 28
    1
      src/java/org/apache/fop/render/intermediate/IFPainter.java
  22. 8
    1
      src/java/org/apache/fop/render/intermediate/IFParser.java
  23. 31
    3
      src/java/org/apache/fop/render/intermediate/IFRenderer.java
  24. 25
    1
      src/java/org/apache/fop/render/intermediate/IFSerializer.java
  25. 39
    0
      src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java
  26. 12
    3
      src/java/org/apache/fop/render/java2d/Java2DPainter.java
  27. 12
    2
      src/java/org/apache/fop/render/pcl/PCLPainter.java
  28. 39
    1
      src/java/org/apache/fop/render/pdf/PDFBorderPainter.java
  29. 22
    4
      src/java/org/apache/fop/render/pdf/PDFPainter.java
  30. 54
    0
      src/java/org/apache/fop/render/ps/PSBorderPainter.java
  31. 17
    2
      src/java/org/apache/fop/render/ps/PSPainter.java
  32. 67
    5
      src/java/org/apache/fop/traits/BorderProps.java
  33. 9
    1
      src/sandbox/org/apache/fop/render/svg/SVGPainter.java

+ 1
- 0
src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd View File

@@ -99,6 +99,7 @@
<xs:attribute name="end" type="mf:borderDef"/>
<xs:attribute name="before" type="mf:borderDef"/>
<xs:attribute name="after" type="mf:borderDef"/>
<xs:attribute name="inner-background-color" type="mf:colorType"/>
</xs:complexType>
</xs:element>
<xs:element name="image">

+ 113
- 30
src/java/org/apache/fop/afp/AFPResourceManager.java View File

@@ -61,11 +61,9 @@ public class AFPResourceManager {
/** Maintain a reference count of instream objects for referencing purposes */
private int instreamObjectCount = 0;

/** a mapping of resourceInfo --> include name */
private final Map/*<AFPResourceInfo,String>*/ includeNameMap
= new java.util.HashMap()/*<AFPResourceInfo,String>*/;

private Map pageSegmentMap = new java.util.HashMap();
/** Mapping of resourceInfo to AbstractCachedObject */
private final Map/*<AFPResourceInfo, AbstractCachedObject>*/ includeObjectCache
= new java.util.HashMap()/*<AFPResourceInfo,String>*/;

private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults();

@@ -134,17 +132,7 @@ public class AFPResourceManager {
AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
updateResourceInfoUri(resourceInfo);

String objectName = (String)includeNameMap.get(resourceInfo);
if (objectName != null) {
// an existing data resource so reference it by adding an include to the current page
includeObject(dataObjectInfo, objectName);
return;
}

objectName = (String)pageSegmentMap.get(resourceInfo);
if (objectName != null) {
// an existing data resource so reference it by adding an include to the current page
includePageSegment(dataObjectInfo, objectName);
if (includeCachedObject(resourceInfo, null)) {
return;
}

@@ -170,7 +158,7 @@ public class AFPResourceManager {

useInclude &= resourceGroup != null;
if (useInclude) {
boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
final boolean usePageSegment = dataObjectInfo.isCreatePageSegment();

// if it is to reside within a resource group at print-file or external level
if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
@@ -189,22 +177,109 @@ public class AFPResourceManager {
// add data object into its resource group destination
resourceGroup.addObject(namedObj);

// create the include object
objectName = namedObj.getName();
if (usePageSegment) {
includePageSegment(dataObjectInfo, objectName);
pageSegmentMap.put(resourceInfo, objectName);
} else {
includeObject(dataObjectInfo, objectName);
// record mapping of resource info to data object resource name
includeNameMap.put(resourceInfo, objectName);
}
includeObject(namedObj, dataObjectInfo);
} else {
// not to be included so inline data object directly into the current page
dataStream.getCurrentPage().addObject(namedObj);
}
}

private abstract class AbstractCachedObject {
protected String objectName;
protected AFPDataObjectInfo dataObjectInfo;

public AbstractCachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
this.objectName = objectName;
this.dataObjectInfo = dataObjectInfo;


}
protected abstract void includeObject();
}

private class CachedPageSegment extends AbstractCachedObject {

public CachedPageSegment(String objectName, AFPDataObjectInfo dataObjectInfo) {
super(objectName, dataObjectInfo);
}

protected void includeObject() {
includePageSegment(dataObjectInfo, objectName);
}

}

private class CachedObject extends AbstractCachedObject {

public CachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
super(objectName, dataObjectInfo);
}

protected void includeObject() {
AFPResourceManager.this.includeObject(dataObjectInfo, objectName);
}

}


private void includeObject(AbstractNamedAFPObject namedObj, AFPDataObjectInfo dataObjectInfo) {

// create the include object
AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
String objectName = namedObj.getName();

AbstractCachedObject cachedObject;

if (dataObjectInfo.isCreatePageSegment()) {
cachedObject = new CachedPageSegment(objectName, dataObjectInfo);
} else {
cachedObject = new CachedObject(objectName, dataObjectInfo);
}

cachedObject.includeObject();

includeObjectCache.put(dataObjectInfo.getResourceInfo(), cachedObject);

//The data field of dataObjectInfo is not further required
// therefore we are safe to null the reference, saving memory
dataObjectInfo.setData(null);

}

/**
* TODO
* @param resourceInfo
* @return
*/
public boolean isObjectCached(AFPResourceInfo resourceInfo) {
return includeObjectCache.containsKey(resourceInfo);
}

/**
* TODO
* @param resourceInfo
* @param areaInfo
* @return
*/
public boolean includeCachedObject(AFPResourceInfo resourceInfo, AFPObjectAreaInfo areaInfo) {

String objectName;

AbstractCachedObject cachedObject = (AbstractCachedObject)includeObjectCache.get(resourceInfo);

if (cachedObject != null) {
if (areaInfo != null) {
cachedObject.dataObjectInfo.setObjectAreaInfo(areaInfo);
}
cachedObject.includeObject();

return true;
} else {
return false;
}
}


private void updateResourceInfoUri(AFPResourceInfo resourceInfo) {
String uri = resourceInfo.getUri();
if (uri == null) {
@@ -292,8 +367,12 @@ public class AFPResourceManager {
resourceInfo.setName(resourceName);
resourceInfo.setUri(uri.toASCIIString());

String objectName = (String)includeNameMap.get(resourceInfo);
if (objectName == null) {

AbstractCachedObject cachedObject = (AbstractCachedObject)
includeObjectCache.get(resourceInfo);

if (cachedObject == null ) {

if (log.isDebugEnabled()) {
log.debug("Adding included resource: " + resourceName);
}
@@ -306,8 +385,12 @@ public class AFPResourceManager {

ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
resourceGroup.addObject(resourceObject);

//TODO what is the data object?
cachedObject = new CachedObject(resourceName, null);

// record mapping of resource info to data object resource name
includeNameMap.put(resourceInfo, resourceName);
includeObjectCache.put(resourceInfo, cachedObject);
} else {
//skip, already created
}

+ 1
- 1
src/java/org/apache/fop/afp/AFPStreamer.java View File

@@ -158,7 +158,7 @@ public class AFPStreamer implements Streamable {
*/
// write out any external resource groups
public void close() throws IOException {
Iterator it = pathResourceGroupMap.entrySet().iterator();
Iterator it = pathResourceGroupMap.values().iterator();
while (it.hasNext()) {
StreamedResourceGroup resourceGroup = (StreamedResourceGroup)it.next();
resourceGroup.close();

+ 3
- 2
src/java/org/apache/fop/area/Trait.java View File

@@ -197,12 +197,13 @@ public final class Trait implements Serializable {
public static final Integer OVERLINE_COLOR = new Integer(35);
/** Trait for color of linethrough decorations when rendering inline parent. */
public static final Integer LINETHROUGH_COLOR = new Integer(36);
/** The ptr trait. Used for accessibility */
public static final Integer PTR = new Integer(37);


/** Maximum value used by trait keys */
public static final int MAX_TRAIT_KEY = 37;
public static final int MAX_TRAIT_KEY = 38;

private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];


+ 23
- 1
src/java/org/apache/fop/fo/Constants.java View File

@@ -779,8 +779,30 @@ public interface Constants {
*/
int PR_X_ALT_TEXT = 275;

/** Property constant FOP proprietary*/
int PR_X_BORDER_BEFORE_RADIUS_START = 276;

/** Property constant FOP proprietary*/
int PR_X_BORDER_BEFORE_RADIUS_END = 277;

/** Property constant FOP proprietary*/
int PR_X_BORDER_AFTER_RADIUS_START = 278;
/** Property constant FOP proprietary*/
int PR_X_BORDER_AFTER_RADIUS_END = 279;

/** Property constant FOP proprietary*/
int PR_X_BORDER_START_RADIUS_START = 280;
/** Property constant FOP proprietary*/
int PR_X_BORDER_START_RADIUS_END = 281;
/** Property constant FOP proprietary*/
int PR_X_BORDER_END_RADIUS_START = 282;
/** Property constant FOP proprietary*/
int PR_X_BORDER_END_RADIUS_END = 283;
/** Property constant FOP proprietary*/
int PR_X_BORDER_RADIUS = 284;

/** Number of property constants defined */
int PROPERTY_COUNT = 275;
int PROPERTY_COUNT = 284;

// compound property constants


+ 71
- 0
src/java/org/apache/fop/fo/FOPropertyMapping.java View File

@@ -29,6 +29,7 @@ import org.apache.fop.fo.flow.table.TableFObj.ColumnNumberPropertyMaker;
import org.apache.fop.fo.properties.BackgroundPositionShorthand;
import org.apache.fop.fo.properties.BorderSpacingShorthandParser;
import org.apache.fop.fo.properties.BorderWidthPropertyMaker;
import org.apache.fop.fo.properties.BoxCornerPropShorthandParser;
import org.apache.fop.fo.properties.BoxPropShorthandParser;
import org.apache.fop.fo.properties.CharacterProperty;
import org.apache.fop.fo.properties.ColorProperty;
@@ -97,6 +98,7 @@ public final class FOPropertyMapping implements Constants {
private PropertyMaker genericCondBorderWidth = null;
private PropertyMaker genericBorderWidth = null;
private PropertyMaker genericBorderStyle = null;
private PropertyMaker genericCondCornerRadius = null;
private PropertyMaker genericBreak = null;
private PropertyMaker genericSpace = null;

@@ -201,6 +203,15 @@ public final class FOPropertyMapping implements Constants {
genericBorderStyle.addEnum("outset", getEnumProperty(EN_OUTSET, "OUTSET"));
genericBorderStyle.setDefault("none");

// GenericCondCornerRadius
genericCondCornerRadius = new CondLengthProperty.Maker(0);
genericCondCornerRadius.useGeneric(genericCondLength);
genericCondCornerRadius.setInherited(false);
genericCondCornerRadius.getSubpropMaker(CP_LENGTH).setDefault("0pt");
genericCondCornerRadius.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT);
genericCondCornerRadius.addShorthand(generics[PR_X_BORDER_RADIUS]);


// GenericBreak
genericBreak = new EnumProperty.Maker(0);
genericBreak.setInherited(false);
@@ -2544,6 +2555,59 @@ public final class FOPropertyMapping implements Constants {
m.setDefault("");
addPropertyMaker("fox:alt-text", m);


// fox:border-*-radius-*

// border-before-radius-start
m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");

addPropertyMaker("fox:border-before-radius-start", m);

// border-before-radius-end
m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_END);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-before-radius-end", m);

// border-after-radius-start
m = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_START);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-after-radius-start", m);

// border-after-radius-end
m = new CondLengthProperty.Maker(PR_X_BORDER_AFTER_RADIUS_END);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-after-radius-end", m);

// border-start-radius-before
m = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_START);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-start-radius-before", m);

// border-start-radius-after
m = new CondLengthProperty.Maker(PR_X_BORDER_START_RADIUS_END);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-start-radius-after", m);

// border-end-radius-before
m = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_START);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-end-radius-before", m);

// border-end-radius-after
m = new CondLengthProperty.Maker(PR_X_BORDER_END_RADIUS_END);
m.useGeneric(genericCondCornerRadius);
m.getSubpropMaker(CP_CONDITIONALITY).setDefault("discard");
addPropertyMaker("fox:border-end-radius-after", m);


// provisional-label-separation
m = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION);
m.setInherited(true);
@@ -2696,6 +2760,13 @@ public final class FOPropertyMapping implements Constants {
m.setDatatypeParser(new BoxPropShorthandParser());
addPropertyMaker("border-width", m);

// fox:border-radius
m = new ListProperty.Maker(PR_X_BORDER_RADIUS);
m.setInherited(false);
m.setDatatypeParser(new BoxCornerPropShorthandParser());
m.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH);
addPropertyMaker("fox:border-radius", m);

// cue
m = new ToBeImplementedProperty.Maker(PR_CUE);
m.setInherited(false);

+ 11
- 0
src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java View File

@@ -49,6 +49,17 @@ public class ExtensionElementMapping extends ElementMapping {
PROPERTY_ATTRIBUTES.add("disable-column-balancing");
//These are FOP's extension properties for accessibility
PROPERTY_ATTRIBUTES.add("alt-text");

//fox:border-*-radius-*
PROPERTY_ATTRIBUTES.add("border-before-radius-start");
PROPERTY_ATTRIBUTES.add("border-before-radius-end");
PROPERTY_ATTRIBUTES.add("border-after-radius-start");
PROPERTY_ATTRIBUTES.add("border-after-radius-end");
PROPERTY_ATTRIBUTES.add("border-start-radius-before");
PROPERTY_ATTRIBUTES.add("border-start-radius-after");
PROPERTY_ATTRIBUTES.add("border-end-radius-before");
PROPERTY_ATTRIBUTES.add("border-end-radius-after");
PROPERTY_ATTRIBUTES.add("border-radius");
}

/**

+ 66
- 0
src/java/org/apache/fop/fo/properties/BoxCornerPropShorthandParser.java View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fo.properties;

import org.apache.fop.fo.FOPropertyMapping;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;

/**
* Shorthand property parser for Box rounded corner properties
*/
public class BoxCornerPropShorthandParser extends GenericShorthandParser {

/**
* Default constructor.
*/
public BoxCornerPropShorthandParser() {
}

/**
* Stores 1 or 2 values of same type representing rounded corner radii.
* If 2 value are present the first is the corner radius in the IP direction,
* the second in the BP direction.
* {@inheritDoc}
* int, Property, PropertyMaker, PropertyList)
*/
protected Property convertValueForProperty(int propId,
Property property,
PropertyMaker maker,
PropertyList propertyList)
throws PropertyException {
String name = FOPropertyMapping.getPropertyName(propId);
Property p = null;
int count = property.getList().size();

if (name.indexOf("border-start") > -1 || name.indexOf("border-end") > -1) {
p = getElement(property, 0);
} else if (name.indexOf("border-before") > -1 || name.indexOf("border-after") > -1) {
p = getElement(property, count > 1 ? 1 : 0);
}

// if p not null, try to convert it to a value of the correct type
if (p != null) {
return maker.convertShorthandProperty(propertyList, p, null);
}
return p;
}

}

+ 134
- 44
src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java View File

@@ -96,6 +96,8 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
/** the "end" edge */
public static final int END = 3;



/**
* Utility class to express border info.
*/
@@ -108,32 +110,40 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
private int mStyle; // Enum for border style
private Color mColor; // Border color
private CondLengthProperty mWidth;
private CondLengthProperty radiusStart;
private CondLengthProperty radiusEnd;

private int hash = -1;

/**
* Hidden constructor
*/
private BorderInfo(int style, CondLengthProperty width, Color color) {
private BorderInfo(int style, CondLengthProperty width, Color color,
CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
mStyle = style;
mWidth = width;
mColor = color;
this.radiusStart = radiusStart;
this.radiusEnd = radiusEnd;
}

/**
* Returns a BorderInfo instance corresponding to the given values
* Returns a BorderInfo instance corresponding to the given values.
*
* @param style the border-style
* @param width the border-width
* @param color the border-color
* @param radiusStart the start radius for rounded borders
* @param radiusEnd the end radius for rounded borders
* @return a cached BorderInfo instance
*/
public static BorderInfo getInstance(int style, CondLengthProperty width, Color color) {
return CACHE.fetch(new BorderInfo(style, width, color));
public static BorderInfo getInstance(int style, CondLengthProperty width, Color color,
CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
return CACHE.fetch(new BorderInfo(style, width, color, radiusStart, radiusEnd));
}

/**
* @return the border-style
* @return the border-style
*/
public int getStyle() {
return this.mStyle;
@@ -168,6 +178,20 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
}
}

/**
* @return the border-*-start-radius
*/
public CondLengthProperty getRadiusStart() {
return this.radiusStart;
}

/**
* @return the border-*-end-radius
*/
public CondLengthProperty getRadiusEnd() {
return this.radiusEnd;
}

/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer("BorderInfo");
@@ -177,6 +201,10 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
sb.append(mColor);
sb.append(", ");
sb.append(mWidth);
sb.append(", ");
sb.append(radiusStart);
sb.append(", ");
sb.append(radiusEnd);
sb.append("}");
return sb.toString();
}
@@ -190,8 +218,10 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
if (obj instanceof BorderInfo) {
BorderInfo bi = (BorderInfo)obj;
return (this.mColor == bi.mColor
&& this.mStyle == bi.mStyle
&& this.mWidth == bi.mWidth);
&& this.mStyle == bi.mStyle
&& this.mWidth == bi.mWidth
&& this.radiusStart == bi.radiusStart
&& this.radiusEnd == bi.radiusEnd);
}

return false;
@@ -204,18 +234,27 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
hash = 37 * hash + (mColor == null ? 0 : mColor.hashCode());
hash = 37 * hash + mStyle;
hash = 37 * hash + (mWidth == null ? 0 : mWidth.hashCode());
hash = 37 * hash + (radiusStart == null ? 0 : radiusStart.hashCode());
hash = 37 * hash + (radiusEnd == null ? 0 : radiusEnd.hashCode());
this.hash = hash;
}
return this.hash;
}
}




/**
* A border info with style "none". Used as a singleton, in the collapsing-border model,
* for elements which don't specify any border on some of their sides.
*/
private static final BorderInfo DEFAULT_BORDER_INFO
= BorderInfo.getInstance(Constants.EN_NONE, new ConditionalNullLength(), null);
= BorderInfo.getInstance(Constants.EN_NONE, new ConditionalNullLength(), null,
new ConditionalNullLength(), new ConditionalNullLength());




/**
* A conditional length of value 0. Returned by the
@@ -292,8 +331,11 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl

backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();




Color bc = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
pList.getFObj().getUserAgent());
pList.getFObj().getUserAgent());
if (bc.getAlpha() == 0) {
backgroundColor = null;
} else {
@@ -319,22 +361,30 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
Constants.PR_BORDER_BEFORE_COLOR,
Constants.PR_BORDER_BEFORE_STYLE,
Constants.PR_BORDER_BEFORE_WIDTH,
Constants.PR_PADDING_BEFORE);
Constants.PR_PADDING_BEFORE,
Constants.PR_X_BORDER_BEFORE_RADIUS_START,
Constants.PR_X_BORDER_BEFORE_RADIUS_END);
initBorderInfo(pList, AFTER,
Constants.PR_BORDER_AFTER_COLOR,
Constants.PR_BORDER_AFTER_STYLE,
Constants.PR_BORDER_AFTER_WIDTH,
Constants.PR_PADDING_AFTER);
Constants.PR_PADDING_AFTER,
Constants.PR_X_BORDER_AFTER_RADIUS_START,
Constants.PR_X_BORDER_AFTER_RADIUS_END);
initBorderInfo(pList, START,
Constants.PR_BORDER_START_COLOR,
Constants.PR_BORDER_START_STYLE,
Constants.PR_BORDER_START_WIDTH,
Constants.PR_PADDING_START);
Constants.PR_PADDING_START,
Constants.PR_X_BORDER_START_RADIUS_START,
Constants.PR_X_BORDER_START_RADIUS_END);
initBorderInfo(pList, END,
Constants.PR_BORDER_END_COLOR,
Constants.PR_BORDER_END_STYLE,
Constants.PR_BORDER_END_WIDTH,
Constants.PR_PADDING_END);
Constants.PR_PADDING_END,
Constants.PR_X_BORDER_END_RADIUS_START,
Constants.PR_X_BORDER_END_RADIUS_END);

}

@@ -347,10 +397,10 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
* @throws PropertyException in case of an error
*/
public static CommonBorderPaddingBackground getInstance(PropertyList pList)
throws PropertyException {
throws PropertyException {

CommonBorderPaddingBackground newInstance
= new CommonBorderPaddingBackground(pList);
= new CommonBorderPaddingBackground(pList);
CommonBorderPaddingBackground cachedInstance = null;
/* if padding-* and background-position-* resolve to absolute lengths
* the whole instance can be cached */
@@ -402,21 +452,26 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
}

private void initBorderInfo(PropertyList pList, int side,
int colorProp, int styleProp, int widthProp, int paddingProp)
throws PropertyException {
int colorProp, int styleProp, int widthProp, int paddingProp,
int radiusStartProp, int radiusEndProp)
throws PropertyException {

padding[side] = pList.get(paddingProp).getCondLength();
// If style = none, force width to 0, don't get Color (spec 7.7.20)
int style = pList.get(styleProp).getEnum();
if (style != Constants.EN_NONE) {
// if (style != Constants.EN_NONE) {
FOUserAgent ua = pList.getFObj().getUserAgent();
setBorderInfo(BorderInfo.getInstance(style,
pList.get(widthProp).getCondLength(),
pList.get(colorProp).getColor(ua)), side);
}
pList.get(widthProp).getCondLength(),
pList.get(colorProp).getColor(ua),
pList.get(radiusStartProp).getCondLength(),
pList.get(radiusEndProp).getCondLength()), side);
// }

}



/**
* Sets a border.
* @param info the border information
@@ -539,6 +594,40 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
}
}

/**
* Returns the border corner radius of the starting edge
* i.e. the edge either adjacent to the before or start border.
* @param side the border side
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @param context the context for percentage calculations
* @return the border radius of the of the starting corner
*/
public int getBorderRadiusStart(int side, boolean discard, PercentBaseContext context) {
if (borderInfo[side] == null) {
return 0;
} else {
return borderInfo[side].radiusStart.getLengthValue(context);
}
}

/**
* Returns the border corner radius of the ending edge
* i.e. the edge either adjacent to the after or end border
* @param side the border side
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @param context the context for percentage calculations
* @return the border radius of the of the ending corner
*/
public int getBorderRadiusEnd(int side, boolean discard, PercentBaseContext context) {
if (borderInfo[side] == null) {
return 0;
} else {
return borderInfo[side].radiusEnd.getLengthValue(context);
}
}

/**
* The border-color for the given side
*
@@ -603,9 +692,9 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
*/
public int getIPPaddingAndBorder(boolean discard, PercentBaseContext context) {
return getPaddingStart(discard, context)
+ getPaddingEnd(discard, context)
+ getBorderStartWidth(discard)
+ getBorderEndWidth(discard);
+ getPaddingEnd(discard, context)
+ getBorderStartWidth(discard)
+ getBorderEndWidth(discard);
}

/**
@@ -617,18 +706,18 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
*/
public int getBPPaddingAndBorder(boolean discard, PercentBaseContext context) {
return getPaddingBefore(discard, context) + getPaddingAfter(discard, context)
+ getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
+ getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
}

/** {@inheritDoc} */
public String toString() {
return "CommonBordersAndPadding (Before, After, Start, End):\n"
+ "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
+ getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
+ "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
+ getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
+ "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
+ ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
+ "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
+ getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
+ "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
+ getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
+ "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
+ ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
}

/**
@@ -739,19 +828,20 @@ public class CommonBorderPaddingBackground { // CSOK: FinalCl
if (obj instanceof CommonBorderPaddingBackground) {
CommonBorderPaddingBackground cbpb = (CommonBorderPaddingBackground)obj;
return (this.backgroundAttachment == cbpb.backgroundAttachment
&& this.backgroundColor == cbpb.backgroundColor
&& this.backgroundImage.equals(cbpb.backgroundImage)
&& this.backgroundPositionHorizontal == cbpb.backgroundPositionHorizontal
&& this.backgroundPositionVertical == cbpb.backgroundPositionVertical
&& this.backgroundRepeat == cbpb.backgroundRepeat
&& this.borderInfo[BEFORE] == cbpb.borderInfo[BEFORE]
&& this.borderInfo[AFTER] == cbpb.borderInfo[AFTER]
&& this.borderInfo[START] == cbpb.borderInfo[START]
&& this.borderInfo[END] == cbpb.borderInfo[END]
&& this.padding[BEFORE] == cbpb.padding[BEFORE]
&& this.padding[AFTER] == cbpb.padding[AFTER]
&& this.padding[START] == cbpb.padding[START]
&& this.padding[END] == cbpb.padding[END]);
&& this.backgroundColor == cbpb.backgroundColor
&& this.backgroundImage.equals(cbpb.backgroundImage)
&& this.backgroundPositionHorizontal == cbpb.backgroundPositionHorizontal
&& this.backgroundPositionVertical == cbpb.backgroundPositionVertical
&& this.backgroundRepeat == cbpb.backgroundRepeat
&& this.borderInfo[BEFORE] == cbpb.borderInfo[BEFORE]
&& this.borderInfo[AFTER] == cbpb.borderInfo[AFTER]
&& this.borderInfo[START] == cbpb.borderInfo[START]
&& this.borderInfo[END] == cbpb.borderInfo[END]
&& this.padding[BEFORE] == cbpb.padding[BEFORE]
&& this.padding[AFTER] == cbpb.padding[AFTER]
&& this.padding[START] == cbpb.padding[START]
&& this.padding[END] == cbpb.padding[END]
);
}

return false;

+ 37
- 20
src/java/org/apache/fop/layoutmgr/TraitSetter.java View File

@@ -79,19 +79,19 @@ public final class TraitSetter {

addBorderTrait(area, bpProps, bNotFirst,
CommonBorderPaddingBackground.START,
BorderProps.SEPARATE, Trait.BORDER_START);
BorderProps.SEPARATE, Trait.BORDER_START, context);

addBorderTrait(area, bpProps, bNotLast,
CommonBorderPaddingBackground.END,
BorderProps.SEPARATE, Trait.BORDER_END);
BorderProps.SEPARATE, Trait.BORDER_END, context);

addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.BEFORE,
BorderProps.SEPARATE, Trait.BORDER_BEFORE);
BorderProps.SEPARATE, Trait.BORDER_BEFORE, context);

addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.AFTER,
BorderProps.SEPARATE, Trait.BORDER_AFTER);
BorderProps.SEPARATE, Trait.BORDER_AFTER, context);
}

/**
@@ -104,13 +104,17 @@ public final class TraitSetter {
private static void addBorderTrait(Area area,
CommonBorderPaddingBackground bpProps,
boolean bDiscard, int iSide, int mode,
Object oTrait) {
Object oTrait, PercentBaseContext context) {
int iBP = bpProps.getBorderWidth(iSide, bDiscard);
if (iBP > 0) {
area.addTrait(oTrait,
new BorderProps(bpProps.getBorderStyle(iSide),
iBP, bpProps.getBorderColor(iSide),
mode));
int radiusStart = bpProps.getBorderRadiusStart(iSide, bDiscard, context);
int radiusEnd = bpProps.getBorderRadiusEnd(iSide, bDiscard, context);
if (iBP > 0 || radiusStart > 0 || radiusEnd > 0) {
BorderProps bps = new BorderProps(bpProps.getBorderStyle(iSide),
iBP, bpProps.getBorderColor(iSide),
mode);
bps.setRadiusStart(radiusStart);
bps.setRadiusEnd(radiusEnd);
area.addTrait(oTrait, bps);
}
}

@@ -125,19 +129,19 @@ public final class TraitSetter {
*/
public static void addBorders(Area area, CommonBorderPaddingBackground bordProps,
PercentBaseContext context) {
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE, context);
if (bps != null) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER, context);
if (bps != null) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START, context);
if (bps != null) {
area.addTrait(Trait.BORDER_START, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END, context);
if (bps != null) {
area.addTrait(Trait.BORDER_END, bps);
}
@@ -161,22 +165,23 @@ public final class TraitSetter {
boolean discardBefore, boolean discardAfter,
boolean discardStart, boolean discardEnd,
PercentBaseContext context) {
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE, context);
if (bps != null && !discardBefore) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER, context);
if (bps != null && !discardAfter) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START, context);
if (bps != null && !discardStart) {
area.addTrait(Trait.BORDER_START, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END, context);
if (bps != null && !discardEnd) {
area.addTrait(Trait.BORDER_END, bps);
}

}

/**
@@ -260,14 +265,19 @@ public final class TraitSetter {

}

private static BorderProps getBorderProps(CommonBorderPaddingBackground bordProps, int side) {
private static BorderProps getBorderProps(CommonBorderPaddingBackground bordProps,
int side, PercentBaseContext context) {
int width = bordProps.getBorderWidth(side, false);
if (width != 0) {
int radiusStart = bordProps.getBorderRadiusStart(side, false, context);
int radiusEnd = bordProps.getBorderRadiusEnd(side, false, context);
if (width != 0 || radiusStart != 0 || radiusEnd != 0) {
BorderProps bps;
bps = new BorderProps(bordProps.getBorderStyle(side),
width,
bordProps.getBorderColor(side),
BorderProps.SEPARATE);
bps.setRadiusStart(radiusStart);
bps.setRadiusEnd(radiusEnd);
return bps;
} else {
return null;
@@ -280,12 +290,19 @@ public final class TraitSetter {
if (width != 0) {
BorderProps bps = new BorderProps(borderInfo.getStyle(), width, borderInfo.getColor(),
(outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER));

//TODO Enabling this causes graphic problems. Revisit!
/*
bps.setRadiusStart(borderInfo.getRadiusStart().getLengthValue());
bps.setRadiusEnd(borderInfo.getRadiusEnd().getLengthValue());
*/
return bps;
} else {
return null;
}
}


/**
* Add background to an area. This method is mainly used by table-related layout
* managers to add background for column, body or row. Since the area corresponding to

+ 49
- 8
src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java View File

@@ -48,6 +48,7 @@ import org.apache.fop.area.inline.Viewport;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.render.intermediate.BorderPainter;
import org.apache.fop.traits.BorderProps;

/**
@@ -151,11 +152,20 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
BorderProps bpsStart = (BorderProps)borderArea.getTrait(Trait.BORDER_START);
BorderProps bpsEnd = (BorderProps)borderArea.getTrait(Trait.BORDER_END);

Trait.Background backgroundTrait
= (Trait.Background)backgroundArea.getTrait(Trait.BACKGROUND);

drawBackground(startx, starty, width, height,
(Trait.Background) backgroundArea.getTrait(Trait.BACKGROUND),
bpsBefore, bpsAfter, bpsStart, bpsEnd);

Color bg = null;
if (backgroundTrait != null) {
bg = backgroundTrait.getColor();
}

drawBorders(startx, starty, width, height,
bpsBefore, bpsAfter, bpsStart, bpsEnd);
bpsBefore, bpsAfter, bpsStart, bpsEnd, bg);
}

/**
@@ -199,14 +209,23 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
paddRectHeight -= bpsAfter.width / 1000f;
}

saveGraphicsState();

//TODO remove this choice
if (BorderPainter.isRoundedCornersSupported()) {
clipBackground(sx, sy, paddRectWidth, paddRectHeight,
bpsBefore, bpsAfter, bpsStart, bpsEnd);
} else {
clipRect(sx, sy, paddRectWidth, paddRectHeight);
}

if (back.getColor() != null) {
updateColor(back.getColor(), true);
fillRect(sx, sy, paddRectWidth, paddRectHeight);
}
if (back.getImageInfo() != null) {
ImageSize imageSize = back.getImageInfo().getSize();
saveGraphicsState();
clipRect(sx, sy, paddRectWidth, paddRectHeight);

int horzCount = (int)((paddRectWidth
* 1000 / imageSize.getWidthMpt()) + 1.0f);
int vertCount = (int)((paddRectHeight
@@ -242,12 +261,33 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
drawImage(back.getURL(), pos);
}
}

restoreGraphicsState();
}
restoreGraphicsState();
}
}

/**
* Clip the background to the inner border.
* This draws the border traits given the position and the traits.
*
* @param startx the start x position
* @param starty the start y position
* @param width the width of the area
* @param height the height of the area
* @param bpsBefore the border-before traits
* @param bpsAfter the border-after traits
* @param bpsStart the border-start traits
* @param bpsEnd the border-end traits
*/
protected void clipBackground (float startx, float starty,
float width, float height,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {

clipRect(startx, starty, width, height);

}

/**
* Draw the borders.
* This draws the border traits given the position and the traits.
@@ -264,9 +304,9 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
protected void drawBorders( // CSOK: ParameterNumber
float startx, float starty, float width, float height,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) {
Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height);
drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd, innerBackgroundColor);
}

private static final int BEFORE = 0;
@@ -284,7 +324,8 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
*/
protected void drawBorders( // CSOK: MethodLength
Rectangle2D.Float borderRect,
BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) {
BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd,
Color innerBackgroundColor) {
//TODO generalize each of the four conditions into using a parameterized drawBorder()
boolean[] border = new boolean[] {
(bpsBefore != null), (bpsEnd != null),

+ 51
- 0
src/java/org/apache/fop/render/afp/AFPDocumentHandler.java View File

@@ -23,6 +23,7 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.fop.afp.AFPDitheredRectanglePainter;
@@ -79,6 +80,13 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
private Map/*<String,String>*/pageSegmentMap
= new java.util.HashMap/*<String,String>*/();


// Rounded corners are cached at the document level
private Map/*<String, String>*/ roundedCornerNameCache
= new HashMap()/*<String, String>*/;

private int roundedCornerCount = 0;

/** Medium Map referenced on previous page **/
private String lastMediumMap;

@@ -339,6 +347,49 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
}
}

/**
* Corner images can be reused by storing at the document level in the AFP
* The cache is used to map cahced images to caller generated descriptions of the corner
* @param cornerKey caller's identifier for the corner
* @return document id of the corner image
*/
public String cacheRoundedCorner(String cornerKey) {

// Make a unique id
StringBuffer idBuilder = new StringBuffer("RC");

String tmp = Integer.toHexString(roundedCornerCount).toUpperCase();
if (tmp.length() > 6) {
//Will never happen
//log.error("Rounded corners cache capacity exceeded");
//We should get a visual clue
roundedCornerCount = 0;
tmp = "000000";
} else if (tmp.length() < 6) {
for (int i = 0; i < 6 - tmp.length(); i++) {
idBuilder.append("0");
}
idBuilder.append(tmp);
}

roundedCornerCount++;

String id = idBuilder.toString();

//cache the corner id
roundedCornerNameCache.put(cornerKey, id);
return id;
}
/**
* This method returns the an id that identifies a cached corner or null if non existent
* @param cornerKey caller's identifier for the corner
* @return document id of the corner image
*/
public String getCachedRoundedCorner(String cornerKey) {
return (String)roundedCornerNameCache.get(cornerKey);
}


// ---=== AFPCustomizable ===---

/** {@inheritDoc} */

+ 47
- 5
src/java/org/apache/fop/render/afp/AFPImageHandler.java View File

@@ -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;
}

/**

+ 3
- 4
src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java View File

@@ -26,8 +26,6 @@ import java.io.IOException;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.xmlgraphics.util.MimeConstants;

import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPGraphics2D;
@@ -86,9 +84,10 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH
AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)createDataObjectInfo();

// set resource information
setResourceInformation(graphicsObjectInfo,

graphicsObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
afpContext.getForeignAttributes());
afpContext.getForeignAttributes()));

// Positioning
graphicsObjectInfo.setObjectAreaInfo(

+ 2
- 2
src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java View File

@@ -317,9 +317,9 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler implements Ima
AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)createDataObjectInfo();

// set resource information
setResourceInformation(imageObjectInfo,
imageObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
afpContext.getForeignAttributes());
afpContext.getForeignAttributes()));
setDefaultResourceLevel(imageObjectInfo, afpContext.getResourceManager());

// Positioning

+ 855
- 13
src/java/org/apache/fop/render/afp/AFPPainter.java View File

@@ -21,20 +21,37 @@ package org.apache.fop.render.afp;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Map;

import org.w3c.dom.Document;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;

import org.apache.fop.afp.AFPBorderPainter;
import org.apache.fop.afp.AFPObjectAreaInfo;
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPResourceInfo;
import org.apache.fop.afp.AFPUnitConverter;
import org.apache.fop.afp.AbstractAFPPainter;
import org.apache.fop.afp.BorderPaintingInfo;
@@ -51,6 +68,7 @@ import org.apache.fop.afp.ptoca.PtocaProducer;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.render.ImageHandlerUtil;
import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter;
import org.apache.fop.render.intermediate.BorderPainter;
@@ -66,8 +84,11 @@ import org.apache.fop.util.CharUtilities;
*/
public class AFPPainter extends AbstractIFPainter {




//** logging instance */
//private static Log log = LogFactory.getLog(AFPPainter.class);
private static Log log = LogFactory.getLog(AFPPainter.class);

private static final int X = 0;
private static final int Y = 1;
@@ -90,8 +111,9 @@ public class AFPPainter extends AbstractIFPainter {
super();
this.documentHandler = documentHandler;
this.state = IFState.create();

this.borderPainter = new AFPBorderPainterAdapter(
new AFPBorderPainter(getPaintingState(), getDataStream()));
new AFPBorderPainter(getPaintingState(), getDataStream()), this, documentHandler);
this.rectanglePainter = documentHandler.createRectanglePainter();
this.unitConv = getPaintingState().getUnitConverter();
}
@@ -115,7 +137,7 @@ public class AFPPainter extends AbstractIFPainter {

/** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
throws IFException {
throws IFException {
//AFP doesn't support clipping, so we treat viewport like a group
//this is the same code as for startGroup()
try {
@@ -172,13 +194,13 @@ public class AFPPainter extends AbstractIFPainter {

/** {@inheritDoc} */
protected RenderingContext createRenderingContext() {
AFPRenderingContext psContext = new AFPRenderingContext(
AFPRenderingContext renderingContext = new AFPRenderingContext(
getUserAgent(),
documentHandler.getResourceManager(),
getPaintingState(),
getFontInfo(),
getContext().getForeignAttributes());
return psContext;
return renderingContext;
}

/** {@inheritDoc} */
@@ -196,6 +218,32 @@ public class AFPPainter extends AbstractIFPainter {
}
}

/** {@inheritDoc} */
protected void drawImage(Image image, Rectangle rect,
RenderingContext context, boolean convert, Map additionalHints)
throws IOException, ImageException {


AFPRenderingContext afpContext = (AFPRenderingContext)context;

AFPResourceInfo resourceInfo = AFPImageHandler.createResourceInformation(
image.getInfo().getOriginalURI(),
afpContext.getForeignAttributes());

//Check if the image is cached before processing it again
if (afpContext.getResourceManager().isObjectCached(resourceInfo)) {

AFPObjectAreaInfo areaInfo = AFPImageHandler.createObjectAreaInfo(
afpContext.getPaintingState(), rect);

afpContext.getResourceManager().includeCachedObject(resourceInfo, areaInfo);

} else {
super.drawImage(image, rect, context, convert, additionalHints);
}

}

/** {@inheritDoc} */
public void drawImage(Document doc, Rectangle rect) throws IFException {
drawImageUsingDocument(doc, rect);
@@ -233,24 +281,769 @@ public class AFPPainter extends AbstractIFPainter {

/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
if (before != null || after != null || start != null || end != null) {
try {
this.borderPainter.drawBorders(rect, before, after, start, end);
} catch (IOException ife) {
throw new IFException("IO error while painting borders", ife);
this.borderPainter.drawBorders(rect, before, after, start, end,
innerBackgroundColor);
} catch (IFException ife) {
throw new IFException("Error while painting borders", ife);
}
}
}


//TODO Try to resolve the name-clash between the AFPBorderPainter in the afp package
//and this one. Not done for now to avoid a lot of re-implementation and code duplication.

private static class AFPBorderPainterAdapter extends BorderPainter {


private final class BorderImagePainter implements Graphics2DImagePainter {
private final double esf;
private final Rectangle borderRect;
private final BorderProps bpsStart;
private final BorderProps bpsEnd;
private final BorderProps bpsBefore;
private final BorderProps bpsAfter;
private final boolean[] roundCorner;
private final Color innerBackgroundColor;

private BorderImagePainter(double esf, Rectangle borderRect,
BorderProps bpsStart, BorderProps bpsEnd,
BorderProps bpsBefore, BorderProps bpsAfter,
boolean[] roundCorner, Color innerBackgroundColor) {
this.esf = esf;
this.borderRect = borderRect;
this.bpsStart = bpsStart;
this.bpsBefore = bpsBefore;
this.roundCorner = roundCorner;
this.bpsEnd = bpsEnd;
this.bpsAfter = bpsAfter;
this.innerBackgroundColor = innerBackgroundColor;
}

public void paint(Graphics2D g2d, Rectangle2D area) {

//background
Area background = new Area(area);
Area cornerRegion = new Area();
Area[] cornerBorder
= new Area[]{new Area(), new Area(), new Area(), new Area()};

if (roundCorner[BEFORE_START]) {

AffineTransform transform = new AffineTransform();
int beforeRadius = (int)(esf * bpsBefore.getRadiusStart());
int startRadius = (int)(esf * bpsStart.getRadiusStart());

int beforeWidth = bpsBefore.width;
int startWidth = bpsStart.width;
int corner = BEFORE_START;

background.subtract(makeCornerClip(beforeRadius, startRadius,
transform));

Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
clip.transform(transform);
cornerRegion.add(clip);

cornerBorder[BEFORE].add(makeCornerBorderBPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));

cornerBorder[START].add(makeCornerBorderIPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
}

if (roundCorner[BEFORE_END]) {
AffineTransform transform
= new AffineTransform(-1, 0, 0, 1, borderRect.width, 0);

int beforeRadius = (int)(esf * bpsBefore.getRadiusEnd());
int startRadius = (int)(esf * bpsEnd.getRadiusStart());

int beforeWidth = bpsBefore.width;
int startWidth = bpsEnd.width;
int corner = BEFORE_END;

background.subtract(makeCornerClip(beforeRadius, startRadius,
transform));

Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
clip.transform(transform);
cornerRegion.add(clip);

cornerBorder[BEFORE].add(makeCornerBorderBPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));

cornerBorder[END].add(makeCornerBorderIPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
}

if (roundCorner[AFTER_END]) {
AffineTransform transform = new AffineTransform(-1, 0, 0, -1,
borderRect.width, borderRect.height);

int beforeRadius = (int)(esf * bpsAfter.getRadiusEnd());
int startRadius = (int)(esf * bpsEnd.getRadiusEnd());

int beforeWidth = bpsAfter.width;
int startWidth = bpsEnd.width;
int corner = AFTER_END;

background.subtract(makeCornerClip(beforeRadius, startRadius,
transform));

Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
clip.transform(transform);
cornerRegion.add(clip);

cornerBorder[AFTER].add(makeCornerBorderBPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
cornerBorder[END].add(makeCornerBorderIPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
}

if (roundCorner[AFTER_START]) {
AffineTransform transform
= new AffineTransform(1, 0, 0, -1, 0, borderRect.height);

int beforeRadius = (int)(esf * bpsAfter.getRadiusStart());
int startRadius = (int)(esf * bpsStart.getRadiusEnd());

int beforeWidth = bpsAfter.width;
int startWidth = bpsStart.width;
int corner = AFTER_START;

background.subtract(makeCornerClip(beforeRadius, startRadius,
transform));

Area clip = new Area(new Rectangle(0, 0, startRadius, beforeRadius));
clip.transform(transform);
cornerRegion.add(clip);

cornerBorder[AFTER].add(makeCornerBorderBPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
cornerBorder[START].add(makeCornerBorderIPD(beforeRadius,
startRadius, beforeWidth, startWidth, transform));
}

g2d.setColor(innerBackgroundColor);
g2d.fill(background);

//paint the borders
//TODO refactor to repeating code into method
if (bpsBefore != null && bpsBefore.width > 0) {
GeneralPath borderPath = new GeneralPath();
borderPath.moveTo(0, 0);
borderPath.lineTo(borderRect.width, 0);
borderPath.lineTo(
borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width),
bpsBefore.width);
borderPath.lineTo(bpsStart == null ? 0 : bpsStart.width, bpsBefore.width);

Area border = new Area(borderPath);

border.subtract(cornerRegion);

g2d.setColor(bpsBefore.color);
g2d.fill(border);
g2d.fill(cornerBorder[BEFORE]);
}

if (bpsEnd != null && bpsEnd.width > 0) {
GeneralPath borderPath = new GeneralPath();
borderPath.moveTo(borderRect.width, 0);
borderPath.lineTo(borderRect.width, borderRect.height);
borderPath.lineTo(
borderRect.width - bpsEnd.width,
borderRect.height - (bpsAfter == null ? 0 : bpsAfter.width));
borderPath.lineTo(
borderRect.width - bpsEnd.width,
bpsBefore == null ? 0 : bpsBefore.width);

Area border = new Area(borderPath);

border.subtract(cornerRegion);

g2d.setColor(bpsEnd.color);
g2d.fill(border);
g2d.fill(cornerBorder[END]);
}

if (bpsAfter != null && bpsAfter.width > 0) {
GeneralPath borderPath = new GeneralPath();
borderPath.moveTo(0, borderRect.height);
borderPath.lineTo(borderRect.width, borderRect.height);
borderPath.lineTo(
borderRect.width - (bpsEnd == null ? 0 : bpsEnd.width),
borderRect.height - bpsAfter.width);
borderPath.lineTo(
bpsStart == null ? 0 : bpsStart.width,
borderRect.height - bpsAfter.width);
Area border = new Area(borderPath);

border.subtract(cornerRegion);

g2d.setColor(bpsAfter.color);
g2d.fill(border);
g2d.fill(cornerBorder[AFTER]);
}

if (bpsStart != null && bpsStart.width > 0) {

GeneralPath borderPath = new GeneralPath();
borderPath.moveTo(bpsStart.width,
bpsBefore == null ? 0 : bpsBefore.width);
borderPath.lineTo(bpsStart.width,
borderRect.height - (bpsAfter == null ? 0 : bpsAfter.width));
borderPath.lineTo(0, borderRect.height);
borderPath.lineTo(0, 0);

Area border = new Area(borderPath);

border.subtract(cornerRegion);

g2d.setColor(bpsStart.color);
g2d.fill(border);
g2d.fill(cornerBorder[START]);
}
}

public Dimension getImageSize() {
return borderRect.getSize();
}
}

public static final String CORNER_MODE_PROPERTY = "fop.round-corners.afp";
public static final String MODE_SEPERATE = "seperate";
public static final String MODE_ALL_IN_ONE = "all-in-one";
public static final String MODE_DEFAULT = "all-in-one";

private AFPBorderPainter delegate;
private final AFPPainter painter;
private final AFPDocumentHandler documentHandler;

public AFPBorderPainterAdapter(AFPBorderPainter borderPainter) {
public AFPBorderPainterAdapter(AFPBorderPainter borderPainter, AFPPainter painter,
AFPDocumentHandler documentHandler) {
this.delegate = borderPainter;
this.painter = painter;
this.documentHandler = documentHandler;
}

public void drawBorders(final Rectangle borderRect,
final BorderProps bpsBefore, final BorderProps bpsAfter,
final BorderProps bpsStart, final BorderProps bpsEnd, Color innerBackgroundColor)
throws IFException {

if (isRoundedCornersSupported()) {
if (MODE_SEPERATE.equals(System.getProperty(CORNER_MODE_PROPERTY))) {
try {
drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) {
throw new IFException("Error drawing border", ioe);
}
drawSeperateRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
innerBackgroundColor);
} else {
drawRoundedCorners(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
innerBackgroundColor);
}
} else {
try {
drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) {
throw new IFException("Error drawing border", ioe);
}
}
}

private boolean isModeSeperate() {
return (MODE_SEPERATE.equals(System.getProperty(CORNER_MODE_PROPERTY, MODE_DEFAULT)));
}

private boolean isModeAllInOne() {
return (MODE_ALL_IN_ONE.equals(System.getProperty(CORNER_MODE_PROPERTY, MODE_DEFAULT)));
}

private boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {

boolean rtn = !(isRoundedCornersSupported() && isModeAllInOne()
&& hasRoundedCorners(bpsBefore, bpsAfter,
bpsStart, bpsEnd));
return rtn;
}

private boolean hasRoundedCorners( final BorderProps bpsBefore, final BorderProps bpsAfter,
final BorderProps bpsStart, final BorderProps bpsEnd) {

return ((bpsStart == null ? false : bpsStart.getRadiusStart() > 0)
&& (bpsBefore == null ? false : bpsBefore.getRadiusStart() > 0))
|| ((bpsBefore == null ? false : bpsBefore.getRadiusEnd() > 0)
&& (bpsEnd == null ? false : bpsEnd.getRadiusStart() > 0))
|| ((bpsEnd == null ? false : bpsEnd.getRadiusEnd() > 0)
&& (bpsAfter == null ? false : bpsAfter.getRadiusEnd() > 0))
|| ((bpsAfter == null ? false : bpsAfter.getRadiusStart() > 0)
&& (bpsStart == null ? false : bpsStart.getRadiusEnd() > 0));
}

private void drawSeperateRoundedCorners(final Rectangle borderRect,
final BorderProps bpsBefore, final BorderProps bpsAfter,
final BorderProps bpsStart, final BorderProps bpsEnd,
final Color innerBackgroundColor)
throws IFException {

double esf = cornerScaleFactor(borderRect.width, borderRect.height,
bpsBefore, bpsAfter,
bpsStart, bpsEnd);

if (bpsBefore != null && bpsStart != null) {
int beforeRadiusStart = (int)(esf * bpsBefore.getRadiusStart());
int startRadiusStart = (int)(esf * bpsStart.getRadiusStart());

Rectangle area = new Rectangle(borderRect.x, borderRect.y,
startRadiusStart, beforeRadiusStart);

drawCorner(BEFORE_START, area, bpsBefore, bpsStart,
beforeRadiusStart, startRadiusStart, innerBackgroundColor);
}

if (bpsEnd != null && bpsBefore != null) {
int endRadiusStart = (int)(esf * bpsEnd.getRadiusStart());
int beforeRadiusEnd = (int)(esf * bpsBefore.getRadiusEnd());
Rectangle area = new Rectangle(borderRect.x + borderRect.width - endRadiusStart,
borderRect.y, endRadiusStart, beforeRadiusEnd);

drawCorner(BEFORE_END, area, bpsBefore, bpsEnd,
beforeRadiusEnd, endRadiusStart, innerBackgroundColor);
}


if (bpsEnd != null && bpsAfter != null) {
int endRadiusEnd = (int)(esf * bpsEnd.getRadiusEnd());
int afterRadiusEnd = (int)(esf * bpsAfter.getRadiusEnd());

Rectangle area = new Rectangle(borderRect.x + borderRect.width - endRadiusEnd,
borderRect.y + borderRect.height - afterRadiusEnd,
endRadiusEnd, afterRadiusEnd);

drawCorner(AFTER_END, area, bpsAfter, bpsEnd,
afterRadiusEnd, endRadiusEnd, innerBackgroundColor);

}

if (bpsStart != null && bpsAfter != null) {
int startRadiusEnd = (int)(esf * bpsStart.getRadiusEnd());
int afterRadiusStart = (int)(esf * bpsAfter.getRadiusStart());
Rectangle area = new Rectangle(borderRect.x ,
borderRect.y + borderRect.height - afterRadiusStart,
startRadiusEnd, afterRadiusStart);

drawCorner(AFTER_START, area, bpsAfter, bpsStart,
afterRadiusStart, startRadiusEnd, innerBackgroundColor);
}
}

private void drawRoundedCorners(final Rectangle borderRect,
final BorderProps bpsBefore, final BorderProps bpsAfter,
final BorderProps bpsStart, final BorderProps bpsEnd,
final Color innerBackgroundColor)
throws IFException {


final double esf = cornerScaleFactor(borderRect.width, borderRect.height,
bpsBefore, bpsAfter,
bpsStart, bpsEnd);

final boolean[] roundCorner = new boolean[]{
bpsBefore != null && bpsStart != null
&& bpsBefore.getRadiusStart() > 0
&& bpsStart.getRadiusStart() > 0
&& bpsBefore.mode != BorderProps.COLLAPSE_OUTER
&& bpsStart.mode != BorderProps.COLLAPSE_OUTER,
bpsEnd != null && bpsBefore != null
&& bpsEnd.getRadiusStart() > 0
&& bpsBefore.getRadiusEnd() > 0
&& bpsEnd.mode != BorderProps.COLLAPSE_OUTER
&& bpsBefore.mode != BorderProps.COLLAPSE_OUTER,
bpsEnd != null && bpsAfter != null
&& bpsEnd.getRadiusEnd() > 0
&& bpsAfter.getRadiusEnd() > 0
&& bpsEnd.mode != BorderProps.COLLAPSE_OUTER
&& bpsAfter.mode != BorderProps.COLLAPSE_OUTER,
bpsStart != null && bpsAfter != null
&& bpsStart.getRadiusEnd() > 0
&& bpsAfter.getRadiusStart() > 0
&& bpsStart.mode != BorderProps.COLLAPSE_OUTER
&& bpsAfter.mode != BorderProps.COLLAPSE_OUTER
};


if (!roundCorner[BEFORE_START] && !roundCorner[BEFORE_END]
&& !roundCorner[AFTER_END] && !roundCorner[AFTER_START]) {
try {
drawRectangularBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) {
throw new IFException("IO error drawing borders", ioe);
}
return;
}

String areaKey = makeKey(borderRect,
bpsBefore, bpsEnd, bpsAfter,
bpsStart, innerBackgroundColor);

Graphics2DImagePainter painter = null;
String name = documentHandler.getCachedRoundedCorner(areaKey);

if (name == null) {

name = documentHandler.cacheRoundedCorner(areaKey);

painter = new BorderImagePainter(esf, borderRect,
bpsStart, bpsEnd, bpsBefore, bpsAfter,
roundCorner, innerBackgroundColor);
}
paintCornersAsBitmap(painter, borderRect, name);
}


private Area makeCornerClip(final int beforeRadius, final int startRadius,
final AffineTransform transform) {

Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);

Area clip = new Area(clipR);

Ellipse2D.Double e = new Ellipse2D.Double();
e.x = 0;
e.y = 0;
e.width = 2 * startRadius;
e.height = 2 * beforeRadius;

clip.subtract(new Area(e));

clip.transform(transform);
return clip;
}


private Area makeCornerBorderBPD(final int beforeRadius, final int startRadius,
final int beforeWidth, final int startWidth, final AffineTransform transform) {

Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);

Ellipse2D.Double e = new Ellipse2D.Double();
e.x = 0;
e.y = 0;
e.width = 2 * startRadius;
e.height = 2 * beforeRadius;

Ellipse2D.Double i = new Ellipse2D.Double();
i.x = startWidth;
i.y = beforeWidth;
i.width = 2 * (startRadius - startWidth);
i.height = 2 * (beforeRadius - beforeWidth);

Area clip = new Area(e);
clip.subtract(new Area(i));
clip.intersect(new Area(clipR));

GeneralPath cut = new GeneralPath();
cut.moveTo(0, 0);
float borderWidthRatio = ((float)beforeWidth) / startWidth;
if (beforeWidth * startRadius > startWidth * beforeRadius) {
cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0);

} else {
cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0);
}

clip.intersect(new Area(cut));
clip.transform(transform);
return clip;
}


private Area makeCornerBorderIPD(final int beforeRadius, final int startRadius,
final int beforeWidth, final int startWidth, final AffineTransform transform) {

Rectangle clipR = new Rectangle(0, 0, startRadius, beforeRadius);


Ellipse2D.Double e = new Ellipse2D.Double();
e.x = 0;
e.y = 0;
e.width = 2 * startRadius;
e.height = 2 * beforeRadius;

Ellipse2D.Double i = new Ellipse2D.Double();
i.x = startWidth;
i.y = beforeWidth;
i.width = 2 * (startRadius - startWidth);
i.height = 2 * (beforeRadius - beforeWidth);

Area clip = new Area(e);
clip.subtract(new Area(i));
clip.intersect(new Area(clipR));

GeneralPath cut = new GeneralPath();
cut.moveTo(0, 0);
float borderWidthRatio = ((float)beforeWidth) / startWidth;
if (beforeWidth * startRadius > startWidth * beforeRadius) {
cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0);

} else {
cut.lineTo(startRadius, borderWidthRatio * startRadius);
cut.lineTo(startRadius, 0);
}

clip.subtract(new Area(cut));
clip.transform(transform);
return clip;
}


private void paintCorner(final Graphics2D g2d, final int beforeWidth,
final int startWidth, final int beforeRadius,
final int startRadius, final Color innerBackgroundColor,
final Color beforeColor, final Color startColor) {

//Draw the before-srart corner
Ellipse2D.Double inner = new Ellipse2D.Double();
inner.x = startWidth;
inner.y = beforeWidth;
inner.width = 2 * (startRadius - startWidth);
inner.height = 2 * (beforeRadius - beforeWidth);

Ellipse2D.Double outer = new Ellipse2D.Double();
outer.x = 0;
outer.y = 0;
outer.width = 2 * (startRadius);
outer.height = 2 * (beforeRadius);

Area border = new Area(outer);
border.subtract(new Area(inner));

GeneralPath afterCut = new GeneralPath();
GeneralPath beforeCut = new GeneralPath();
afterCut.moveTo(0, 0);
beforeCut.moveTo(0, 0);
float borderWidthRatio = ((float)beforeWidth) / startWidth;
if (beforeWidth * startRadius > startWidth * beforeRadius) {
afterCut.lineTo(startRadius, borderWidthRatio * startRadius);
beforeCut.lineTo(1f / borderWidthRatio * beforeRadius, beforeRadius);

afterCut.lineTo(startRadius, 0);
beforeCut.lineTo(0, beforeRadius);
} else {
afterCut.lineTo(startRadius, (float)(borderWidthRatio * startRadius));
beforeCut.lineTo(1f / borderWidthRatio * beforeRadius, beforeRadius);

afterCut.lineTo(startRadius, 0);
beforeCut.lineTo(0, beforeRadius);

}

//start
g2d.setColor(startColor);
g2d.fill(border);

//before
border = new Area(outer);
border.subtract(new Area(inner));
border.subtract(new Area(beforeCut));

//start
g2d.setColor(beforeColor);
g2d.fill(border);

//paint background
if (innerBackgroundColor == null) {
g2d.setColor(Color.white);
// log.warn("No background color set");

} else {
g2d.setColor(innerBackgroundColor);
}

g2d.fill(inner);
}


private void drawCorner(final int corner, final Rectangle area,
final BorderProps before, final BorderProps start,
final int beforeRadius, final int startRadius, final Color innerBackground)
throws IFException {

if (beforeRadius > 0 && startRadius > 0) {
String cornerKey = makeCornerKey(corner, before, start,
beforeRadius, startRadius, innerBackground);

Graphics2DImagePainter painter = null;

String name = documentHandler.getCachedRoundedCorner(cornerKey);

// If the corner is not in the cache we construct a Graphics2DImagePainter
// that paints the corner
if (name == null) {
//Cache the name
name = documentHandler.cacheRoundedCorner(cornerKey);

// create the Graphics2DImagePainter
painter = new Graphics2DImagePainter() {
public void paint(Graphics2D g2d, Rectangle2D area) {

int beforeWidth = before.width;
int startWidth = start.width;

Color beforeColor = before.color;
Color startColor = start.color;

//No transformation
AffineTransform t;
switch(corner) {
case BEFORE_START:
//No transform required
break;
case BEFORE_END:
t = new AffineTransform(-1, 0, 0, 1, startRadius, 0);
g2d.transform(t);
break;
case AFTER_END:
t = new AffineTransform(-1, 0, 0, -1, startRadius, beforeRadius);
g2d.transform(t);
break;
case AFTER_START:
t = new AffineTransform(1, 0, 0, -1, 0, beforeRadius);
g2d.transform(t);
break;
default: break;
}

paintCorner(g2d, beforeWidth, startWidth,
beforeRadius, startRadius, innerBackground,
beforeColor, startColor);
}

public Dimension getImageSize() {
return area.getSize();
}
};
}
paintCornersAsBitmap(painter, area, name);
}
}


private String makeCornerKey(int corner, BorderProps beforeProps, BorderProps startProps,
int beforeRadius, int startRadius, Color innerBackgroundColor) {

return hash(new StringBuffer()
.append(corner)
.append(":")
.append(beforeRadius)
.append(":")
.append(startRadius)
.append(":")
.append(beforeProps.width)
.append(":")
.append(startProps.width)
.append(":")
.append(beforeProps.color)
.append(":")
.append(startProps.color)
.append(":")
.append(innerBackgroundColor)
.toString());
}


private String makeKey(Rectangle area, BorderProps beforeProps,
BorderProps endProps, BorderProps afterProps, BorderProps startProps,
Color innerBackgroundColor) {

return hash(new StringBuffer()
.append(area.width)
.append(":")
.append(area.height)
.append(":")
.append(beforeProps)
.append(":")
.append(endProps)
.append(":")
.append(afterProps)
.append(":")
.append(startProps)
.append(":")
.append(innerBackgroundColor)
.toString());
}


private String hash(String text) {

MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (Exception e) {
throw new RuntimeException("Internal error", e);
}

byte[] result = md.digest(text.getBytes());

StringBuffer sb = new StringBuffer();
char[] digits = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
for (int idx = 0; idx < 6; ++idx) {
byte b = result[idx];
sb.append(digits[(b & 0xf0) >> 4]);
sb.append(digits[b & 0x0f]);
}
return sb.toString();
}

private void paintCornersAsBitmap(Graphics2DImagePainter painter,
Rectangle boundingBox, String name) throws IFException {
//TODO parameters ok?
ImageInfo info = new ImageInfo(name, null);

ImageSize size = new ImageSize();
size.setSizeInMillipoints(boundingBox.width, boundingBox.height);

//Use the foreign attributes map to set image handling hints
Map map = new java.util.HashMap(2);
map.put(AFPForeignAttributeReader.RESOURCE_NAME, name);
map.put(AFPForeignAttributeReader.RESOURCE_LEVEL, "print-file");

AFPRenderingContext context = (AFPRenderingContext)
this.painter.createRenderingContext(/*map*/);

size.setResolution(context.getPaintingState().getResolution());
size.calcPixelsFromSize();
info.setSize(size);
ImageGraphics2D img = new ImageGraphics2D(info, painter);

Map hints = new java.util.HashMap();

hints.put(ImageHandlerUtil.CONVERSION_MODE, ImageHandlerUtil.CONVERSION_MODE_BITMAP);
hints.put("TARGET_RESOLUTION",
new Integer(context.getPaintingState().getResolution()));


try {
this.painter.drawImage(img, boundingBox, context, true, hints);
} catch (IOException ioe) {
throw new IFException(
"I/O error while painting corner using a bitmap", ioe);
} catch (ImageException ie) {
throw new IFException(
"Image error while painting corner using a bitmap", ie);
}
}

protected void clip() throws IOException {
@@ -291,11 +1084,11 @@ public class AFPPainter extends AbstractIFPainter {
}

public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IOException {
throws IOException {
if (start.y != end.y) {
//TODO Support arbitrary lines if necessary
throw new UnsupportedOperationException(
"Can only deal with horizontal lines right now");
"Can only deal with horizontal lines right now");
}

//Simply delegates to drawBorderLine() as AFP line painting is not very sophisticated.
@@ -304,11 +1097,45 @@ public class AFPPainter extends AbstractIFPainter {
true, true, style.getEnumValue(), color);
}


protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width,
int height) throws IOException {
throw new UnsupportedOperationException(
"Can only deal with horizontal lines right now");

}


protected void changeCoords(double a, double b, double c, double d, double e, double f) {
throw new UnsupportedOperationException(
"Can only deal with horizontal lines right now");
}

protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
throws IOException {
throw new UnsupportedOperationException(
"Cannot handle cubic Bezier");
}

protected void rotateCoordinates(double angle) throws IOException {
throw new UnsupportedOperationException(
"Cannot handle coordinate rotation");
}

protected void scaleCoordinates(float xScale, float yScale) throws IOException {
throw new UnsupportedOperationException(
"Cannot handle coordinate scaling");
}

protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
throw new UnsupportedOperationException(
"Cannot handle coordinate translation");
}
}

/** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException {
throws IFException {
try {
this.borderPainter.drawLine(start, end, width, color, style);
} catch (IOException ioe) {
@@ -499,4 +1326,19 @@ public class AFPPainter extends AbstractIFPainter {
getPaintingState().restore();
}


/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {

//not supported by AFP
}

/** {@inheritDoc} */
public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
return borderPainter.isBackgroundRequired( bpsBefore, bpsAfter,
bpsStart, bpsEnd);
}

}

+ 4
- 2
src/java/org/apache/fop/render/afp/AbstractAFPImageHandlerRawStream.java View File

@@ -91,9 +91,11 @@ public abstract class AbstractAFPImageHandlerRawStream extends AFPImageHandler
AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();

// set resource information
setResourceInformation(dataObjectInfo,

dataObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
afpContext.getForeignAttributes());
afpContext.getForeignAttributes()));


// Positioning
dataObjectInfo.setObjectAreaInfo(createObjectAreaInfo(afpContext.getPaintingState(), pos));

+ 7
- 1
src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java View File

@@ -315,7 +315,7 @@ public abstract class AbstractIFPainter implements IFPainter {

/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
if (before != null) {
Rectangle b = new Rectangle(
rect.x, rect.y,
@@ -411,4 +411,10 @@ public abstract class AbstractIFPainter implements IFPainter {
return new AffineTransform(matrix);
}

/** {@inheritDoc} */
public boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
return true;
}

}

+ 658
- 46
src/java/org/apache/fop/render/intermediate/BorderPainter.java View File

@@ -32,6 +32,12 @@ import org.apache.fop.traits.RuleStyle;
*/
public abstract class BorderPainter {

public static final String ROUNDED_CORNERS = "fop.round-corners";

protected static final int BEFORE = 0, END = 1, AFTER = 2, START = 3;
protected static final int BEFORE_START = 0, BEFORE_END = 1, AFTER_END = 2, AFTER_START = 3;


/**
* Draws borders.
* @param borderRect the border rectangle
@@ -39,59 +45,94 @@ public abstract class BorderPainter {
* @param bpsAfter the border specification on the after side
* @param bpsStart the border specification on the start side
* @param bpsEnd the border specification on the end side
* @throws IOException if an I/O error occurs while creating the borders
* @throws IFException if an error occurs while drawing the borders
*/
public void drawBorders(Rectangle borderRect, // CSOK: MethodLength
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor)
throws IFException {

try {
if (isRoundedCornersSupported()) {
drawRoundedBorders(borderRect, bpsBefore, bpsAfter,
bpsStart, bpsEnd);

} else {
drawRectangularBorders(borderRect, bpsBefore, bpsAfter,
bpsStart, bpsEnd);
}

} catch (IOException ioe) {
throw new IFException("IO error drawing borders", ioe);
}
}

private BorderProps sanitizeBorderProps(BorderProps bps) {
return bps == null ? bps : bps.width == 0 ? (BorderProps)null : bps;
}

protected void drawRectangularBorders(Rectangle borderRect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IOException {

bpsBefore = sanitizeBorderProps(bpsBefore);
bpsAfter = sanitizeBorderProps(bpsAfter);
bpsStart = sanitizeBorderProps(bpsStart);
bpsEnd = sanitizeBorderProps(bpsEnd);

int startx = borderRect.x;
int starty = borderRect.y;
int width = borderRect.width;
int height = borderRect.height;
boolean[] b = new boolean[] {
(bpsBefore != null), (bpsEnd != null),
(bpsAfter != null), (bpsStart != null)};
(bpsBefore != null), (bpsEnd != null),
(bpsAfter != null), (bpsStart != null)};
if (!b[0] && !b[1] && !b[2] && !b[3]) {
return;
}
int[] bw = new int[] {
(b[0] ? bpsBefore.width : 0),
(b[1] ? bpsEnd.width : 0),
(b[2] ? bpsAfter.width : 0),
(b[3] ? bpsStart.width : 0)};
(b[BEFORE] ? bpsBefore.width : 0),
(b[END] ? bpsEnd.width : 0),
(b[AFTER] ? bpsAfter.width : 0),
(b[3] ? bpsStart.width : 0)};
int[] clipw = new int[] {
BorderProps.getClippedWidth(bpsBefore),
BorderProps.getClippedWidth(bpsEnd),
BorderProps.getClippedWidth(bpsAfter),
BorderProps.getClippedWidth(bpsStart)};
starty += clipw[0];
height -= clipw[0];
height -= clipw[2];
startx += clipw[3];
width -= clipw[3];
width -= clipw[1];
BorderProps.getClippedWidth(bpsBefore),
BorderProps.getClippedWidth(bpsEnd),
BorderProps.getClippedWidth(bpsAfter),
BorderProps.getClippedWidth(bpsStart)};
starty += clipw[BEFORE];
height -= clipw[BEFORE];
height -= clipw[AFTER];
startx += clipw[START];
width -= clipw[START];
width -= clipw[END];

boolean[] slant = new boolean[] {
(b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
(b[START] && b[BEFORE]),
(b[BEFORE] && b[END]),
(b[END] && b[AFTER]),
(b[AFTER] && b[START])};
if (bpsBefore != null) {
int sx1 = startx;
int sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1);
int sx2 = (slant[BEFORE_START] ? sx1 + bw[START] - clipw[START] : sx1);
int ex1 = startx + width;
int ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1);
int outery = starty - clipw[0];
int clipy = outery + clipw[0];
int innery = outery + bw[0];
int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1);
int outery = starty - clipw[BEFORE];
int clipy = outery + clipw[BEFORE];
int innery = outery + bw[BEFORE];

saveGraphicsState();
moveTo(sx1, clipy);


int sx1a = sx1;
int ex1a = ex1;
if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
sx1a -= clipw[3];
sx1a -= clipw[START];
}
if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
ex1a += clipw[1];
ex1a += clipw[END];
}
lineTo(sx1a, outery);
lineTo(ex1a, outery);
@@ -107,12 +148,12 @@ public abstract class BorderPainter {
}
if (bpsEnd != null) {
int sy1 = starty;
int sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1);
int sy2 = (slant[BEFORE_END] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1);
int ey1 = starty + height;
int ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1);
int outerx = startx + width + clipw[1];
int clipx = outerx - clipw[1];
int innerx = outerx - bw[1];
int ey2 = (slant[AFTER_END] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1);
int outerx = startx + width + clipw[END];
int clipx = outerx - clipw[END];
int innerx = outerx - bw[END];

saveGraphicsState();
moveTo(clipx, sy1);
@@ -120,10 +161,10 @@ public abstract class BorderPainter {
int ey1a = ey1;
if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
sy1a -= clipw[0];
sy1a -= clipw[BEFORE];
}
if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
ey1a += clipw[2];
ey1a += clipw[AFTER];
}
lineTo(outerx, sy1a);
lineTo(outerx, ey1a);
@@ -138,12 +179,12 @@ public abstract class BorderPainter {
}
if (bpsAfter != null) {
int sx1 = startx;
int sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1);
int sx2 = (slant[AFTER_START] ? sx1 + bw[START] - clipw[START] : sx1);
int ex1 = startx + width;
int ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1);
int outery = starty + height + clipw[2];
int clipy = outery - clipw[2];
int innery = outery - bw[2];
int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1);
int outery = starty + height + clipw[AFTER];
int clipy = outery - clipw[AFTER];
int innery = outery - bw[AFTER];

saveGraphicsState();
moveTo(ex1, clipy);
@@ -151,10 +192,10 @@ public abstract class BorderPainter {
int ex1a = ex1;
if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
sx1a -= clipw[3];
sx1a -= clipw[START];
}
if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
ex1a += clipw[1];
ex1a += clipw[END];
}
lineTo(ex1a, outery);
lineTo(sx1a, outery);
@@ -169,23 +210,25 @@ public abstract class BorderPainter {
}
if (bpsStart != null) {
int sy1 = starty;
int sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
int sy2 = (slant[BEFORE_START] ? sy1 + bw[BEFORE] - clipw[BEFORE] : sy1);
int ey1 = sy1 + height;
int ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
int outerx = startx - clipw[3];
int clipx = outerx + clipw[3];
int innerx = outerx + bw[3];
int ey2 = (slant[AFTER_START] ? ey1 - bw[AFTER] + clipw[AFTER] : ey1);
int outerx = startx - clipw[START];
int clipx = outerx + clipw[START];
int innerx = outerx + bw[START];

saveGraphicsState();

moveTo(clipx, ey1);

int sy1a = sy1;
int ey1a = ey1;
if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) {
sy1a -= clipw[0];
sy1a -= clipw[BEFORE];
}
if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
ey1a += clipw[2];
ey1a += clipw[AFTER];
}
lineTo(outerx, ey1a);
lineTo(outerx, sy1a);
@@ -200,6 +243,459 @@ public abstract class BorderPainter {
}
}

protected void drawRoundedBorders(Rectangle borderRect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IOException {

bpsBefore = sanitizeBorderProps(bpsBefore);
bpsAfter = sanitizeBorderProps(bpsAfter);
bpsStart = sanitizeBorderProps(bpsStart);
bpsEnd = sanitizeBorderProps(bpsEnd);

boolean[] b = new boolean[] {
(bpsBefore != null), (bpsEnd != null),
(bpsAfter != null), (bpsStart != null)};
if (!b[BEFORE] && !b[END] && !b[AFTER] && !b[START]) {
return;
}
int[] bw = new int[] {
(b[BEFORE] ? bpsBefore.width : 0),
(b[END] ? bpsEnd.width : 0),
(b[AFTER] ? bpsAfter.width : 0),
(b[START] ? bpsStart.width : 0)};

int[] clipw = new int[] {
BorderProps.getClippedWidth(bpsBefore),
BorderProps.getClippedWidth(bpsEnd),
BorderProps.getClippedWidth(bpsAfter),
BorderProps.getClippedWidth(bpsStart)};

final int startx = borderRect.x + clipw[START];
final int starty = borderRect.y + clipw[BEFORE];
final int width = borderRect.width - clipw[START] - clipw[END];
final int height = borderRect.height - clipw[BEFORE] - clipw[AFTER];

boolean[] slant = new boolean[] {
(b[START] && b[BEFORE]), (b[BEFORE] && b[END]),
(b[END] && b[AFTER]), (b[START] && b[AFTER])};

//Determine scale factor if any adjacent elliptic corners overlap
double esf = cornerScaleFactor(width, height, bpsBefore, bpsAfter, bpsStart, bpsEnd);

if (bpsBefore != null) {
//Let x increase in the START->END direction
final int sx2 = (slant[BEFORE_START] ? bw[START] - clipw[START] : 0);
final int ex1 = width;
final int ex2 = (slant[BEFORE_END] ? ex1 - bw[END] + clipw[END] : ex1);
final int outery = -clipw[BEFORE];
final int innery = outery + bw[BEFORE];
final int clipy = outery + clipw[BEFORE];
final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusStart());
final int ellipseSBH = (int)(esf * bpsBefore.getRadiusStart());
final int ellipseSBX = ellipseSBW;
final int ellipseSBY = clipy + ellipseSBH;
final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusStart());
final int ellipseBEH = (int)(esf * bpsBefore.getRadiusEnd());
final int ellipseBEX = ex1 - ellipseBEW;
final int ellipseBEY = clipy + ellipseBEH;

saveGraphicsState();
translateCoordinates(startx, starty);
drawBorderSegment( sx2, ex1, ex2, outery, innery,
clipw[START], clipw[END],
ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
bpsBefore, bpsStart, bpsEnd
);
restoreGraphicsState();
}

if (bpsStart != null) {
//Let x increase in the AFTER->BEFORE direction
final int sx2 = (slant[AFTER_START] ? bw[AFTER] - clipw[AFTER] : 0);
final int ex1 = height;
final int ex2 = (slant[BEFORE_START] ? ex1 - bw[BEFORE] + clipw[BEFORE] : ex1);
final int outery = -clipw[START];
final int innery = outery + bw[START];
final int clipy = outery + clipw[START];
final int ellipseSBW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusStart());
final int ellipseSBH = (int)(esf * bpsStart.getRadiusEnd());
final int ellipseSBX = ellipseSBW;
final int ellipseSBY = clipy + ellipseSBH;
final int ellipseBEW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusStart());
final int ellipseBEH = (int)(esf * bpsStart.getRadiusStart());
final int ellipseBEX = ex1 - ellipseBEW;
final int ellipseBEY = clipy + ellipseBEH;

saveGraphicsState();
translateCoordinates(startx, starty + height);
rotateCoordinates(Math.PI * 3d / 2d);
drawBorderSegment( sx2, ex1, ex2, outery, innery,
clipw[AFTER], clipw[BEFORE],
ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
bpsStart, bpsAfter, bpsBefore
);
restoreGraphicsState();

}


if (bpsAfter != null) {
//Let x increase in the START->END direction
final int sx2 = (slant[AFTER_START] ? bw[START] - clipw[START] : 0);
final int ex1 = width;
final int ex2 = (slant[AFTER_END] ? ex1 - bw[END] + clipw[END] : ex1);
final int outery = -clipw[AFTER];
final int innery = outery + bw[AFTER];
final int clipy = outery + clipw[AFTER];
final int ellipseSBW = bpsStart == null ? 0 : (int)(esf * bpsStart.getRadiusEnd());
final int ellipseSBH = (int)(esf * bpsAfter.getRadiusStart());
final int ellipseSBX = ellipseSBW;
final int ellipseSBY = clipy + ellipseSBH;
final int ellipseBEW = bpsEnd == null ? 0 : (int)(esf * bpsEnd.getRadiusEnd());
final int ellipseBEH = (int)(esf * bpsAfter.getRadiusEnd());
final int ellipseBEX = ex1 - ellipseBEW;
final int ellipseBEY = clipy + ellipseBEH;

saveGraphicsState();
translateCoordinates(startx, starty + height);
scaleCoordinates(1, -1);
drawBorderSegment( sx2, ex1, ex2, outery, innery,
clipw[START], clipw[END],
ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
bpsAfter, bpsStart, bpsEnd
);
restoreGraphicsState();
}

if (bpsEnd != null) {
//Let x increase in the BEFORE-> AFTER direction
final int sx2 = (slant[BEFORE_END] ? bw[BEFORE] - clipw[BEFORE] : 0);
final int ex1 = height;
final int ex2 = (slant[AFTER_END] ? ex1 - bw[AFTER] + clipw[AFTER] : ex1);
final int outery = -clipw[END];
final int innery = outery + bw[END];
final int clipy = outery + clipw[END];
final int ellipseSBW = bpsBefore == null ? 0 : (int)(esf * bpsBefore.getRadiusEnd());
final int ellipseSBH = (int)(esf * bpsEnd.getRadiusStart());
final int ellipseSBX = ellipseSBW;
final int ellipseSBY = clipy + ellipseSBH;
final int ellipseBEW = bpsAfter == null ? 0 : (int)(esf * bpsAfter.getRadiusEnd());
final int ellipseBEH = (int)(esf * bpsEnd.getRadiusEnd());
final int ellipseBEX = ex1 - ellipseBEW;
final int ellipseBEY = clipy + ellipseBEH;

saveGraphicsState();
translateCoordinates(startx + width, starty);
rotateCoordinates(Math.PI / 2d);
drawBorderSegment( sx2, ex1, ex2, outery, innery,
clipw[BEFORE], clipw[AFTER],
ellipseSBX, ellipseSBY, ellipseSBW, ellipseSBH,
ellipseBEX, ellipseBEY, ellipseBEW, ellipseBEH,
bpsEnd, bpsBefore, bpsAfter
);
restoreGraphicsState();
}
}

private void drawBorderSegment(final int sx2, final int ex1, final int ex2,
final int outery, final int innery,
final int clipWidthStart, final int clipWidthEnd,
final int ellipseSBX, final int ellipseSBY,
final int ellipseSBRadiusX, final int ellipseSBRadiusY,
final int ellipseBEX, final int ellipseBEY,
final int ellipseBERadiusX, final int ellipseBERadiusY,
final BorderProps bpsThis, final BorderProps bpsStart, final BorderProps bpsEnd )
throws IOException {

int sx1a = 0;
int ex1a = ex1;


if (ellipseSBRadiusX != 0 && ellipseSBRadiusY != 0 ) {

final double[] joinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX,
ellipseSBRadiusY, (double)innery / sx2);

final double outerJoinPointX = joinMetrics[0];
final double outerJoinPointY = joinMetrics[1];
final double sbJoinAngle = joinMetrics[2];

moveTo((int)outerJoinPointX, (int)outerJoinPointY);
arcTo(Math.PI + sbJoinAngle, Math.PI * 3 / 2,
ellipseSBX, ellipseSBY, ellipseSBRadiusX, ellipseSBRadiusY);
} else {

moveTo(0, 0);

if (bpsThis.mode == BorderProps.COLLAPSE_OUTER) {

if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
sx1a -= clipWidthStart;
}
if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
ex1a += clipWidthEnd;
}

lineTo(sx1a, outery);
lineTo(ex1a, outery);
}
}

if (ellipseBERadiusX != 0) {

final double[] outerJoinMetrics = getCornerBorderJoinMetrics(
ellipseBERadiusX, ellipseBERadiusY, (double)innery / (ex1 - ex2));
final double beJoinAngle = Math.PI / 2 - outerJoinMetrics[2];

lineTo(ellipseBEX, 0);
arcTo( Math.PI * 3 / 2 , Math.PI * 3 / 2 + beJoinAngle,
ellipseBEX, ellipseBEY, ellipseBERadiusX, ellipseBERadiusY);

if (ellipseBEX < ex2 && ellipseBEY > innery) {

final double[] innerJoinMetrics = getCornerBorderJoinMetrics(
(double)ex2 - ellipseBEX, (double)ellipseBEY - innery,
(double)innery / (ex1 - ex2));
final double innerJoinPointX = innerJoinMetrics[0];
final double innerJoinPointY = innerJoinMetrics[1];
final double beInnerJoinAngle = Math.PI / 2 - innerJoinMetrics[2];

lineTo((int) (ex2 - innerJoinPointX), (int)(innerJoinPointY + innery));
arcTo(beInnerJoinAngle + Math.PI * 3 / 2, Math.PI * 3 / 2,
ellipseBEX, ellipseBEY, ex2 - ellipseBEX, ellipseBEY - innery);
} else {
lineTo(ex2, innery);
}

} else {
lineTo(ex1, 0);
lineTo(ex2, innery);
}

if (ellipseSBRadiusX == 0) {
lineTo(sx2, innery);
} else {
if (ellipseSBX > sx2 && ellipseSBY > innery) {


final double[] innerJoinMetrics = getCornerBorderJoinMetrics(ellipseSBRadiusX - sx2,
ellipseSBRadiusY - innery, (double)innery / sx2);

final double sbInnerJoinAngle = innerJoinMetrics[2];

lineTo(ellipseSBX, innery);
arcTo(Math.PI * 3 / 2, sbInnerJoinAngle + Math.PI,
ellipseSBX, ellipseSBY, ellipseSBX - sx2, ellipseSBY - innery);
} else {
lineTo(sx2, innery);
}
}

closePath();
clip();

if (ellipseBERadiusY == 0 && ellipseSBRadiusY == 0) {
drawBorderLine(sx1a, outery, ex1a, innery, true, true,
bpsThis.style, bpsThis.color);

} else {
int innerFillY = Math.max(Math.max(ellipseBEY, ellipseSBY), innery);
drawBorderLine(sx1a, outery, ex1a, innerFillY, true, true,
bpsThis.style, bpsThis.color);
}
}

private double[] getCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
double borderWidthRatio) {

//TODO decide on implementation
boolean invert = System.getProperty("fop.round-corners.border-invert") != null;
if (invert) {
borderWidthRatio = 1d / borderWidthRatio;
}
String cornerJoinStyle = System.getProperty("fop.round-corners.corner-join-style");
if ("css".equals(cornerJoinStyle)) {
return getCSSCornerBorderJoinMetrics(ellipseCenterX, ellipseCenterY, borderWidthRatio);
} else {
if (invert) { throw new RuntimeException("non css AND bw inverted!"); }
return getDefaultCornerBorderJoinMetrics(
ellipseCenterX, ellipseCenterY, borderWidthRatio);
}

}

private double[] getCSSCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
double borderWidthRatio) {

double angle = Math.atan(borderWidthRatio);
double x = ellipseCenterX * Math.cos(Math.atan(ellipseCenterX
/ ellipseCenterY * borderWidthRatio));
double y = ellipseCenterY * Math.sqrt(1d - x * x / ellipseCenterX / ellipseCenterX);

return new double[]{ellipseCenterX - x, ellipseCenterY - y, angle};
}
private double[] getDefaultCornerBorderJoinMetrics(double ellipseCenterX, double ellipseCenterY,
double borderWidthRatio) {

double x = ellipseCenterY * ellipseCenterX * (
ellipseCenterY + ellipseCenterX * borderWidthRatio
- Math.sqrt(2d * ellipseCenterX * ellipseCenterY * borderWidthRatio)
)
/ (ellipseCenterY * ellipseCenterY
+ ellipseCenterX * ellipseCenterX * borderWidthRatio * borderWidthRatio);
double y = borderWidthRatio * x;

return new double[]{x, y, Math.atan((ellipseCenterY - y) / (ellipseCenterX - x))};
}

/**
* Clip the background to the inner border
* @param rect
* @param bpsBefore
* @param bpsAfter
* @param bpsStart
* @param bpsEnd
* @throws IOException if an I/O error occurs
*/
public void clipBackground(Rectangle rect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IOException {
int startx = rect.x;
int starty = rect.y;
int width = rect.width;
int height = rect.height;


int fullWidth = width + ( bpsStart == null ? 0 : bpsStart.width )
+ (bpsStart == null ? 0 : bpsStart.width);
int fullHeight = height + ( bpsBefore == null ? 0 : bpsBefore.width )
+ (bpsAfter == null ? 0 : bpsAfter.width);

double esf = cornerScaleFactor( fullWidth, fullHeight, bpsBefore, bpsAfter,
bpsStart, bpsEnd);

int ellipseSS = 0;
int ellipseBS = 0;
int ellipseBE = 0;
int ellipseES = 0;
int ellipseEE = 0;
int ellipseAE = 0;
int ellipseAS = 0;
int ellipseSE = 0;

if (bpsBefore != null && bpsBefore.getRadiusStart() > 0
&& bpsStart != null && bpsStart.getRadiusStart() > 0) {
ellipseSS = Math.max((int)(bpsStart.getRadiusStart() * esf) - bpsStart.width, 0);
ellipseBS = Math.max((int)(bpsBefore.getRadiusStart() * esf) - bpsBefore.width, 0);
}

if (bpsBefore != null && bpsBefore.getRadiusEnd() > 0
&& bpsEnd != null && bpsEnd.getRadiusStart() > 0) {
ellipseBE = Math.max((int)(bpsBefore.getRadiusEnd() * esf) - bpsBefore.width, 0);
ellipseES = Math.max((int)(bpsEnd.getRadiusStart() * esf) - bpsEnd.width, 0);
}

if (bpsEnd != null && bpsEnd.getRadiusEnd() > 0
&& bpsAfter != null && bpsAfter.getRadiusEnd() > 0) {
ellipseEE = Math.max((int)(bpsEnd.getRadiusEnd() * esf) - bpsEnd.width, 0);
ellipseAE = Math.max((int)(bpsAfter.getRadiusEnd() * esf) - bpsAfter.width, 0);
}

if (bpsAfter != null && bpsAfter.getRadiusStart() > 0
&& bpsStart != null && bpsStart.getRadiusEnd() > 0) {
ellipseAS = Math.max((int)(bpsAfter.getRadiusStart() * esf) - bpsAfter.width, 0);
ellipseSE = Math.max((int)(bpsStart.getRadiusEnd() * esf) - bpsStart.width, 0);
}

// Draw clipping region in the order: Before->End->After->Start
moveTo(startx + ellipseSS, starty);

lineTo(startx + width - ellipseES, starty);

if (ellipseBE > 0 && ellipseES > 0) {
arcTo(Math.PI * 3 / 2, Math.PI * 2,
startx + width - ellipseES, starty + ellipseBE, ellipseES, ellipseBE);
}

lineTo(startx + width, starty + height - ellipseAE);

if (ellipseEE > 0 && ellipseAE > 0) {
arcTo(0, Math.PI / 2, startx + width - ellipseEE,
starty + height - ellipseAE, ellipseEE, ellipseAE);
}

lineTo(startx + ellipseSE, starty + height);

if (ellipseSE > 0 && ellipseSE > 0) {
arcTo( Math.PI / 2, Math.PI, startx + ellipseSE,
starty + height - ellipseAS, ellipseSE, ellipseAS);
}

lineTo( startx, starty + ellipseBS);

if (ellipseSS > 0 && ellipseBS > 0) {
arcTo( Math.PI, Math.PI * 3 / 2,
startx + ellipseSS, starty + ellipseBS, ellipseSS, ellipseBS);
}

clip();

}

/*
* If the ellipse radii exceed the border edge length, the ellipses are rescaled.
*/
protected double cornerScaleFactor(int width, int height,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
// Ellipse scale factor
double esf = 1d;

if (bpsBefore != null) {
if (bpsStart != null && bpsEnd != null
&& bpsStart.getRadiusStart() + bpsEnd.getRadiusStart() > 0) {

double f = (double)width / (bpsStart.getRadiusStart() + bpsEnd.getRadiusStart());
if (f < esf) {
esf = f;
}
}
}
if (bpsStart != null) {
if (bpsAfter != null && bpsBefore != null
&& bpsAfter.getRadiusStart() + bpsBefore.getRadiusStart() > 0) {
double f = (double)height / (bpsAfter.getRadiusStart()
+ bpsBefore.getRadiusStart());
if ( f < esf) {
esf = f;
}
}
}
if (bpsAfter != null) {
if (bpsStart != null && bpsEnd != null
&& bpsStart.getRadiusEnd() + bpsEnd.getRadiusEnd() > 0) {
double f = (double)width / (bpsStart.getRadiusEnd() + bpsEnd.getRadiusEnd());
if (f < esf) {
esf = f;
}
}
}

if (bpsEnd != null) {
if (bpsAfter != null && bpsBefore != null
&& bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd() > 0) {
double f = (double)height / (bpsAfter.getRadiusEnd() + bpsBefore.getRadiusEnd());
if (f < esf) {
esf = f;
}
}
}

return esf;
}

/**
* Draws a border line.
@@ -249,6 +745,118 @@ public abstract class BorderPainter {
*/
protected abstract void lineTo(int x, int y) throws IOException;

/**
* Draw a cubic bezier from current position to (p3x, p3y) using the control points
* (p1x, p1y) and (p2x, p2y)
* @param p1x x coordinate of the first control point
* @param p1y y coordinate of the first control point
* @param p2x x coordinate of the second control point
* @param p2y y coordinate of the second control point
* @param p3x x coordinate of the end point
* @param p3y y coordinate of the end point
* @throws IOException if an I/O error occurs
*/
protected abstract void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
throws IOException;

/**
* Draws an arc on the ellipse centered at (cx, cy) with width width and height height
* from start angle startAngle (with respect to the x-axis counter-clockwise)
* to the end angle endAngle.
* The ellipses major axis are assumed to coincide with the coordinate axis.
* The current position MUST coincide with the starting position on the ellipse.
* @param startAngle the start angle
* @param endAngle the end angle
* @param cx the x coordinate of the ellipse center
* @param cy the y coordinate of the ellipse center
* @param width the extent of the ellipse in the x direction
* @param height the extent of the ellipse in the y direction
* @throws IOException if an I/O error occurs
*/
protected void arcTo(final double startAngle, final double endAngle, final int cx, final int cy,
final int width, final int height)
throws IOException {

// Implementation follows http://www.spaceroots.org/documents/ellipse/ -
// Drawing an elliptical arc using polylines, quadratic or cubic Bézier curves
// L. Maisonobe, July 21, 2003

// Scaling the coordinate system to represent the ellipse as a circle:
final double etaStart = Math.atan(Math.tan(startAngle) * width / height)
+ quadrant(startAngle);
final double etaEnd = Math.atan(Math.tan(endAngle) * width / height)
+ quadrant(endAngle);

final double sinStart = Math.sin(etaStart);
final double cosStart = Math.cos(etaStart);
final double sinEnd = Math.sin(etaEnd);
final double cosEnd = Math.cos(etaEnd);

final double p0x = cx + cosStart * width;
final double p0y = cy + sinStart * height;
final double p3x = cx + cosEnd * width;
final double p3y = cy + sinEnd * height;


double etaDiff = Math.abs(etaEnd - etaStart);
double tan = Math.tan((etaDiff) / 2d);
final double alpha = Math.sin(etaDiff) * (Math.sqrt(4d + 3d * tan * tan) - 1d) / 3d;


int order = etaEnd > etaStart ? 1 : -1;

// p1 = p0 + alpha*(-sin(startAngle), cos(startAngle))
final double p1x = p0x - alpha * sinStart * width * order;
final double p1y = p0y + alpha * cosStart * height * order;

// p1 = p3 + alpha*(sin(endAngle), -cos(endAngle))
final double p2x = p3x + alpha * sinEnd * width * order;
final double p2y = p3y - alpha * cosEnd * height * order;

//Draw the curve in original coordinate system
cubicBezierTo((int)p1x, (int)p1y, (int)p2x, (int)p2y, (int)p3x, (int)p3y);
}

private double quadrant(double angle) {
if (angle <= Math.PI ) {
if (angle <= Math.PI / 2d) {
return 0;
} else {
return Math.PI;
}
} else {
if (angle > Math.PI * 3d / 2d) {
return 2d * Math.PI;
} else {
return Math.PI;
}
}
}

/**
* Rotate the coordinate frame
* @param angle angle in radians to rotate the coordinate frame
* @throws IOException if an I/O error occurs
*/
protected abstract void rotateCoordinates(double angle) throws IOException;

/**
* Translate the coordinate frame
* @param xTranslate translation in the x direction
* @param yTranslate translation in the y direction
* @throws IOException if an I/O error occurs
*/
protected abstract void translateCoordinates(int xTranslate, int yTranslate) throws IOException;

/**
* Scale the coordinate frame
* @param xScale scale factor in the x direction
* @param yScale scale factor in the y direction
* @throws IOException if an I/O error occurs
*/
protected abstract void scaleCoordinates(float xScale, float yScale) throws IOException;


/**
* Closes the current path.
* @throws IOException if an I/O error occurs
@@ -273,4 +881,8 @@ public abstract class BorderPainter {
*/
protected abstract void restoreGraphicsState() throws IOException;

public static boolean isRoundedCornersSupported() {
return "true".equalsIgnoreCase(System.getProperty(ROUNDED_CORNERS, "true"));
}

}

+ 12
- 0
src/java/org/apache/fop/render/intermediate/IFException.java View File

@@ -43,4 +43,16 @@ public class IFException extends Exception {
super(message, cause);
}

/**
* Constructs a new exception with the cause.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <code>null</code> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
/*public IFException( Exception cause) {
super(cause);
}*/

}

+ 28
- 1
src/java/org/apache/fop/render/intermediate/IFPainter.java View File

@@ -166,6 +166,32 @@ public interface IFPainter {
void clipRect(Rectangle rect) throws IFException;
//TODO clipRect() shall be considered temporary until verified with SVG and PCL


/**
* Restricts the current clipping region to the inner border.
* @param rect the rectangle's coordinates and extent
* @param bpsBefore the border segment on the before-side (top)
* @param bpsAfter the border segment on the after-side (bottom)
* @param bpsStart the border segment on the start-side (left)
* @param bpsEnd the border segment on the end-side (right)
* @throws IFException if an error occurs while handling this event
*/
void clipBackground (Rectangle rect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException;


/**
*
* @param bpsBefore
* @param bpsAfter
* @param bpsStart
* @param bpsEnd
* @return true if the background needs to be painted
*/
boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd);

/**
* Fills a rectangular area.
* @param rect the rectangle's coordinates and extent
@@ -182,11 +208,12 @@ public interface IFPainter {
* @param after the border segment on the after-side (bottom)
* @param start the border segment on the start-side (left)
* @param end the border segment on the end-side (right)
* @param innerBackgroundColor the color of the inner background
* @throws IFException if an error occurs while handling this event
*/
void drawBorderRect(Rectangle rect,
BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException;
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException;

/**
* Draws a line. NOTE: Currently, only horizontal lines are implemented!

+ 8
- 1
src/java/org/apache/fop/render/intermediate/IFParser.java View File

@@ -642,9 +642,16 @@ public class IFParser implements IFConstants {
borders[i] = BorderProps.valueOf(userAgent, b);
}
}
Color backgroundColor;

try {
backgroundColor = getAttributeAsColor(attributes, "inner-background-color");
} catch (PropertyException pe) {
throw new IFException("Error parsing the color attribute", pe);
}

painter.drawBorderRect(new Rectangle(x, y, width, height),
borders[0], borders[1], borders[2], borders[3]);
borders[0], borders[1], borders[2], borders[3], backgroundColor);
}

}

+ 31
- 3
src/java/org/apache/fop/render/intermediate/IFRenderer.java View File

@@ -1231,21 +1231,49 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
handleIFException(ife);
}
}

/** {@inheritDoc} */
protected void clipBackground(float startx, float starty,
float width, float height,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
pushGroup(new IFGraphicContext.Group());
Rectangle rect = toMillipointRectangle(startx, starty, width, height);
try {
painter.clipBackground( rect,
bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IFException ife) {
handleIFException(ife);
}
}
/** {@inheritDoc} */
protected void closePath() {
throw new IllegalStateException("Not used");
}

/** {@inheritDoc} */
protected void drawBackground(float startx, float starty,
float width, float height,
Trait.Background back,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
if (painter.isBackgroundRequired(bpsBefore, bpsAfter, bpsStart, bpsEnd)) {
super.drawBackground(startx, starty, width, height,
back, bpsBefore, bpsAfter,
bpsStart, bpsEnd);
}
}

/** {@inheritDoc} */
protected void drawBorders( // CSOK: ParameterNumber
float startx, float starty,
float width, float height,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
BorderProps bpsStart, BorderProps bpsEnd, Color innerBackgroundColor) {
//TODO lose scale?
Rectangle rect = toMillipointRectangle(startx, starty, width, height);
try {
painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd,
innerBackgroundColor);
} catch (IFException ife) {
handleIFException(ife);
}

+ 25
- 1
src/java/org/apache/fop/render/intermediate/IFSerializer.java View File

@@ -33,6 +33,7 @@ import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

@@ -470,6 +471,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
// TODO Auto-generated method stub

}


/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -490,7 +499,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler

/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
if (before == null && after == null && start == null && end == null) {
return;
}
@@ -512,6 +521,12 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
if (end != null) {
addAttribute(atts, "end", end.toString());
}

if (innerBackgroundColor != null) {
addAttribute(atts, "inner-background-color",
ColorUtil.colorToString(innerBackgroundColor));
}

handler.element(EL_BORDER_RECT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in drawBorderRect()", e);
@@ -775,4 +790,13 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
}

/** {@inheritDoc} */
public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
return true;
}




}

+ 39
- 0
src/java/org/apache/fop/render/java2d/Java2DBorderPainter.java View File

@@ -26,6 +26,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -315,4 +316,42 @@ public class Java2DBorderPainter extends BorderPainter {
this.currentPath = null;
}

/** {@inheritDoc} */
protected void arcTo(double startAngle, double endAngle, int cx, int cy, int width, int height)
throws IOException {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
protected void changeCoords(double a, double b, double c, double d, double e, double f) {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
throws IOException {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
protected void rotateCoordinates(double angle) throws IOException {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
protected void scaleCoordinates(float xScale, float yScale) throws IOException {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
// TODO Auto-generated method stub

}

}

+ 12
- 3
src/java/org/apache/fop/render/java2d/Java2DPainter.java View File

@@ -172,6 +172,13 @@ public class Java2DPainter extends AbstractIFPainter {
getState().updateClip(rect);
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
// TODO Auto-generated method stub

}

/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -188,10 +195,10 @@ public class Java2DPainter extends AbstractIFPainter {
BorderProps start, BorderProps end) throws IFException {
if (before != null || after != null || start != null || end != null) {
try {
this.borderPainter.drawBorders(rect, before, after, start, end);
} catch (IOException e) {
this.borderPainter.drawBorders(rect, before, after, start, end, null);
} catch (IFException e) {
//Won't happen with Java2D
throw new IllegalStateException("Unexpected I/O error");
throw new IllegalStateException("Unexpected IF error");
}
}
}
@@ -261,4 +268,6 @@ public class Java2DPainter extends AbstractIFPainter {
g2dState.transform(transform);
}



}

+ 12
- 2
src/java/org/apache/fop/render/pcl/PCLPainter.java View File

@@ -99,7 +99,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {

/** @return the target resolution */
protected int getResolution() {
int resolution = (int)Math.round(getUserAgent().getTargetResolution());
int resolution = Math.round(getUserAgent().getTargetResolution());
if (resolution <= 300) {
return 300;
} else {
@@ -181,6 +181,14 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
//If you need clipping support, switch to RenderingMode.BITMAP.
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
//PCL cannot clip (only HP GL/2 can)
//If you need clipping support, switch to RenderingMode.BITMAP.

}

/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -209,7 +217,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
final BorderProps before, final BorderProps after,
final BorderProps start, final BorderProps end) throws IFException {
if (isSpeedOptimized()) {
super.drawBorderRect(rect, before, after, start, end);
super.drawBorderRect(rect, before, after, start, end, null);
return;
}
if (before != null || after != null || start != null || end != null) {
@@ -529,4 +537,6 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
gen.setCursorPos(transPoint.getX(), transPoint.getY());
}



}

+ 39
- 1
src/java/org/apache/fop/render/pdf/PDFBorderPainter.java View File

@@ -22,6 +22,7 @@ package org.apache.fop.render.pdf;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -53,6 +54,7 @@ public class PDFBorderPainter extends BorderPainter {
protected void drawBorderLine( // CSOK: ParameterNumber
int x1, int y1, int x2, int y2, boolean horz,
boolean startOrBefore, int style, Color col) {
//TODO lose scale?
drawBorderLine(generator, x1 / 1000f, y1 / 1000f, x2 / 1000f, y2 / 1000f,
horz, startOrBefore, style, col);
}
@@ -68,11 +70,12 @@ public class PDFBorderPainter extends BorderPainter {
float colFactor;
float w = x2 - x1;
float h = y2 - y1;
/*
if ((w < 0) || (h < 0)) {
LOG.error("Negative extent received (w=" + w + ", h=" + h
+ "). Border won't be painted.");
return;
}
}*/
switch (style) {
case Constants.EN_DASHED:
generator.setColor(col, false);
@@ -291,6 +294,7 @@ public class PDFBorderPainter extends BorderPainter {
}

static final String format(int coordinate) {
//TODO lose scale?
return format(coordinate / 1000f);
}

@@ -328,4 +332,38 @@ public class PDFBorderPainter extends BorderPainter {
generator.add("Q\n");
}

/** {@inheritDoc} */
protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) {
generator.add(format(p1x) + " " + format(p1y) + " " + format(p2x) + " " + format(p2y)
+ " " + format(p3x) + " " + format(p3y) + " c ");
}


private void transformCoordinates(int a, int b, int c, int d, int e, int f) {
generator.add( "" + format(a) + " " + format(b) + " " + format(c) + " " + format(d)
+ " " + format(e) + " " + format(f) + " cm ");
}

private void transformCoordinates2(float a, float b, float c, float d, float e, float f) {
generator.add( "" + format(a) + " " + format(b) + " " + format(c) + " " + format(d)
+ " " + format(e) + " " + format(f) + " cm ");
}

/** {@inheritDoc} */
protected void rotateCoordinates(double angle) throws IOException {
float s = (float)Math.sin(angle);
float c = (float)Math.cos(angle);
transformCoordinates2(c, s, -s, c, 0, 0);
}

/** {@inheritDoc} */
protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
transformCoordinates(1000, 0, 0, 1000, xTranslate, yTranslate);
}

/** {@inheritDoc} */
protected void scaleCoordinates(float xScale, float yScale) throws IOException {
transformCoordinates2(xScale, 0, 0, yScale, 0, 0);
}

}

+ 22
- 4
src/java/org/apache/fop/render/pdf/PDFPainter.java View File

@@ -223,6 +223,20 @@ public class PDFPainter extends AbstractIFPainter {
generator.clipRect(rect);
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {

try {
borderPainter.clipBackground(rect,
bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) {
throw new IFException("I/O error while clipping background", ioe);
}

}

/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -256,17 +270,21 @@ public class PDFPainter extends AbstractIFPainter {

/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
if (before != null || after != null || start != null || end != null) {
generator.endTextObject();
try {
this.borderPainter.drawBorders(rect, before, after, start, end);
} catch (IOException ioe) {
throw new IFException("I/O error while drawing borders", ioe);
this.borderPainter.drawBorders(rect, before, after, start, end,
innerBackgroundColor);
} catch (IFException ioe) {
throw new IFException("IF error while drawing borders", ioe);
}
}
}




/** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException {

+ 54
- 0
src/java/org/apache/fop/render/ps/PSBorderPainter.java View File

@@ -327,4 +327,58 @@ public class PSBorderPainter extends BorderPainter {
generator.restoreGraphicsState();
}




/** {@inheritDoc} */
protected void cubicBezierTo(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y)
throws IOException {
StringBuffer sb = new StringBuffer();
sb.append(generator.formatDouble(toPoints(p1x)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(p1y)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(p2x)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(p2y)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(p3x)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(p3y)));
sb.append(" curveto ");
generator.writeln(sb.toString());

}

/** {@inheritDoc} */
protected void rotateCoordinates(double angle) throws IOException {
StringBuffer sb = new StringBuffer();
sb.append(generator.formatDouble(angle * 180d / Math.PI));
sb.append(" ");
sb.append(" rotate ");
generator.writeln(sb.toString());
}

/** {@inheritDoc} */
protected void translateCoordinates(int xTranslate, int yTranslate) throws IOException {
StringBuffer sb = new StringBuffer();
sb.append(generator.formatDouble(toPoints(xTranslate)));
sb.append(" ");
sb.append(generator.formatDouble(toPoints(yTranslate)));
sb.append(" ");
sb.append(" translate ");
generator.writeln(sb.toString());
}

/** {@inheritDoc} */
protected void scaleCoordinates(float xScale, float yScale) throws IOException {
StringBuffer sb = new StringBuffer();
sb.append(generator.formatDouble(xScale));
sb.append(" ");
sb.append(generator.formatDouble(yScale));
sb.append(" ");
sb.append(" scale ");
generator.writeln(sb.toString());
}

}

+ 17
- 2
src/java/org/apache/fop/render/ps/PSPainter.java View File

@@ -208,6 +208,20 @@ public class PSPainter extends AbstractIFPainter {
}
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect,
BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {

try {
borderPainter.clipBackground(rect,
bpsBefore, bpsAfter, bpsStart, bpsEnd);
} catch (IOException ioe) {
throw new IFException("I/O error while clipping background", ioe);
}

}

/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -235,11 +249,12 @@ public class PSPainter extends AbstractIFPainter {

/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
BorderProps start, BorderProps end, Color innerBackgroundColor) throws IFException {
if (before != null || after != null || start != null || end != null) {
try {
endTextObject();
this.borderPainter.drawBorders(rect, before, after, start, end);
this.borderPainter.drawBorders(rect, before, after, start, end,
innerBackgroundColor);
} catch (IOException ioe) {
throw new IFException("I/O error in drawBorderRect()", ioe);
}

+ 67
- 5
src/java/org/apache/fop/traits/BorderProps.java View File

@@ -47,6 +47,12 @@ public class BorderProps implements Serializable {
public Color color; // CSOK: VisibilityModifier
/** Border width */
public int width; // CSOK: VisibilityModifier

private int radiusStart = 0;
private int radiusEnd = 0;



/** Border mode (one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) */
public int mode; // CSOK: VisibilityModifier

@@ -75,6 +81,37 @@ public class BorderProps implements Serializable {
this(getConstantForStyle(style), width, color, mode);
}

/**
*
* @return the radius of the corner adjacent to the before or start border
*/
public int getRadiusStart() {
return radiusStart;
}

/**
*
* @param radiusStart the radius of the corner adjacent to the before or start border
*/
public void setRadiusStart(int radiusStart) {
this.radiusStart = radiusStart;
}

/**
* @return the radius of the corner adjacent to the after or end border
*/
public int getRadiusEnd() {
return radiusEnd;
}

/**
*
* @param radiusEnd the radius of the corner adjacent to the after or end border
*/
public void setRadiusEnd(int radiusEnd) {
this.radiusEnd = radiusEnd;
}

/**
* @param bp the border properties or null
* @return the effective width of the clipped part of the border
@@ -112,7 +149,9 @@ public class BorderProps implements Serializable {
return (style == other.style)
&& color.equals(other.color)
&& width == other.width
&& mode == other.mode;
&& mode == other.mode
&& radiusStart == other.radiusStart
&& radiusEnd == other.radiusEnd;
}
}
return false;
@@ -154,7 +193,18 @@ public class BorderProps implements Serializable {
throw new IllegalArgumentException(e.getMessage());
}

return new BorderProps(style, width, c, mode);
BorderProps bp = new BorderProps(style, width, c, mode);

found = m.find();
if (found) {
int startRadius = Integer.parseInt(m.group());
m.find();
int endRadius = Integer.parseInt(m.group());
bp.setRadiusStart(startRadius);
bp.setRadiusEnd(endRadius);
}

return bp;
} else {
throw new IllegalArgumentException("BorderProps must be surrounded by parentheses");
}
@@ -170,12 +220,24 @@ public class BorderProps implements Serializable {
sbuf.append(',');
sbuf.append(width);
if (mode != SEPARATE) {
sbuf.append(',');
if (mode == COLLAPSE_INNER) {
sbuf.append("collapse-inner");
sbuf.append(",collapse-inner");
} else {
sbuf.append("collapse-outer");
sbuf.append(",collapse-outer");
}
}

if (radiusStart != 0 || radiusEnd != 0) {
if (mode == SEPARATE) {
// Because of the corner radii properties the mode must be set
// so that the parameter index is consistent
sbuf.append(",separate");
}
sbuf.append(',');
sbuf.append(radiusStart);

sbuf.append(',');
sbuf.append(radiusEnd);
}
sbuf.append(')');
return sbuf.toString();

+ 9
- 1
src/sandbox/org/apache/fop/render/svg/SVGPainter.java View File

@@ -30,7 +30,6 @@ import java.io.IOException;
import java.util.Map;

import org.w3c.dom.Document;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

@@ -267,6 +266,13 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
//TODO Implement me!!!
}

/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
//TODO Implement me!!!

}

/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
@@ -406,4 +412,6 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
}



}

Loading…
Cancel
Save