<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">
/** 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();
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;
}
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()) {
// 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) {
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);
}
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
}
*/
// 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();
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];
*/
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
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;
private PropertyMaker genericCondBorderWidth = null;
private PropertyMaker genericBorderWidth = null;
private PropertyMaker genericBorderStyle = null;
+ private PropertyMaker genericCondCornerRadius = null;
private PropertyMaker genericBreak = null;
private PropertyMaker genericSpace = null;
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);
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);
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);
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");
}
/**
--- /dev/null
+/*
+ * 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;
+ }
+
+}
/** the "end" edge */
public static final int END = 3;
+
+
/**
* Utility class to express border info.
*/
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;
}
}
+ /**
+ * @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");
sb.append(mColor);
sb.append(", ");
sb.append(mWidth);
+ sb.append(", ");
+ sb.append(radiusStart);
+ sb.append(", ");
+ sb.append(radiusEnd);
sb.append("}");
return sb.toString();
}
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;
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
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 {
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);
}
* @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 */
}
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
}
}
+ /**
+ * 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
*
*/
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);
}
/**
*/
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";
}
/**
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;
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);
}
/**
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);
}
}
*/
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);
}
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);
}
+
}
/**
}
- 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;
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
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;
/**
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);
}
/**
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
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.
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;
*/
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),
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;
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;
}
}
+ /**
+ * 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} */
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;
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;
}
/**
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;
AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)createDataObjectInfo();
// set resource information
- setResourceInformation(graphicsObjectInfo,
+
+ graphicsObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
- afpContext.getForeignAttributes());
+ afpContext.getForeignAttributes()));
// Positioning
graphicsObjectInfo.setObjectAreaInfo(
AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)createDataObjectInfo();
// set resource information
- setResourceInformation(imageObjectInfo,
+ imageObjectInfo.setResourceInfo(createResourceInformation(
image.getInfo().getOriginalURI(),
- afpContext.getForeignAttributes());
+ afpContext.getForeignAttributes()));
setDefaultResourceLevel(imageObjectInfo, afpContext.getResourceManager());
// Positioning
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;
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;
*/
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;
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();
}
/** {@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 {
/** {@inheritDoc} */
protected RenderingContext createRenderingContext() {
- AFPRenderingContext psContext = new AFPRenderingContext(
+ AFPRenderingContext renderingContext = new AFPRenderingContext(
getUserAgent(),
documentHandler.getResourceManager(),
getPaintingState(),
getFontInfo(),
getContext().getForeignAttributes());
- return psContext;
+ return renderingContext;
}
/** {@inheritDoc} */
}
}
+ /** {@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);
/** {@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 {
}
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.
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) {
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);
+ }
+
}
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));
/** {@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,
return new AffineTransform(matrix);
}
+ /** {@inheritDoc} */
+ public boolean isBackgroundRequired( BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) {
+ return true;
+ }
+
}
*/
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
* @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);
}
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);
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);
}
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);
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);
}
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);
}
}
+ 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.
*/
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
*/
protected abstract void restoreGraphicsState() throws IOException;
+ public static boolean isRoundedCornersSupported() {
+ return "true".equalsIgnoreCase(System.getProperty(ROUNDED_CORNERS, "true"));
+ }
+
}
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);
+ }*/
+
}
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
* @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!
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);
}
}
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);
}
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;
}
}
+ /** {@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) {
/** {@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;
}
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);
}
}
+ /** {@inheritDoc} */
+ public boolean isBackgroundRequired(BorderProps bpsBefore, BorderProps bpsAfter,
+ BorderProps bpsStart, BorderProps bpsEnd) {
+ return true;
+ }
+
+
+
+
}
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;
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
+
+ }
+
}
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) {
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");
}
}
}
g2dState.transform(transform);
}
+
+
}
/** @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 {
//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) {
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) {
gen.setCursorPos(transPoint.getX(), transPoint.getY());
}
+
+
}
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;
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);
}
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);
}
static final String format(int coordinate) {
+ //TODO lose scale?
return format(coordinate / 1000f);
}
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);
+ }
+
}
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) {
/** {@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 {
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());
+ }
+
}
}
}
+ /** {@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) {
/** {@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);
}
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
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
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;
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");
}
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();
import java.util.Map;
import org.w3c.dom.Document;
-
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
//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) {
}
}
+
+
}