* improvement of the PropertyCache itself should 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 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 This line, and those below, will be ignored-- M src/java/org/apache/fop/render/rtf/TextAttributesConverter.java M src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/PageNumberCitationLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java M src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java M src/java/org/apache/fop/fonts/FontInfo.java M src/java/org/apache/fop/fo/properties/CommonHyphenation.java M src/java/org/apache/fop/fo/properties/CommonAccessibility.java M src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java M src/java/org/apache/fop/fo/properties/FontFamilyProperty.java M src/java/org/apache/fop/fo/properties/FixedLength.java M src/java/org/apache/fop/fo/properties/PropertyCache.java M src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java M src/java/org/apache/fop/fo/properties/CommonFont.java M src/java/org/apache/fop/fo/properties/ColorProperty.java M src/java/org/apache/fop/fo/properties/KeepProperty.java M src/java/org/apache/fop/fo/PropertyList.java M src/java/org/apache/fop/fo/FOPropertyMapping.java M src/java/org/apache/fop/fo/expr/PropertyParser.java M src/java/org/apache/fop/fo/expr/ICCColorFunction.java M src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java M src/java/org/apache/fop/fo/expr/RGBColorFunction.java M src/java/org/apache/fop/fo/expr/SystemColorFunction.java M status.xml git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@578311 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_95beta
@@ -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); | |||
} |
@@ -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); | |||
} | |||
/** |
@@ -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()); | |||
} | |||
@@ -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()); | |||
} | |||
@@ -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: |
@@ -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] + ")"); | |||
} | |||
@@ -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] + ")"); | |||
} | |||
@@ -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(); | |||
} | |||
} | |||
@@ -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 { |
@@ -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); |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -25,12 +25,6 @@ import org.apache.fop.fo.FObj; | |||
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. | |||
*/ |
@@ -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)); | |||
} | |||
@@ -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,11 +114,19 @@ 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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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) { |
@@ -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); |
@@ -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); |
@@ -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(); |
@@ -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()); |
@@ -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 |
@@ -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()); | |||
} | |||
@@ -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()); | |||
} | |||
@@ -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; |
@@ -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); |
@@ -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. |