diff options
28 files changed, 975 insertions, 245 deletions
diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 50cf8c4a9..0d53a44b3 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -568,7 +568,7 @@ public final class FOPropertyMapping implements Constants { if (nameval != null) { FObj fobj = (fo == null ? propertyList.getFObj() : fo); FOUserAgent ua = (fobj == null ? null : fobj.getUserAgent()); - return new ColorProperty(ua, nameval); + return ColorProperty.getInstance(ua, nameval); } return super.convertPropertyDatatype(p, propertyList, fo); } diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java index 8a59b5751..6d4dbd43c 100644 --- a/src/java/org/apache/fop/fo/PropertyList.java +++ b/src/java/org/apache/fop/fo/PropertyList.java @@ -577,7 +577,7 @@ public abstract class PropertyList { */ public CommonBorderPaddingBackground getBorderPaddingBackgroundProps() throws PropertyException { - return new CommonBorderPaddingBackground(this, fobj); + return new CommonBorderPaddingBackground(this); } /** @@ -586,7 +586,7 @@ public abstract class PropertyList { * @throws PropertyException if there's a problem while processing the properties */ public CommonHyphenation getHyphenationProps() throws PropertyException { - return new CommonHyphenation(this); + return CommonHyphenation.getInstance(this); } /** @@ -652,7 +652,7 @@ public abstract class PropertyList { * @throws PropertyException if there's a problem while processing the properties */ public CommonFont getFontProps() throws PropertyException { - return CommonFont(this); + return CommonFont.getInstance(this); } /** diff --git a/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java b/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java index ff3ca25eb..3aab1325d 100644 --- a/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java +++ b/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java @@ -43,7 +43,7 @@ class CMYKcolorFunction extends FunctionBase { FOUserAgent ua = (pInfo == null) ? null : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent()); - return new ColorProperty(ua, sb.toString()); + return ColorProperty.getInstance(ua, sb.toString()); } diff --git a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java index 389bf42e9..ca66aac27 100644 --- a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java +++ b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java @@ -70,7 +70,7 @@ class ICCColorFunction extends FunctionBase { FOUserAgent ua = (pInfo == null ? null : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent())); - return new ColorProperty(ua, sb.toString()); + return ColorProperty.getInstance(ua, sb.toString()); } diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java index 3bb15cc4c..6dcbb0b0a 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyParser.java +++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java @@ -302,7 +302,7 @@ public final class PropertyParser extends PropertyTokenizer { FOUserAgent ua = (propInfo == null) ? null : (propInfo.getFO() == null ? null : propInfo.getFO().getUserAgent()); - prop = new ColorProperty(ua, currentTokenValue); + prop = ColorProperty.getInstance(ua, currentTokenValue); break; case TOK_FUNCTION_LPAR: diff --git a/src/java/org/apache/fop/fo/expr/RGBColorFunction.java b/src/java/org/apache/fop/fo/expr/RGBColorFunction.java index 6c6162731..9b3287154 100644 --- a/src/java/org/apache/fop/fo/expr/RGBColorFunction.java +++ b/src/java/org/apache/fop/fo/expr/RGBColorFunction.java @@ -50,7 +50,7 @@ class RGBColorFunction extends FunctionBase { FOUserAgent ua = (pInfo == null) ? null : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent()); - return new ColorProperty(ua, "rgb(" + args[0] + "," + args[1] + "," + args[2] + ")"); + return ColorProperty.getInstance(ua, "rgb(" + args[0] + "," + args[1] + "," + args[2] + ")"); } diff --git a/src/java/org/apache/fop/fo/expr/SystemColorFunction.java b/src/java/org/apache/fop/fo/expr/SystemColorFunction.java index 5398b7051..b32c38e54 100644 --- a/src/java/org/apache/fop/fo/expr/SystemColorFunction.java +++ b/src/java/org/apache/fop/fo/expr/SystemColorFunction.java @@ -39,7 +39,7 @@ class SystemColorFunction extends FunctionBase { FOUserAgent ua = (pInfo == null) ? null : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent()); - return new ColorProperty(ua, "system-color(" + args[0] + ")"); + return ColorProperty.getInstance(ua, "system-color(" + args[0] + ")"); } diff --git a/src/java/org/apache/fop/fo/properties/ColorProperty.java b/src/java/org/apache/fop/fo/properties/ColorProperty.java index 5eb399d3d..853a965bb 100644 --- a/src/java/org/apache/fop/fo/properties/ColorProperty.java +++ b/src/java/org/apache/fop/fo/properties/ColorProperty.java @@ -28,9 +28,12 @@ import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.util.ColorUtil; /** - * Superclass for properties that wrap Color values + * Class for properties that wrap Color values */ -public class ColorProperty extends Property { +public final class ColorProperty extends Property { + + /** cache holding canonical ColorProperty instances */ + private static final PropertyCache cache = new PropertyCache(); /** * The color represented by this property. @@ -92,16 +95,28 @@ public class ColorProperty extends Property { * @throws PropertyException if the value can't be parsed * @see ColorUtil#parseColorString(FOUserAgent, String) */ - public ColorProperty(FOUserAgent foUserAgent, String value) throws PropertyException { - this.color = ColorUtil.parseColorString(foUserAgent, value); + public static ColorProperty getInstance(FOUserAgent foUserAgent, String value) throws PropertyException { + ColorProperty instance = new ColorProperty( + ColorUtil.parseColorString( + foUserAgent, value)); + return (ColorProperty) cache.fetch(instance); } /** + * + * @param value + * @return + */ + public static ColorProperty getInstance(Color value) { + return (ColorProperty) cache.fetch(new ColorProperty(value)); + } + + /** * Create a new ColorProperty with a given color. * * @param value the color to use. */ - public ColorProperty(Color value) { + private ColorProperty(Color value) { this.color = value; } @@ -114,9 +129,7 @@ public class ColorProperty extends Property { return color; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String toString() { return ColorUtil.colorToString(color); } @@ -135,5 +148,22 @@ public class ColorProperty extends Property { public Object getObject() { return this; } + + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof ColorProperty) { + return ((ColorProperty) o).color.equals(this.color); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + return this.color.hashCode(); + } } diff --git a/src/java/org/apache/fop/fo/properties/CommonAccessibility.java b/src/java/org/apache/fop/fo/properties/CommonAccessibility.java index 15ffbffc1..e97ba5a0a 100644 --- a/src/java/org/apache/fop/fo/properties/CommonAccessibility.java +++ b/src/java/org/apache/fop/fo/properties/CommonAccessibility.java @@ -40,7 +40,7 @@ public class CommonAccessibility { public String role = null; /** - * Create a CommonAbsolutePosition object. + * Create a <code>CommonAccessibility</code> object. * @param pList The PropertyList with propery values. */ public CommonAccessibility(PropertyList pList) throws PropertyException { diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index 712ae9703..e41685019 100755 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -137,15 +137,16 @@ public class CommonBorderPaddingBackground { /** * Construct a CommonBorderPaddingBackground object. + * * @param pList The PropertyList to get properties from. * @param fobj The FO to create this instance for. * @throws PropertyException if there's an error while binding the properties */ - public CommonBorderPaddingBackground(PropertyList pList, FObj fobj) throws PropertyException { + public CommonBorderPaddingBackground(PropertyList pList) throws PropertyException { backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum(); backgroundColor = pList.get(Constants.PR_BACKGROUND_COLOR).getColor( - fobj == null ? null : fobj.getUserAgent()); + pList.getFObj().getUserAgent()); if (backgroundColor.getAlpha() == 0) { backgroundColor = null; } @@ -162,7 +163,7 @@ public class CommonBorderPaddingBackground { //Additional processing: preload image String url = ImageFactory.getURL(backgroundImage); - FOUserAgent userAgent = fobj.getUserAgent(); + FOUserAgent userAgent = pList.getFObj().getUserAgent(); ImageFactory fact = userAgent.getFactory().getImageFactory(); fopimage = fact.getImage(url, userAgent); if (fopimage == null) { @@ -207,9 +208,7 @@ public class CommonBorderPaddingBackground { // 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 == null) - ? null - : (pList.getFObj() == null ? null : pList.getFObj().getUserAgent()); + FOUserAgent ua = pList.getFObj().getUserAgent(); setBorderInfo(new BorderInfo(style, pList.get(widthProp).getCondLength(), pList.get(colorProp).getColor(ua)), side); diff --git a/src/java/org/apache/fop/fo/properties/CommonFont.java b/src/java/org/apache/fop/fo/properties/CommonFont.java index d0d38223f..a2d01ffa3 100755 --- a/src/java/org/apache/fop/fo/properties/CommonFont.java +++ b/src/java/org/apache/fop/fo/properties/CommonFont.java @@ -24,140 +24,329 @@ import java.util.List; import org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.Numeric; -import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; -import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; /** - * Collection of properties used in + * Collection of CommonFont properties */ -public class CommonFont { +public final class CommonFont { + /** cache holding canonical CommonFont instances (only those with + * absolute font-size and font-size-adjust) */ + private static final PropertyCache cache = new PropertyCache(); + /** - * The "font-family" property. + * Class holding canonical instances of bundles of the + * cacheable (non-relative) CommonFont properties + * */ - private String[] fontFamily; + protected static final class CachedCommonFont { + + /** cache holding all canonical instances */ + private static final PropertyCache cache = new PropertyCache(); + + private int hash = 0; + + /** + * The "font-family" property. + */ + private final FontFamilyProperty fontFamily; + + /** + * The "font-selection-strategy" property. + */ + private final EnumProperty fontSelectionStrategy; + + /** + * The "font-stretch" property. + */ + private final EnumProperty fontStretch; - /** - * The "font-selection-strategy" property. - */ - public int fontSelectionStrategy; + /** + * The "font-style" property. + */ + private final EnumProperty fontStyle; - /** - * The "font-size" property. - */ - public Length fontSize; + /** + * The "font-variant" property. + */ + private final EnumProperty fontVariant; - /** - * The "font-stretch" property. - */ - public int fontStretch; + /** + * The "font-weight" property. + */ + private final EnumProperty fontWeight; + + /** + * Constructor + * + * @param fontFamily the font-family property + * @param fontSelectionStrategy the font-selection-strategy property + * @param fontStretch the font-stretch property + * @param fontStyle the font-style property + * @param fontVariant the font-variant property + * @param fontWeight the font-weight property + */ + private CachedCommonFont(FontFamilyProperty fontFamily, + EnumProperty fontSelectionStrategy, + EnumProperty fontStretch, + EnumProperty fontStyle, + EnumProperty fontVariant, + EnumProperty fontWeight) { + this.fontFamily = fontFamily; + this.fontSelectionStrategy = fontSelectionStrategy; + this.fontStretch = fontStretch; + this.fontStyle = fontStyle; + this.fontVariant = fontVariant; + this.fontWeight = fontWeight; + } + + /** + * Returns the canonical instance corresponding to the given + * properties + * + * @param fontFamily the font-family property + * @param fontSelectionStrategy the font-selection-strategy property + * @param fontStretch the font-stretch property + * @param fontStyle the font-style property + * @param fontVariant the font-variant property + * @param fontWeight the font-weight property + * @return the canonical instance + */ + private static final CachedCommonFont getInstance(FontFamilyProperty fontFamily, + EnumProperty fontSelectionStrategy, + EnumProperty fontStretch, + EnumProperty fontStyle, + EnumProperty fontVariant, + EnumProperty fontWeight) { + return cache.fetch(new CachedCommonFont( + fontFamily, + fontSelectionStrategy, + fontStretch, + fontStyle, + fontVariant, + fontWeight)); + } + + /** @return the first font-family name in the list */ + private String getFirstFontFamily() { + return ((Property) fontFamily.list.get(0)).getString(); + } + + /** @return an array with the font-family names */ + private String[] getFontFamily() { + List lst = fontFamily.getList(); + String[] fontFamily = new String[lst.size()]; + for (int i = 0, c = lst.size(); i < c; i++) { + fontFamily[i] = ((Property)lst.get(i)).getString(); + } + return fontFamily; + } + + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof CachedCommonFont) { + CachedCommonFont ccf = (CachedCommonFont) o; + return (ccf.fontFamily == this.fontFamily) + && (ccf.fontSelectionStrategy == this.fontSelectionStrategy) + && (ccf.fontStretch == this.fontStretch) + && (ccf.fontStyle == this.fontStyle) + && (ccf.fontVariant == this.fontVariant) + && (ccf.fontWeight == this.fontWeight); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + if (this.hash == 0) { + int hash = 17; + hash = 37 * hash + (fontFamily == null ? 0 : fontFamily.hashCode()); + hash = 37 * hash + (fontSelectionStrategy == null ? 0 : fontSelectionStrategy.hashCode()); + hash = 37 * hash + (fontStretch == null ? 0 : fontStretch.hashCode()); + hash = 37 * hash + (fontStyle == null ? 0 : fontStyle.hashCode()); + hash = 37 * hash + (fontVariant == null ? 0 : fontVariant.hashCode()); + hash = 37 * hash + (fontStretch == null ? 0 : fontStretch.hashCode()); + this.hash = hash; + } + return this.hash; + } + + } /** - * The "font-size-adjust" property. + * The cached CommonFont properties (access these through the getters) + * The remaining properties, font-size and font-size-adjust, + * will only be cached values if they are absolute. */ - public Numeric fontSizeAdjust; - + private final CachedCommonFont cachedCommonFont; + /** - * The "font-style" property. + * The "font-size" property. */ - public int fontStyle; + public final Length fontSize; /** - * The "font-variant" property. + * The "font-size-adjust" property. */ - public int fontVariant; + public final Numeric fontSizeAdjust; /** - * The "font-weight" property. + * Construct a CommonFont instance + * + * @param ccf the cached CommonFont properties + * @param fontSize the font-size (possibly non-cached) + * @param fontSizeAdjust the font-size-adjust (possibly non-cached) */ - public int fontWeight; - - private Font fontState; + private CommonFont(CachedCommonFont ccf, + Length fontSize, + Numeric fontSizeAdjust) { + this.cachedCommonFont = ccf; + this.fontSize = fontSize; + this.fontSizeAdjust = fontSizeAdjust; + } /** - * Create a CommonFont object. - * @param pList The PropertyList to get properties from. + * Returns a CommonFont instance for the given PropertyList + * If the font-size and font-size-adjust properties are absolute + * the entire instance will be cached. + * If not, then a distinct instance will be returned, with + * as much cached information as possible. + * + * @param pList the PropertyList to get the properties from + * @return a CommonFont instance corresponding to the properties + * @throws PropertyException if there was a problem getting the properties */ - public CommonFont(PropertyList pList) throws PropertyException { - List lst = pList.get(Constants.PR_FONT_FAMILY).getList(); - fontFamily = new String[lst.size()]; - for (int i = 0, c = lst.size(); i < c; i++) { - fontFamily[i] = ((Property)lst.get(i)).getString(); - } - if (fontFamily.length == 0) { - //Shouldn't happen, but we never know. - fontFamily = new String[] {"any"}; + public static CommonFont getInstance(PropertyList pList) throws PropertyException { + FontFamilyProperty fontFamily = (FontFamilyProperty) pList.get(Constants.PR_FONT_FAMILY); + EnumProperty fontSelectionStrategy = (EnumProperty) pList.get(Constants.PR_FONT_SELECTION_STRATEGY); + EnumProperty fontStretch = (EnumProperty) pList.get(Constants.PR_FONT_STRETCH); + EnumProperty fontStyle = (EnumProperty) pList.get(Constants.PR_FONT_STYLE); + EnumProperty fontVariant = (EnumProperty) pList.get(Constants.PR_FONT_VARIANT); + EnumProperty fontWeight = (EnumProperty) pList.get(Constants.PR_FONT_WEIGHT); + CachedCommonFont cachedCommonFont = CachedCommonFont.getInstance( + fontFamily, + fontSelectionStrategy, + fontStretch, + fontStyle, + fontVariant, + fontWeight); + + Numeric fontSizeAdjust = pList.get(Constants.PR_FONT_SIZE_ADJUST).getNumeric(); + Length fontSize = pList.get(Constants.PR_FONT_SIZE).getLength(); + CommonFont cf = new CommonFont(cachedCommonFont, fontSize, fontSizeAdjust); + if (fontSize.isAbsolute() && fontSizeAdjust.isAbsolute()) { + return cache.fetch(cf); + } else { + return cf; } - fontSelectionStrategy = pList.get(Constants.PR_FONT_SELECTION_STRATEGY).getEnum(); - fontSize = pList.get(Constants.PR_FONT_SIZE).getLength(); - fontStretch = pList.get(Constants.PR_FONT_STRETCH).getEnum(); - fontSizeAdjust = pList.get(Constants.PR_FONT_SIZE_ADJUST).getNumeric(); - fontStyle = pList.get(Constants.PR_FONT_STYLE).getEnum(); - fontVariant = pList.get(Constants.PR_FONT_VARIANT).getEnum(); - fontWeight = pList.get(Constants.PR_FONT_WEIGHT).getEnum(); + } - + /** @return the first font-family name in the list */ public String getFirstFontFamily() { - return this.fontFamily[0]; + return cachedCommonFont.getFirstFontFamily(); + } + + /** @return the "font-selection-strategy" property */ + public int getFontSelectionStrategy() { + return cachedCommonFont.fontSelectionStrategy.getEnum(); + } + + /** @return the "font-stretch" property */ + public int getFontStretch() { + return cachedCommonFont.fontStretch.getEnum(); } - /** @return the font-family names */ - public String[] getFontFamily() { - return this.fontFamily; + /** @return the "font-style" property */ + public int getFontStyle() { + return cachedCommonFont.fontStyle.getEnum(); + } + + /** @return the "font-variant" property */ + public int getFontVariant() { + return cachedCommonFont.fontVariant.getEnum(); + } + + /** @return the "font-weight" property */ + public int getFontWeight() { + return cachedCommonFont.fontWeight.getEnum(); } /** - * Create and return a Font object based on the properties. + * Create and return an array of <code>FontTriplets</code> based on + * the properties stored in the instance variables. * * @param fontInfo * @return a Font object. */ - public Font getFontState(FontInfo fontInfo, PercentBaseContext context) { - if (fontState == null) { - /**@todo this is ugly. need to improve. */ - - int font_weight; - switch (fontWeight) { - case Constants.EN_100: font_weight = 100; break; - case Constants.EN_200: font_weight = 200; break; - case Constants.EN_300: font_weight = 300; break; - case Constants.EN_400: font_weight = 400; break; - case Constants.EN_500: font_weight = 500; break; - case Constants.EN_600: font_weight = 600; break; - case Constants.EN_700: font_weight = 700; break; - case Constants.EN_800: font_weight = 800; break; - case Constants.EN_900: font_weight = 900; break; - default: font_weight = 400; - } + public FontTriplet[] getFontState(FontInfo fontInfo) { + int font_weight; + switch (cachedCommonFont.fontWeight.getEnum()) { + case Constants.EN_100: font_weight = 100; break; + case Constants.EN_200: font_weight = 200; break; + case Constants.EN_300: font_weight = 300; break; + case Constants.EN_400: font_weight = 400; break; + case Constants.EN_500: font_weight = 500; break; + case Constants.EN_600: font_weight = 600; break; + case Constants.EN_700: font_weight = 700; break; + case Constants.EN_800: font_weight = 800; break; + case Constants.EN_900: font_weight = 900; break; + default: font_weight = 400; + } - String style; - switch (fontStyle) { - case Constants.EN_ITALIC: - style = "italic"; - break; - case Constants.EN_OBLIQUE: - style = "oblique"; - break; - case Constants.EN_BACKSLANT: - style = "backslant"; - break; - default: - style = "normal"; - } - // NOTE: this is incomplete. font-size may be specified with - // various kinds of keywords too - //int fontVariant = propertyList.get("font-variant").getEnum(); - FontTriplet triplet = fontInfo.fontLookup(getFontFamily(), style, - font_weight); - fontState = fontInfo.getFontInstance(triplet, fontSize.getValue(context)); + String style; + switch (cachedCommonFont.fontStyle.getEnum()) { + case Constants.EN_ITALIC: + style = "italic"; + break; + case Constants.EN_OBLIQUE: + style = "oblique"; + break; + case Constants.EN_BACKSLANT: + style = "backslant"; + break; + default: + style = "normal"; } - return fontState; + // NOTE: this is incomplete. font-size may be specified with + // various kinds of keywords too + //int fontVariant = propertyList.get("font-variant").getEnum(); + FontTriplet[] triplets = fontInfo.fontLookup( + cachedCommonFont.getFontFamily(), + style, font_weight); + return triplets; } + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof CommonFont) { + CommonFont cf = (CommonFont) o; + return (cf.cachedCommonFont == this.cachedCommonFont + && cf.fontSize == this.fontSize + && cf.fontSizeAdjust == this.fontSizeAdjust); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + int hash = 17; + hash = 37 * hash + cachedCommonFont.hashCode(); + hash = 37 * hash + fontSize.hashCode(); + hash = 37 * hash + fontSizeAdjust.hashCode(); + return hash; + } } diff --git a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java index e10fdad8e..d08d18312 100644 --- a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java +++ b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java @@ -28,57 +28,123 @@ import org.apache.fop.fo.expr.PropertyException; * See Sec. 7.9 of the XSL-FO Standard. * Public "structure" allows direct member access. */ -public class CommonHyphenation { - /** - * The "language" property. - */ - public String language; +public final class CommonHyphenation { + + private static final PropertyCache cache = new PropertyCache(); + + private int hash = 0; + + /** The "language" property */ + public final StringProperty language; - /** - * The "country" property. - */ - public String country; + /** The "country" property */ + public final StringProperty country; - /** - * The "script" property. - */ - public String script; + /** The "script" property */ + public final StringProperty script; - /** - * The "hyphenate" property. - */ - public int hyphenate; + /** The "hyphenate" property */ + public final EnumProperty hyphenate; - /** - * The "hyphenation-character" property. - */ - public char hyphenationCharacter; + /** The "hyphenation-character" property */ + public final CharacterProperty hyphenationCharacter; - /** - * The "hyphenation-push-character" property. - */ - public int hyphenationPushCharacterCount; + /** The "hyphenation-push-character-count" property */ + public final NumberProperty hyphenationPushCharacterCount; + + /** The "hyphenation-remain-character-count" property*/ + public final NumberProperty hyphenationRemainCharacterCount; /** - * The "hyphenation-remain-character-count" property. + * Construct a CommonHyphenation object holding the given properties + * */ - public int hyphenationRemainCharacterCount; - + private CommonHyphenation(StringProperty language, + StringProperty country, + StringProperty script, + EnumProperty hyphenate, + CharacterProperty hyphenationCharacter, + NumberProperty hyphenationPushCharacterCount, + NumberProperty hyphenationRemainCharacterCount) { + this.language = language; + this.country = country; + this.script = script; + this.hyphenate = hyphenate; + this.hyphenationCharacter = hyphenationCharacter; + this.hyphenationPushCharacterCount = hyphenationPushCharacterCount; + this.hyphenationRemainCharacterCount = hyphenationRemainCharacterCount; + } + /** - * Create a CommonHyphenation object. - * @param pList The PropertyList with propery values. + * Gets the canonical <code>CommonHyphenation</code> instance corresponding + * to the values of the related properties present on the given + * <code>PropertyList</code> + * + * @param propertyList the <code>PropertyList</code> */ - public CommonHyphenation(PropertyList pList) throws PropertyException { - language = pList.get(Constants.PR_LANGUAGE).getString(); - country = pList.get(Constants.PR_COUNTRY).getString(); - script = pList.get(Constants.PR_SCRIPT).getString(); - hyphenate = pList.get(Constants.PR_HYPHENATE).getEnum(); - hyphenationCharacter = pList.get(Constants.PR_HYPHENATION_CHARACTER).getCharacter(); - hyphenationPushCharacterCount = - pList.get(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT).getNumber().intValue(); - hyphenationRemainCharacterCount = - pList.get(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT).getNumber().intValue(); - + public static CommonHyphenation getInstance(PropertyList propertyList) throws PropertyException { + StringProperty language = + (StringProperty) propertyList.get(Constants.PR_LANGUAGE); + StringProperty country = + (StringProperty) propertyList.get(Constants.PR_COUNTRY); + StringProperty script = + (StringProperty) propertyList.get(Constants.PR_SCRIPT); + EnumProperty hyphenate = + (EnumProperty) propertyList.get(Constants.PR_HYPHENATE); + CharacterProperty hyphenationCharacter = + (CharacterProperty) propertyList.get(Constants.PR_HYPHENATION_CHARACTER); + NumberProperty hyphenationPushCharacterCount = + (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT); + NumberProperty hyphenationRemainCharacterCount = + (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT); + + CommonHyphenation instance = new CommonHyphenation( + language, + country, + script, + hyphenate, + hyphenationCharacter, + hyphenationPushCharacterCount, + hyphenationRemainCharacterCount); + + return cache.fetch(instance); + } - + + /** {@inheritDoc */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof CommonHyphenation) { + CommonHyphenation ch = (CommonHyphenation) obj; + return (ch.language == this.language + && ch.country == this.country + && ch.script == this.script + && ch.hyphenate == this.hyphenate + && ch.hyphenationCharacter == this.hyphenationCharacter + && ch.hyphenationPushCharacterCount == this.hyphenationPushCharacterCount + && ch.hyphenationRemainCharacterCount == this.hyphenationRemainCharacterCount); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + if (hash == 0) { + int hash = 17; + hash = 37 * hash + (language == null ? 0 : language.hashCode()); + hash = 37 * hash + (script == null ? 0 : script.hashCode()); + hash = 37 * hash + (country == null ? 0 : country.hashCode()); + hash = 37 * hash + (hyphenate == null ? 0 : hyphenate.hashCode()); + hash = 37 * hash + + (hyphenationCharacter == null ? 0 : hyphenationCharacter.hashCode()); + hash = 37 * hash + + (hyphenationPushCharacterCount == null ? 0 : hyphenationPushCharacterCount.hashCode()); + hash = 37 * hash + + (hyphenationRemainCharacterCount == null ? 0 : hyphenationRemainCharacterCount.hashCode()); + } + return hash; + } + } diff --git a/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java b/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java index 9852d5fdf..f15050fbe 100644 --- a/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java @@ -26,12 +26,6 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; /** - * @author me - * - * To change the template for this generated type comment go to - * Window - Preferences - Java - Code Generation - Code and Comments - */ -/** * This class extends Property.Maker with support for sub-properties. */ public class CompoundPropertyMaker extends PropertyMaker { diff --git a/src/java/org/apache/fop/fo/properties/FixedLength.java b/src/java/org/apache/fop/fo/properties/FixedLength.java index e9b0a3f8c..2c497094f 100644 --- a/src/java/org/apache/fop/fo/properties/FixedLength.java +++ b/src/java/org/apache/fop/fo/properties/FixedLength.java @@ -24,7 +24,7 @@ import org.apache.fop.datatypes.PercentBaseContext; /** * An absolute length quantity in XSL */ -public class FixedLength extends LengthProperty { +public final class FixedLength extends LengthProperty { /** cache holding all canonical FixedLength instances */ private static final PropertyCache cache = new PropertyCache(); @@ -33,6 +33,7 @@ public class FixedLength extends LengthProperty { /** * Set the length given a number of units and a unit name. + * * @param numUnits quantity of input units * @param units input unit specifier (in, cm, etc.) */ @@ -41,16 +42,17 @@ public class FixedLength extends LengthProperty { } /** - * Return the canonical FixedLength instance corresponding + * Return the cached FixedLength instance corresponding * to the computed value + * * @param numUnits input units * @param units unit specifier * @return the canonical FixedLength instance corresponding * to the given number of units and unit specifier */ - public static FixedLength getInstance(double numUnits, String units) { - return (FixedLength) cache.fetch( - new FixedLength(numUnits, units)); + public static FixedLength getInstance(double numUnits, + String units) { + return (FixedLength) cache.fetch(new FixedLength(numUnits, units)); } diff --git a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java index 1b31351b8..610882aa4 100644 --- a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java +++ b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java @@ -19,6 +19,8 @@ package org.apache.fop.fo.properties; +import java.util.Iterator; + import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; @@ -26,8 +28,13 @@ import org.apache.fop.fo.expr.PropertyException; /** * Property class for the font-family property. */ -public class FontFamilyProperty extends ListProperty { +public final class FontFamilyProperty extends ListProperty { + /** cache holding all canonical FontFamilyProperty instances */ + private static final PropertyCache cache = new PropertyCache(); + + private int hash = 0; + /** * Inner class for creating instances of ListProperty */ @@ -42,15 +49,12 @@ public class FontFamilyProperty extends ListProperty { /** * {@inheritDoc} - * org.apache.fop.fo.PropertyList, - * java.lang.String, - * org.apache.fop.fo.FObj) */ public Property make(PropertyList propertyList, String value, FObj fo) throws PropertyException { if ("inherit".equals(value)) { return super.make(propertyList, value, fo); } else { - ListProperty prop = new ListProperty(); + FontFamilyProperty prop = new FontFamilyProperty(); String tmpVal; int startIndex = 0; int commaIndex = value.indexOf(','); @@ -89,7 +93,7 @@ public class FontFamilyProperty extends ListProperty { prop.addProperty(StringProperty.getInstance(tmpVal)); } } - return prop; + return cache.fetch(prop); } } @@ -110,12 +114,20 @@ public class FontFamilyProperty extends ListProperty { /** * @param prop the first Property to be added to the list */ - public FontFamilyProperty(Property prop) { + private FontFamilyProperty(Property prop) { super(); addProperty(prop); } /** + * Default constructor. + * + */ + private FontFamilyProperty() { + super(); + } + + /** * Add a new property to the list * @param prop Property to be added to the list */ @@ -137,4 +149,30 @@ public class FontFamilyProperty extends ListProperty { } } + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof FontFamilyProperty) { + FontFamilyProperty ffp = (FontFamilyProperty) o; + return (this.list != null + && this.list.equals(ffp.list)); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + if (this.hash == 0) { + int hash = 17; + for (Iterator i = list.iterator(); i.hasNext();) { + Property p = (Property) i.next(); + hash = 37 * hash + (p == null ? 0 : p.hashCode()); + } + this.hash = hash; + } + return this.hash; + } } diff --git a/src/java/org/apache/fop/fo/properties/KeepProperty.java b/src/java/org/apache/fop/fo/properties/KeepProperty.java index 9ac33fad3..2701c3f59 100644 --- a/src/java/org/apache/fop/fo/properties/KeepProperty.java +++ b/src/java/org/apache/fop/fo/properties/KeepProperty.java @@ -25,9 +25,14 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; /** - * Superclass for properties that wrap Keep values + * Class for properties that wrap Keep values */ -public class KeepProperty extends Property implements CompoundDatatype { +public final class KeepProperty extends Property implements CompoundDatatype { + + /** class holding all canonical KeepProperty instances*/ + private static final PropertyCache cache = new PropertyCache(); + + private boolean isCachedValue = false; private Property withinLine; private Property withinColumn; private Property withinPage; @@ -70,6 +75,10 @@ public class KeepProperty extends Property implements CompoundDatatype { */ public void setComponent(int cmpId, Property cmpnValue, boolean bIsDefault) { + if (isCachedValue) { + log.warn("KeepProperty.setComponent() called on cached value. Ignoring..."); + return; + } if (cmpId == CP_WITHIN_LINE) { setWithinLine(cmpnValue, bIsDefault); } else if (cmpId == CP_WITHIN_COLUMN) { @@ -152,10 +161,14 @@ public class KeepProperty extends Property implements CompoundDatatype { } /** - * @return this.keep + * @return the canonical KeepProperty instance corresponding to + * this property */ public KeepProperty getKeep() { - return this; + KeepProperty keep = (KeepProperty) cache.fetch(this); + /* make sure setComponent() can never alter cached values */ + keep.isCachedValue = true; + return keep; } /** @@ -165,4 +178,27 @@ public class KeepProperty extends Property implements CompoundDatatype { return this; } + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof KeepProperty) { + KeepProperty keep = (KeepProperty) o; + return (keep.withinColumn == this.withinColumn) + && (keep.withinLine == this.withinLine) + && (keep.withinPage == this.withinPage); + } + return false; + } + + /** {@inheritDoc} */ + public int hashCode() { + int hash = 17; + hash = 37 * hash + (withinColumn == null ? 0 : withinColumn.hashCode()); + hash = 37 * hash + (withinLine == null ? 0 : withinLine.hashCode()); + hash = 37 * hash + (withinPage == null ? 0 : withinPage.hashCode()); + return hash; + } } diff --git a/src/java/org/apache/fop/fo/properties/PropertyCache.java b/src/java/org/apache/fop/fo/properties/PropertyCache.java index 0cca50bea..22f001a07 100644 --- a/src/java/org/apache/fop/fo/properties/PropertyCache.java +++ b/src/java/org/apache/fop/fo/properties/PropertyCache.java @@ -20,39 +20,353 @@ package org.apache.fop.fo.properties; import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; /** - * Thin wrapper around a HashMap to implement the property caching idiom - * in which a new Property instance is created then tested against cached - * instances created previously. If an existing property is found, this is - * retained and the newly created one is instantly eligible for garbage - * collection. + * Dedicated cache, meant for storing canonical instances + * of property-related classes. + * The public access points are overloaded <code>fetch()</code> methods + * that each correspond to a cached type. + * It is designed especially to be used concurrently by multiple threads, + * drawing heavily upon the principles behind Java 1.5's + * <code>ConcurrentHashMap</code>. */ -public class PropertyCache { +public final class PropertyCache { - private Map propCache = Collections.synchronizedMap(new WeakHashMap()); + /** bitmask to apply to the hash to get to the + * corresponding cache segment */ + private static final int SEGMENT_MASK = 0x1F; + /** the segments array (length = 32) */ + private CacheSegment[] segments = new CacheSegment[SEGMENT_MASK + 1]; + /** the table of hash-buckets */ + private CacheEntry[] table = new CacheEntry[8]; + + /* same hash function as used by java.util.HashMap */ + private static int hash(Object x) { + int h = x.hashCode(); + + h += ~(h << 9); + h ^= (h >>> 14); + h += (h << 4); + h ^= (h >>> 10); + return h; + } + + /* shortcut function */ + private static boolean eq(Object p, Object q) { + return (p == q || (p != null && p.equals(q))); + } + + /* Class modeling a cached entry */ + private final class CacheEntry { + final CacheEntry next; + volatile WeakReference ref; + final int hash; + + /* main constructor */ + CacheEntry(Object p, CacheEntry next) { + this.next = next; + this.ref = new WeakReference(p); + this.hash = p.hashCode(); + } + + /* clone constructor */ + CacheEntry(CacheEntry old, CacheEntry next) { + this.next = next; + this.ref = old.ref; + this.hash = old.hash; + } + + } + + /* Wrapper objects to synchronize on */ + private final class CacheSegment { + private int count = 0; + } + + /* + * Class modeling a cleanup thread. + * + * Once run() is called, the segment is locked and the hash-bucket + * will be traversed, removing any obsolete entries. + * If the cleanup has no effect, rehash() is called. + */ + private final class CacheCleaner implements Runnable { + + private int hash; + + CacheCleaner(int hash) { + this.hash = hash; + } + + public void run() { + //System.out.println("Cleaning segment " + this.segment); + CacheSegment segment = segments[this.hash & SEGMENT_MASK]; + int oldCount; + int newCount; + synchronized (segment) { + oldCount = segment.count; + /* check first to see if another cleaner thread already + * pushed the number of entries back below the threshold + * if so, return immediately + */ + if (segment.count < (2 * table.length)) { + return; + } + + int index = this.hash & (table.length - 1); + CacheEntry first = table[index]; + WeakReference ref; + for (CacheEntry e = first; e != null; e = e.next) { + ref = e.ref; + if (ref != null && ref.get() == null) { + /* remove obsolete entry + /* 1. clear value, cause interference for non-blocking get() */ + e.ref = null; + + /* 2. clone the segment, without the obsolete entry */ + CacheEntry head = e.next; + for (CacheEntry c = first; c != e; c = c.next) { + head = new CacheEntry(c, head); + } + table[index] = head; + segment.count--; + } + } + newCount = segment.count; + } + if (oldCount == newCount) { + /* cleanup had no effect, try rehashing */ + rehash(SEGMENT_MASK); + } + } + } + + /* + * Puts a new instance in the cache. + * If the total number of entries for the corresponding + * segment exceeds twice the amount of hash-buckets, a + * cleanup thread will be launched to remove obsolete + * entries. + */ + private final void put(Object o) { + + int hash = hash(o); + CacheSegment segment = segments[hash & SEGMENT_MASK]; + + synchronized (segment) { + int index = hash & (table.length - 1); + CacheEntry entry = table[index]; + + if (entry == null) { + entry = new CacheEntry(o, null); + table[index] = entry; + segment.count++; + } else { + WeakReference ref = entry.ref; + if (ref != null && eq(ref.get(), o)) { + return; + } else { + CacheEntry newEntry = new CacheEntry(o, entry); + table[index] = newEntry; + segment.count++; + } + } + + if (segment.count > (2 * table.length)) { + /* launch cleanup in a separate thread, + * so it acquires its own lock, and put() + * can return immediately */ + Thread cleaner = new Thread(new CacheCleaner(hash)); + cleaner.start(); + } + } + } + + + /* Gets a cached instance. Returns null if not found */ + private final Object get(Object o) { + + int hash = hash(o); + int index = hash & (table.length - 1); + + CacheEntry entry = table[index]; + WeakReference r; + Object q; + + /* try non-synched first */ + for (CacheEntry e = entry; e != null; e = e.next) { + if (e.hash == o.hashCode() + && (r = e.ref) != null + && (q = r.get()) != null + && eq(q, o)) { + return q; + } + } + + /* retry synched, only if the above attempt did not succeed, + * as another thread may, in the meantime, have added a + * corresponding entry */ + CacheSegment segment = segments[hash & SEGMENT_MASK]; + synchronized (segment) { + entry = table[index]; + for (CacheEntry e = entry; e != null; e = e.next) { + if (e.hash == o.hashCode() + && (r = e.ref) != null + && (q = r.get()) != null + && eq(q, o)) { + return q; + } + } + } + return null; + } + + /* + * Recursively acquires locks on all 32 segments, + * then performs a check on the segments first to see ` + * how many precisely exceed the threshold ( 2 x table.length ). + * If this number exceeds half the amount of buckets, + * extends the cache and redistributes the entries. + * + * Example: + * For a cache with default size of 8 buckets, each bucket is + * a segment, and as such, rehash() will only have effect + * if more than 4 buckets exceed the size of 16 entries. + * + */ + private final void rehash(int index) { + + CacheSegment seg = segments[index]; + synchronized (seg) { + if (index > 0) { + /* need to recursively acquire locks on all segments */ + rehash(index - 1); + } else { + /* double the amount of buckets */ + int newLength = table.length << 1; + if (newLength > 0) { //no overflow? + /* Check segmentcounts first */ + int countSegments = 0; + int threshold = table.length * 2; + for (int i = segments.length; --i >= 0;) { + if (segments[i].count > threshold) { + countSegments++; + } + } + + if (countSegments <= (table.length / 2)) { + return; + } else { + /* reset segmentcounts */ + for (int i = segments.length; --i >= 0;) { + segments[i].count = 0; + } + } + + CacheEntry[] newTable = new CacheEntry[newLength]; + + int hash, idx; + WeakReference ref; + Object o; + newLength--; + for (int i = table.length; --i >= 0;) { + for (CacheEntry c = table[i]; c != null; c = c.next) { + ref = c.ref; + if ((o = ref.get()) != null) { + hash = hash(o); + idx = hash & newLength; + newTable[idx] = new CacheEntry(c, newTable[idx]); + segments[hash & SEGMENT_MASK].count++; + } + } + } + table = newTable; + } + } + } + } + + /** + * Default constructor. + */ + public PropertyCache() { + for (int i = SEGMENT_MASK + 1; --i >= 0;) { + segments[i] = new CacheSegment(); + } + } + + /** + * Generic fetch() method. + * Checks if the given <code>Object</code> is present in the cache - + * if so, returns a reference to the cached instance. + * Otherwise the given object is added to the cache and returned. + * + * @param obj the Object to check for + * @return the cached instance + */ + private final Object fetch(Object obj) { + if (obj == null) { + return null; + } + + Object cacheEntry = get(obj); + if (cacheEntry != null) { + return cacheEntry; + } + put(obj); + return obj; + } /** - * Checks if the given property is present in the cache - if so, returns - * a reference to the cached value. Otherwise the given object is added - * to the cache and returned. + * Checks if the given <code>Property</code> is present in the cache - + * if so, returns a reference to the cached instance. + * Otherwise the given object is added to the cache and returned. + * * @param prop the Property instance to check for * @return the cached instance */ - public Property fetch(Property prop) { + public final Property fetch(Property prop) { - WeakReference ref = (WeakReference) propCache.get(prop); - if (ref != null) { - Property cacheEntry = (Property)ref.get(); - if (cacheEntry != null) { - return cacheEntry; - } - } - propCache.put(prop, new WeakReference(prop)); - return prop; + return (Property) fetch((Object) prop); + } + + /** + * Checks if the given <code>CommonHyphenation</code> is present in the cache - + * if so, returns a reference to the cached instance. + * Otherwise the given object is added to the cache and returned. + * + * @param chy the CommonHyphenation instance to check for + * @return the cached instance + */ + public final CommonHyphenation fetch(CommonHyphenation chy) { + + return (CommonHyphenation) fetch((Object) chy); + } + + /** + * Checks if the given <code>CachedCommonFont</code> is present in the cache - + * if so, returns a reference to the cached instance. + * Otherwise the given object is added to the cache and returned. + * + * @param ccf the CachedCommonFont instance to check for + * @return the cached instance + */ + public final CommonFont.CachedCommonFont fetch(CommonFont.CachedCommonFont ccf) { + + return (CommonFont.CachedCommonFont) fetch((Object) ccf); + } + + /** + * Checks if the given <code>CommonFont</code> is present in the cache - + * if so, returns a reference to the cached instance. + * Otherwise the given object is added to the cache and returned. + * + * @param cf the CommonFont instance to check for + * @return the cached instance + */ + public final CommonFont fetch(CommonFont cf) { + + return (CommonFont) fetch((Object) cf); } } diff --git a/src/java/org/apache/fop/fonts/FontInfo.java b/src/java/org/apache/fop/fonts/FontInfo.java index 995d59bf8..e833b7b93 100644 --- a/src/java/org/apache/fop/fonts/FontInfo.java +++ b/src/java/org/apache/fop/fonts/FontInfo.java @@ -41,6 +41,8 @@ import org.apache.commons.logging.LogFactory; */ public class FontInfo { + private static final FontTriplet[] TRIPLETS_TYPE = new FontTriplet[1]; + /** logging instance */ protected static Log log = LogFactory.getLog(FontInfo.class); @@ -232,26 +234,35 @@ public class FontInfo { } /** - * Lookup a font. + * Looks up a set of fonts. * <br> - * Locate the font name for a given family, style and weight. - * The font name can then be used as a key as it is unique for + * Locate the font name(s) for the given families, style and weight. + * The font name(s) can then be used as a key as they are unique for * the associated document. - * This also adds the font to the list of used fonts. - * @param family font family (priority list) - * @param style font style - * @param weight font weight - * @return font triplet of the font chosen + * This also adds the fonts to the list of used fonts. + * @param families font families (priority list) + * @param style font style + * @param weight font weight + * @return the set of font triplets of all supported and chosen font-families + * in the specified style and weight. */ - public FontTriplet fontLookup(String[] family, String style, + public FontTriplet[] fontLookup(String[] families, String style, int weight) { - for (int i = 0; i < family.length; i++) { - FontTriplet triplet = fontLookup(family[i], style, weight, (i >= family.length - 1)); + FontTriplet triplet; + List tmpTriplets = new ArrayList(); + for (int i = 0; i < families.length; i++) { + triplet = fontLookup(families[i], style, weight, (i >= families.length - 1)); if (triplet != null) { - return triplet; + tmpTriplets.add(triplet); } } - throw new IllegalStateException("fontLookup must return a key on the last call"); + if (tmpTriplets.size() != 0) { + FontTriplet[] triplets = (FontTriplet[]) tmpTriplets.toArray(TRIPLETS_TYPE); + return (FontTriplet[]) triplets; + } + throw new IllegalStateException( + "fontLookup must return an array with at least one " + + "FontTriplet on the last call."); } private void notifyFontReplacement(FontTriplet replacedKey, FontTriplet newKey) { diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 0300a6cf2..597f41ad0 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -30,6 +30,8 @@ import org.apache.fop.area.Block; import org.apache.fop.area.LineArea; import org.apache.fop.datatypes.Length; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; import org.apache.fop.layoutmgr.inline.LineLayoutManager; @@ -78,11 +80,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager public void initialize() { super.initialize(); - Font fs = getBlockFO().getCommonFont().getFontState( - getBlockFO().getFOEventHandler().getFontInfo(), this); - - lead = fs.getAscender(); - follow = -fs.getDescender(); + FontInfo fi = getBlockFO().getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = getBlockFO().getCommonFont().getFontState(fi); + Font initFont = fi.getFontInstance(fontkeys[0], getBlockFO().getCommonFont().fontSize.getValue(this)); + lead = initFont.getAscender(); + follow = -initFont.getDescender(); //middleShift = -fs.getXHeight() / 2; lineHeight = getBlockFO().getLineHeight().getOptimum(this).getLength(); startIndent = getBlockFO().getCommonMarginBlock().startIndent.getValue(this); diff --git a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java index 25e196398..f470e137e 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java @@ -21,6 +21,8 @@ package org.apache.fop.layoutmgr.inline; import org.apache.fop.fo.flow.Character; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; @@ -63,10 +65,12 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void initialize() { - font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing()); letterSpaceIPD = ls.getSpace(); - hyphIPD = font.getCharWidth(fobj.getCommonHyphenation().hyphenationCharacter); + hyphIPD = font.getCharWidth(fobj.getCommonHyphenation().hyphenationCharacter.getCharacter()); borderProps = fobj.getCommonBorderPaddingBackground(); setCommonBorderPaddingBackground(borderProps); org.apache.fop.area.inline.TextArea chArea = getCharacterInlineArea(fobj); diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index 30402a5f2..e9b6c19ec 100755 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -39,6 +39,8 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.BlockKnuthSequence; import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BreakElement; @@ -87,6 +89,8 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { protected int dominantBaseline; /** The line height property */ protected SpaceProperty lineHeight; + /** The keep-together property */ + //private KeepProperty keepTogether; private AlignmentContext alignmentContext = null; @@ -110,7 +114,9 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { /** {@inheritDoc} */ public void initialize() { int padding = 0; - font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); lineHeight = fobj.getLineHeight(); borderProps = fobj.getCommonBorderPaddingBackground(); inlineProps = fobj.getCommonMarginInline(); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java index a7c5178b0..5f93ab98c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java @@ -26,6 +26,8 @@ import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.flow.Leader; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; @@ -67,7 +69,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void initialize() { - font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); // the property leader-alignment does not affect vertical positioning // (see section 7.21.1 in the XSL Recommendation) // setAlignment(node.getLeaderAlignment()); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index ad1f6d231..d6f88f54f 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -59,6 +59,8 @@ import java.util.ArrayList; import java.util.LinkedList; import org.apache.fop.area.Trait; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.traits.MinOptMax; @@ -569,7 +571,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager /** {@inheritDoc} */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { - Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); alignmentContext = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode()); context.setAlignmentContext(alignmentContext); @@ -941,7 +945,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager ? 0 : hyphenationLadderCount.getValue(), this); - if (hyphenationProperties.hyphenate == EN_TRUE + if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE && fobj.getWrapOption() != EN_NO_WRAP) { findHyphenationPoints(currPar); } @@ -967,8 +971,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager } // now try something different - log.debug("Hyphenation possible? " + (hyphenationProperties.hyphenate == EN_TRUE)); - if (hyphenationProperties.hyphenate == EN_TRUE + log.debug("Hyphenation possible? " + (hyphenationProperties.hyphenate.getEnum() == EN_TRUE)); + if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE && !(allowedBreaks == BreakingAlgorithm.ONLY_FORCED_BREAKS)) { // consider every hyphenation point as a legal break allowedBreaks = BreakingAlgorithm.ALL_BREAKS; @@ -983,10 +987,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager // the second try failed too, try with a huge threshold // and force the algorithm to find // a set of breaking points - log.debug("No set of breaking points found with maxAdjustment = " - + maxAdjustment - + (hyphenationProperties.hyphenate == EN_TRUE - ? " and hyphenation" : "")); + if (log.isDebugEnabled()) { + log.debug("No set of breaking points found with maxAdjustment = " + + maxAdjustment + + (hyphenationProperties.hyphenate.getEnum() == EN_TRUE + ? " and hyphenation" : "")); + } maxAdjustment = 20; iBPcount = alg.findBreakingPoints(currPar, @@ -1526,12 +1532,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager // since these properties inherit and could be specified // on an inline or wrapper below the block level. Hyphenation hyph - = Hyphenator.hyphenate(hyphenationProperties.language, - hyphenationProperties.country, + = Hyphenator.hyphenate(hyphenationProperties.language.getString(), + hyphenationProperties.country.getString(), getFObj().getUserAgent().getFactory().getHyphenationTreeResolver(), sbChars.toString(), - hyphenationProperties.hyphenationRemainCharacterCount, - hyphenationProperties.hyphenationPushCharacterCount); + hyphenationProperties.hyphenationRemainCharacterCount.getValue(), + hyphenationProperties.hyphenationPushCharacterCount.getValue()); // They hyph structure contains the information we need // Now start from prev: reset to that position, ask that LM to get // a Position for the first hyphenation offset. If the offset isn't in diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java index fda62ea15..f3a83b9f2 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java @@ -27,6 +27,8 @@ import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.UnresolvedPageNumber; import org.apache.fop.area.inline.TextArea; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.PositionIterator; @@ -57,7 +59,9 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void initialize() { - font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); setCommonBorderPaddingBackground(fobj.getCommonBorderPaddingBackground()); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java index d09523e3c..88f1d283c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java @@ -24,6 +24,8 @@ import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.Trait; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; @@ -49,7 +51,9 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void initialize() { - font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); setCommonBorderPaddingBackground(fobj.getCommonBorderPaddingBackground()); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index adb76846c..e62f3dc2a 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -31,6 +31,8 @@ import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FOText; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; @@ -186,12 +188,14 @@ public class TextLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void initialize() { - font = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo(), this); + FontInfo fi = foText.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = foText.getCommonFont().getFontState(fi); + font = fi.getFontInstance(fontkeys[0], foText.getCommonFont().fontSize.getValue(this)); // With CID fonts, space isn't neccesary currentFontState.width(32) spaceCharIPD = font.getCharWidth(' '); // Use hyphenationChar property - hyphIPD = font.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter); + hyphIPD = font.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter.getCharacter()); SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing()); halfLS = new SpaceVal(MinOptMax.multiply(ls.getSpace(), 0.5), @@ -504,7 +508,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { && i == lastIndex && areaInfo.bHyphenated) { // add the hyphenation character - wordChars.append(foText.getCommonHyphenation().hyphenationCharacter); + wordChars.append(foText.getCommonHyphenation().hyphenationCharacter.getCharacter()); } textArea.addWord(wordChars.toString(), 0, letterAdjust); wordStartIndex = -1; diff --git a/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java b/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java index d85a634b8..d40c6a826 100644 --- a/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java +++ b/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java @@ -142,16 +142,16 @@ final class TextAttributesConverter { RtfFontManager.getInstance().getFontNumber(font.getFirstFontFamily())); rtfAttr.setHalfPoints(RtfText.ATTR_FONT_SIZE, font.fontSize); - if (font.fontWeight == Constants.EN_700 - || font.fontWeight == Constants.EN_800 - || font.fontWeight == Constants.EN_900) { + if (font.getFontWeight() == Constants.EN_700 + || font.getFontWeight() == Constants.EN_800 + || font.getFontWeight() == Constants.EN_900) { //Everything from 700 and above is declared as bold rtfAttr.set("b", 1); } else { rtfAttr.set("b", 0); } - if (font.fontStyle == Constants.EN_ITALIC) { + if (font.getFontStyle() == Constants.EN_ITALIC) { rtfAttr.set(RtfText.ATTR_ITALIC, 1); } else { rtfAttr.set(RtfText.ATTR_ITALIC, 0); diff --git a/status.xml b/status.xml index 645ca66c1..ccf8190de 100644 --- a/status.xml +++ b/status.xml @@ -28,6 +28,23 @@ <changes> <release version="FOP Trunk"> + <action context="code" dev="AD" type="update"> + Facilitate the implementation for font-selection-strategy: + * Changed FontInfo.fontLookup to always return an array of FontTriplet + * Changed CommonFont.getFontState to return an array of FontTriplet + * Initial modifications to the related LMs: initialized with + the font corresponding to the first of the triplets + </action> + <action context="code" dev="AD" type="update"> + PropertyCache phase 2: + * improvement of the PropertyCache itself should now guarantee acceptable + performance of the static caches in multi-session environments, which is + a possible problem with synchronizedMap + * changed CommonFont to use the cache: + added CachedCommonFont to contain the properties that are always cacheable + CommonFont itself is only cached if the remaining properties are absolutes + * changed CommonHyphenation, KeepProperty, ColorProperty and FontFamilyProperty to use the cache + </action> <action context="Code" dev="AD" type="fix" fixes-bug="42705"> Fixed swallowing PCDATA in text-node children of retrieved markers. |