- /*
- * 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 java.awt.Color;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.util.Arrays;
-
- import org.apache.xmlgraphics.image.loader.ImageException;
- import org.apache.xmlgraphics.image.loader.ImageInfo;
- import org.apache.xmlgraphics.image.loader.ImageManager;
- import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-
- import org.apache.fop.ResourceEventProducer;
- import org.apache.fop.apps.FOUserAgent;
- import org.apache.fop.datatypes.Length;
- import org.apache.fop.datatypes.PercentBaseContext;
- import org.apache.fop.datatypes.URISpecification;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.FObj;
- import org.apache.fop.fo.PropertyList;
- import org.apache.fop.fo.expr.PropertyException;
- import org.apache.fop.util.CompareUtil;
-
- /**
- * Stores all common border and padding properties.
- * See Sec. 7.7 of the XSL-FO Standard.
- */
- public class CommonBorderPaddingBackground {
-
- /**
- * cache holding all canonical instances
- * (w/ absolute background-position-* and padding-*)
- */
- private static final PropertyCache<CommonBorderPaddingBackground> CACHE
- = new PropertyCache<CommonBorderPaddingBackground>();
-
- private int hash = -1;
-
- /**
- * The "background-attachment" property.
- */
- public final int backgroundAttachment; // CSOK: VisibilityModifier
-
- /**
- * The "background-color" property.
- */
- public final Color backgroundColor; // CSOK: VisibilityModifier
-
- /**
- * The "background-image" property.
- */
- public final String backgroundImage; // CSOK: VisibilityModifier
-
- /**
- * The "background-repeat" property.
- */
- public final int backgroundRepeat; // CSOK: VisibilityModifier
-
- /**
- * The "background-position-horizontal" property.
- */
- public final Length backgroundPositionHorizontal; // CSOK: VisibilityModifier
-
- /**
- * The "background-position-vertical" property.
- */
- public final Length backgroundPositionVertical; // CSOK: VisibilityModifier
-
-
- private ImageInfo backgroundImageInfo;
-
-
- /** the "before" edge */
- public static final int BEFORE = 0;
- /** the "after" edge */
- public static final int AFTER = 1;
- /** the "start" edge */
- public static final int START = 2;
- /** the "end" edge */
- public static final int END = 3;
-
- /**
- * Utility class to express border info.
- */
- public static final class BorderInfo {
-
- /** cache holding all canonical instances */
- private static final PropertyCache<BorderInfo> CACHE
- = new PropertyCache<BorderInfo>();
-
- private int mStyle; // Enum for border style
- private Color mColor; // Border color
- private CondLengthProperty mWidth;
-
- private int hash = -1;
-
- /**
- * Hidden constructor
- */
- private BorderInfo(int style, CondLengthProperty width, Color color) {
- mStyle = style;
- mWidth = width;
- mColor = color;
- }
-
- /**
- * Returns a BorderInfo instance corresponding to the given values
- *
- * @param style the border-style
- * @param width the border-width
- * @param color the border-color
- * @return a cached BorderInfo instance
- */
- public static BorderInfo getInstance(int style, CondLengthProperty width, Color color) {
- return CACHE.fetch(new BorderInfo(style, width, color));
- }
-
- /**
- * @return the border-style
- */
- public int getStyle() {
- return this.mStyle;
- }
-
- /**
- * @return the border-color
- */
- public Color getColor() {
- return this.mColor;
- }
-
- /**
- * @return the border-width
- */
- public CondLengthProperty getWidth() {
- return this.mWidth;
- }
-
- /**
- * Convenience method returning the border-width,
- * taking into account values of "none" and "hidden"
- *
- * @return the retained border-width
- */
- public int getRetainedWidth() {
- if ((mStyle == Constants.EN_NONE)
- || (mStyle == Constants.EN_HIDDEN)) {
- return 0;
- } else {
- return mWidth.getLengthValue();
- }
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer("BorderInfo");
- sb.append(" {");
- sb.append(mStyle);
- sb.append(", ");
- sb.append(mColor);
- sb.append(", ");
- sb.append(mWidth);
- sb.append("}");
- return sb.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof BorderInfo)) {
- return false;
- }
- BorderInfo other = (BorderInfo) obj;
- return CompareUtil.equal(mColor, other.mColor)
- && mStyle == other.mStyle
- && CompareUtil.equal(mWidth, other.mWidth);
- }
-
- @Override
- public int hashCode() {
- if (this.hash == -1) {
- int hash = 17;
- hash = 37 * hash + (mColor == null ? 0 : mColor.hashCode());
- hash = 37 * hash + mStyle;
- hash = 37 * hash + (mWidth == null ? 0 : mWidth.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);
-
- /**
- * A conditional length of value 0. Returned by the
- * {@link CommonBorderPaddingBackground#getBorderInfo(int)} method when the
- * corresponding border isn't specified, to avoid to callers painful checks for null.
- */
- private static class ConditionalNullLength extends CondLengthProperty {
-
- @Override
- public Property getComponent(int cmpId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Property getConditionality() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Length getLength() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Property getLengthComponent() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getLengthValue() {
- return 0;
- }
-
- @Override
- public int getLengthValue(PercentBaseContext context) {
- return 0;
- }
-
- @Override
- public boolean isDiscard() {
- return true;
- }
-
- @Override
- public void setComponent(int cmpId, Property cmpnValue, boolean isDefault) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String toString() {
- return "CondLength[0mpt, discard]";
- }
- }
-
- /**
- * Returns a default BorderInfo of style none.
- *
- * @return a BorderInfo instance with style set to {@link Constants#EN_NONE}
- */
- public static BorderInfo getDefaultBorderInfo() {
- return DEFAULT_BORDER_INFO;
- }
-
- private BorderInfo[] borderInfo = new BorderInfo[4];
- private CondLengthProperty[] padding = new CondLengthProperty[4];
-
- /**
- * Construct a CommonBorderPaddingBackground object.
- *
- * @param pList The PropertyList to get properties from.
- * @throws PropertyException if there's an error while binding the properties
- */
- CommonBorderPaddingBackground(PropertyList pList) throws PropertyException {
-
- backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();
-
- Color bc = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
- pList.getFObj().getUserAgent());
- if (bc.getAlpha() == 0) {
- backgroundColor = null;
- } else {
- backgroundColor = bc;
- }
-
- String img = pList.get(Constants.PR_BACKGROUND_IMAGE).getString();
- if (img == null || "none".equals(img)) {
- backgroundImage = "";
- backgroundRepeat = -1;
- backgroundPositionHorizontal = null;
- backgroundPositionVertical = null;
- } else {
- backgroundImage = img;
- backgroundRepeat = pList.get(Constants.PR_BACKGROUND_REPEAT).getEnum();
- backgroundPositionHorizontal = pList.get(
- Constants.PR_BACKGROUND_POSITION_HORIZONTAL).getLength();
- backgroundPositionVertical = pList.get(
- Constants.PR_BACKGROUND_POSITION_VERTICAL).getLength();
- }
-
- initBorderInfo(pList, BEFORE,
- Constants.PR_BORDER_BEFORE_COLOR,
- Constants.PR_BORDER_BEFORE_STYLE,
- Constants.PR_BORDER_BEFORE_WIDTH,
- Constants.PR_PADDING_BEFORE);
- initBorderInfo(pList, AFTER,
- Constants.PR_BORDER_AFTER_COLOR,
- Constants.PR_BORDER_AFTER_STYLE,
- Constants.PR_BORDER_AFTER_WIDTH,
- Constants.PR_PADDING_AFTER);
- initBorderInfo(pList, START,
- Constants.PR_BORDER_START_COLOR,
- Constants.PR_BORDER_START_STYLE,
- Constants.PR_BORDER_START_WIDTH,
- Constants.PR_PADDING_START);
- initBorderInfo(pList, END,
- Constants.PR_BORDER_END_COLOR,
- Constants.PR_BORDER_END_STYLE,
- Constants.PR_BORDER_END_WIDTH,
- Constants.PR_PADDING_END);
-
- }
-
- /**
- * Obtain a CommonBorderPaddingBackground instance based on the
- * related property valus in the given {@link PropertyList}
- *
- * @param pList the {@link PropertyList} to use
- * @return a CommonBorderPaddingBackground instance (cached if possible)
- * @throws PropertyException in case of an error
- */
- public static CommonBorderPaddingBackground getInstance(PropertyList pList)
- throws PropertyException {
- CommonBorderPaddingBackground newInstance = new CommonBorderPaddingBackground(pList);
- CommonBorderPaddingBackground cachedInstance = null;
- /* if padding-* and background-position-* resolve to absolute lengths
- * the whole instance can be cached */
- if ((newInstance.padding[BEFORE] == null
- || newInstance.padding[BEFORE].getLength().isAbsolute())
- && (newInstance.padding[AFTER] == null
- || newInstance.padding[AFTER].getLength().isAbsolute())
- && (newInstance.padding[START] == null
- || newInstance.padding[START].getLength().isAbsolute())
- && (newInstance.padding[END] == null
- || newInstance.padding[END].getLength().isAbsolute())
- && (newInstance.backgroundPositionHorizontal == null
- || newInstance.backgroundPositionHorizontal.isAbsolute())
- && (newInstance.backgroundPositionVertical == null
- || newInstance.backgroundPositionVertical.isAbsolute())) {
- cachedInstance = CACHE.fetch(newInstance);
- }
- synchronized (newInstance.backgroundImage.intern()) {
- /* for non-cached, or not-yet-cached instances, preload the image */
- if ((cachedInstance == null || cachedInstance == newInstance)
- && !("".equals(newInstance.backgroundImage))) {
- //Additional processing: preload image
- String uri = URISpecification.getURL(newInstance.backgroundImage);
- FObj fobj = pList.getFObj();
- FOUserAgent userAgent = pList.getFObj().getUserAgent();
- ImageManager manager = userAgent.getImageManager();
- ImageSessionContext sessionContext = userAgent.getImageSessionContext();
- ImageInfo info;
- try {
- info = manager.getImageInfo(uri, sessionContext);
- newInstance.backgroundImageInfo = info;
- } catch (ImageException e) {
- ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
- fobj.getUserAgent().getEventBroadcaster());
- eventProducer.imageError(fobj, uri, e, fobj.getLocator());
- } catch (FileNotFoundException fnfe) {
- ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
- fobj.getUserAgent().getEventBroadcaster());
- eventProducer.imageNotFound(fobj, uri, fnfe, fobj.getLocator());
- } catch (IOException ioe) {
- ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
- fobj.getUserAgent().getEventBroadcaster());
- eventProducer.imageIOError(fobj, uri, ioe, fobj.getLocator());
- }
- }
- }
-
- return (cachedInstance != null ? cachedInstance : newInstance);
- }
-
- private void initBorderInfo(PropertyList pList, int side,
- int colorProp, int styleProp, int widthProp, int paddingProp)
- throws PropertyException {
-
- padding[side] = pList.get(paddingProp).getCondLength();
- // If style = none, force width to 0, don't get Color (spec 7.7.20)
- int style = pList.get(styleProp).getEnum();
- if (style != Constants.EN_NONE) {
- FOUserAgent ua = pList.getFObj().getUserAgent();
- setBorderInfo(BorderInfo.getInstance(style,
- pList.get(widthProp).getCondLength(),
- pList.get(colorProp).getColor(ua)), side);
- }
-
- }
-
- /**
- * Sets a border.
- * @param info the border information
- * @param side the side to apply the info to
- */
- private void setBorderInfo(BorderInfo info, int side) {
- this.borderInfo[side] = info;
- }
-
- /**
- * @param side the side to retrieve
- * @return the border info for a side
- */
- public BorderInfo getBorderInfo(int side) {
- if (this.borderInfo[side] == null) {
- return getDefaultBorderInfo();
- } else {
- return this.borderInfo[side];
- }
- }
-
- /**
- * @return the background image info object, null if there is
- * no background image.
- */
- public ImageInfo getImageInfo() {
- return this.backgroundImageInfo;
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @return the width of the start-border, taking into account the specified conditionality
- */
- public int getBorderStartWidth(boolean discard) {
- return getBorderWidth(START, discard);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (end of a reference-area)
- * @return the width of the end-border, taking into account the specified conditionality
- */
- public int getBorderEndWidth(boolean discard) {
- return getBorderWidth(END, discard);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @return the width of the before-border, taking into account the specified conditionality
- */
- public int getBorderBeforeWidth(boolean discard) {
- return getBorderWidth(BEFORE, discard);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (end of a reference-area)
- * @return the width of the after-border, taking into account the specified conditionality
- */
- public int getBorderAfterWidth(boolean discard) {
- return getBorderWidth(AFTER, discard);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @param context the context to evaluate percentage values
- * @return the width of the start-padding, taking into account the specified conditionality
- */
- public int getPaddingStart(boolean discard, PercentBaseContext context) {
- return getPadding(START, discard, context);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @param context the context to evaluate percentage values
- * @return the width of the end-padding, taking into account the specified conditionality
- */
- public int getPaddingEnd(boolean discard, PercentBaseContext context) {
- return getPadding(END, discard, context);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @param context the context to evaluate percentage values
- * @return the width of the before-padding, taking into account the specified conditionality
- */
- public int getPaddingBefore(boolean discard, PercentBaseContext context) {
- return getPadding(BEFORE, discard, context);
- }
-
- /**
- * @param discard indicates whether the .conditionality component should be
- * considered (start of a reference-area)
- * @param context the context to evaluate percentage values
- * @return the width of the after-padding, taking into account the specified conditionality
- */
- public int getPaddingAfter(boolean discard, PercentBaseContext context) {
- return getPadding(AFTER, discard, context);
- }
-
- /**
- * @param side the side of the border
- * @param discard indicates whether the .conditionality component should be considered (end of a
- * reference-area)
- * @return the width of the start-border, taking into account the specified conditionality
- */
- public int getBorderWidth(int side, boolean discard) {
- if ((borderInfo[side] == null)
- || (borderInfo[side].mStyle == Constants.EN_NONE)
- || (borderInfo[side].mStyle == Constants.EN_HIDDEN)
- || (discard && borderInfo[side].mWidth.isDiscard())) {
- return 0;
- } else {
- return borderInfo[side].mWidth.getLengthValue();
- }
- }
-
- /**
- * The border-color for the given side
- *
- * @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
- * @return the border-color for the given side
- */
- public Color getBorderColor(int side) {
- if (borderInfo[side] != null) {
- return borderInfo[side].getColor();
- } else {
- return null;
- }
- }
-
- /**
- * The border-style for the given side
- *
- * @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
- * @return the border-style for the given side
- */
- public int getBorderStyle(int side) {
- if (borderInfo[side] != null) {
- return borderInfo[side].mStyle;
- } else {
- return Constants.EN_NONE;
- }
- }
-
- /**
- * Return the padding for the given side, taking into account
- * the conditionality and evaluating any percentages in the given
- * context.
- *
- * @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
- * @param discard true if the conditionality component should be considered
- * @param context the context for percentage-resolution
- * @return the computed padding for the given side
- */
- public int getPadding(int side, boolean discard, PercentBaseContext context) {
- if ((padding[side] == null) || (discard && padding[side].isDiscard())) {
- return 0;
- } else {
- return padding[side].getLengthValue(context);
- }
- }
-
- /**
- * Returns the CondLengthProperty for the padding on one side.
- * @param side the side
- * @return the requested CondLengthProperty
- */
- public CondLengthProperty getPaddingLengthProperty(int side) {
- return padding[side];
- }
-
- /**
- * Return all the border and padding width in the inline progression
- * dimension.
- * @param discard the discard flag.
- * @param context for percentage evaluation.
- * @return all the padding and border width.
- */
- public int getIPPaddingAndBorder(boolean discard, PercentBaseContext context) {
- return getPaddingStart(discard, context)
- + getPaddingEnd(discard, context)
- + getBorderStartWidth(discard)
- + getBorderEndWidth(discard);
- }
-
- /**
- * Return all the border and padding height in the block progression
- * dimension.
- * @param discard the discard flag.
- * @param context for percentage evaluation
- * @return all the padding and border height.
- */
- public int getBPPaddingAndBorder(boolean discard, PercentBaseContext context) {
- return getPaddingBefore(discard, context) + getPaddingAfter(discard, context)
- + getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
- }
-
- @Override
- public String toString() {
- return "CommonBordersAndPadding (Before, After, Start, End):\n"
- + "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
- + getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
- + "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
- + getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
- + "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
- + ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
- }
-
- /**
- * @return true if there is any kind of background to be painted
- */
- public boolean hasBackground() {
- return ((backgroundColor != null || getImageInfo() != null));
- }
-
- /** @return true if border is non-zero. */
- public boolean hasBorder() {
- return ((getBorderBeforeWidth(false) + getBorderAfterWidth(false)
- + getBorderStartWidth(false) + getBorderEndWidth(false)) > 0);
- }
-
- /**
- * @param context for percentage based evaluation.
- * @return true if padding is non-zero.
- */
- public boolean hasPadding(PercentBaseContext context) {
- return ((getPaddingBefore(false, context) + getPaddingAfter(false, context)
- + getPaddingStart(false, context) + getPaddingEnd(false, context)) > 0);
- }
-
- /** @return true if there are any borders defined. */
- public boolean hasBorderInfo() {
- return (borderInfo[BEFORE] != null || borderInfo[AFTER] != null
- || borderInfo[START] != null || borderInfo[END] != null);
- }
-
- /**
- * Returns the "background-color" property.
- * @return the "background-color" property.
- */
- public Color getBackgroundColor() {
- return backgroundColor;
- }
-
- /**
- * Returns the "background-attachment" property.
- * @return the "background-attachment" property.
- */
- public int getBackgroundAttachment() {
- return backgroundAttachment;
- }
-
- /**
- * Returns the "background-image" property.
- * @return the "background-image" property.
- */
- public String getBackgroundImage() {
- return backgroundImage;
- }
-
- /**
- * Returns the "background-repeat" property.
- * @return the "background-repeat" property.
- */
- public int getBackgroundRepeat() {
- return backgroundRepeat;
- }
-
- /**
- * Returns the "background-position-horizontal" property.
- * @return the "background-position-horizontal" property.
- */
- public Length getBackgroundPositionHorizontal() {
- return backgroundPositionHorizontal;
- }
-
- /**
- * Returns the "background-position-vertical" property.
- * @return the "background-position-vertical" property.
- */
- public Length getBackgroundPositionVertical() {
- return backgroundPositionVertical;
- }
-
- /**
- * Returns the background image info
- * @return the background image info
- */
- public ImageInfo getBackgroundImageInfo() {
- return backgroundImageInfo;
- }
-
- /**
- * Returns the border info
- * @return the border info
- */
- public BorderInfo[] getBorderInfo() {
- return borderInfo;
- }
-
- /**
- * Returns the padding
- * @return the padding
- */
- public CondLengthProperty[] getPadding() {
- return padding;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof CommonBorderPaddingBackground)) {
- return false;
- }
-
- CommonBorderPaddingBackground other = (CommonBorderPaddingBackground) obj;
- return backgroundAttachment == other.backgroundAttachment
- && CompareUtil.equal(backgroundColor, other.backgroundColor)
- && CompareUtil.equal(backgroundImage, other.backgroundImage)
- && CompareUtil.equal(backgroundPositionHorizontal, backgroundPositionHorizontal)
- && CompareUtil.equal(backgroundPositionVertical, other.backgroundPositionVertical)
- && backgroundRepeat == other.backgroundRepeat
- && Arrays.equals(borderInfo, other.borderInfo)
- && Arrays.equals(padding, other.padding);
- }
-
- @Override
- public int hashCode() {
- if (this.hash == -1) {
- int hash = getHashCode(backgroundColor,
- backgroundImage,
- backgroundPositionHorizontal,
- backgroundPositionVertical,
- borderInfo[BEFORE],
- borderInfo[AFTER],
- borderInfo[START],
- borderInfo[END],
- padding[BEFORE],
- padding[AFTER],
- padding[START],
- padding[END]);
- hash = 37 * hash + backgroundAttachment;
- hash = 37 * hash + backgroundRepeat;
- this.hash = hash;
- }
- return this.hash;
- }
-
- private int getHashCode(Object... objects) {
- int hash = 17;
- for (Object o : objects) {
- hash = 37 * hash + (o == null ? 0 : o.hashCode());
- }
- return hash;
- }
- }
|