diff options
author | Adrian Cumiskey <acumiskey@apache.org> | 2008-05-08 15:45:26 +0000 |
---|---|---|
committer | Adrian Cumiskey <acumiskey@apache.org> | 2008-05-08 15:45:26 +0000 |
commit | f9f85a1e74881074d432f426249b2f62a9b3712f (patch) | |
tree | 7d3f2bf092326f6b21b59f716c93a82155991512 | |
parent | bba0a03fd929ac83d2b08daf49bf5643f90a3686 (diff) | |
download | xmlgraphics-fop-f9f85a1e74881074d432f426249b2f62a9b3712f.tar.gz xmlgraphics-fop-f9f85a1e74881074d432f426249b2f62a9b3712f.zip |
Merged revisions 654111,654121,654190,654314,654322-654323,654338,654347,654437,654461,654528,654557 via svnmerge from
https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk
........
r654111 | adelmelle | 2008-05-07 15:04:17 +0100 (Wed, 07 May 2008) | 6 lines
Bugzilla 41500:
Fixed a ClassCastException when fo:wrapper was used as a child of an fo:block-container.
Bugzilla 42423:
Added support for the "id" attribute on fo:wrappers that are children of the fo:flow.
........
r654121 | adelmelle | 2008-05-07 15:19:13 +0100 (Wed, 07 May 2008) | 1 line
Small correction: use appropriate value for 'context'
........
r654190 | adelmelle | 2008-05-07 18:41:07 +0100 (Wed, 07 May 2008) | 1 line
Added comment
........
r654314 | adelmelle | 2008-05-07 23:45:46 +0100 (Wed, 07 May 2008) | 1 line
Corrected errors in ColumnNumberPropertyMaker.java; temporarily disabled two testcases for a non-standard feature
........
r654322 | acumiskey | 2008-05-08 00:27:45 +0100 (Thu, 08 May 2008) | 1 line
Looks like the serialVersionUID got bumped but the log statement wasn't being completely honest about what it was doing. FontCache now attempts to delete its cache file so subsequent runs should repopulate it.
........
r654323 | acumiskey | 2008-05-08 00:29:04 +0100 (Thu, 08 May 2008) | 1 line
Whoops.. small checkstyle nit :)
........
r654338 | adelmelle | 2008-05-08 01:18:14 +0100 (Thu, 08 May 2008) | 1 line
Removed inadvertently added override [Avoid late night commits...]
........
r654347 | acumiskey | 2008-05-08 01:33:21 +0100 (Thu, 08 May 2008) | 1 line
Removed tabs. Checkstyle now configured. Late nights.. and a new machine build.. :S
........
r654437 | jeremias | 2008-05-08 08:32:02 +0100 (Thu, 08 May 2008) | 1 line
Checkstyle fix.
........
r654461 | jeremias | 2008-05-08 10:55:55 +0100 (Thu, 08 May 2008) | 5 lines
Added support for font referencing for auto-detected fonts.
Referenced TrueType fonts can now use glyphs outside the WinAnsi charset (Same approach as for extended glyph support with Type 1 fonts).
Removed FopFactory reference from FontManager to make the class more useful outside this context (ex. transcoders).
Updated fonts documentation with information that TTCs are now supported through auto-detection.
........
r654528 | adelmelle | 2008-05-08 14:56:14 +0100 (Thu, 08 May 2008) | 5 lines
Tweak:
-> add 'runtimeType' member and toString() to PropertyCache to facilitate debugging
-> modify the initial assignment in the related Property types accordingly
........
r654557 | pietsch | 2008-05-08 16:36:02 +0100 (Thu, 08 May 2008) | 1 line
Quick fix for XML validation error
........
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@654562 13f79535-47bb-0310-9956-ffa450edef68
41 files changed, 552 insertions, 239 deletions
diff --git a/src/documentation/content/xdocs/trunk/fonts.xml b/src/documentation/content/xdocs/trunk/fonts.xml index eb466ec73..30b72ffac 100644 --- a/src/documentation/content/xdocs/trunk/fonts.xml +++ b/src/documentation/content/xdocs/trunk/fonts.xml @@ -177,10 +177,8 @@ if you wanted to add a custom font. This unconvenient step has been removed and in addition to that, FOP supports auto-registration of fonts, i.e. FOP can find fonts installed in your operating system or can scan user-specified directories for fonts. - Font registration via XML font metrics file is still supported and is still necessary - if you want to use a TrueType Collection (*.ttc). Direct support for TrueType - collections may be added later. Furthermore, the XML font metrics files are still - required if you don't want to embed, but only reference a font. + Font registration via XML font metrics file is still supported and may still be necessary + for some very special cases as fallback variant while we stabilize font auto-detection. </p> <p> Basic information about fonts can be found at: @@ -375,20 +373,56 @@ </section> <section id="embedding"> <title>Embedding</title> - <note>The PostScript renderer does not yet support TrueType fonts, but can embed Type 1 fonts.</note> - <note>The font is simply embedded into the PDF file, it is not converted.</note> - <p>Font embedding is enabled in the userconfig.xml file and controlled by the embed-url attribute. - If you don't specify the embed-url attribute the font will not be embedded, but will only be referenced.</p> - <warning> - Omitting the embed-url attribute for CID-encoded TrueType fonts will currently produce invalid - PDF files! If you create the XML font metric file using the "-enc ansi" option, you can omit - the embed-url attribute for TrueType fonts but you're restricted to the WinAnsi character set. - </warning> - <p>When FOP embeds a font, it adds a prefix to the fontname to ensure that the name will not match the fontname of an installed font. - This is helpful with older versions of Acrobat Reader that preferred installed fonts over embedded fonts.</p> - <p>When embedding PostScript fonts, the entire font is always embedded.</p> - <p>When embedding TrueType fonts (ttf) or TrueType Collections (ttc), a subset of the - original font, containing only the glyphs used, is embedded in the output document.</p> + <p> + By default, all fonts are embedded if an output format supports font embedding. In some + cases, however, it is preferred that some fonts are only referenced. When working + with referenced fonts it is important to be in control of the target environment where + the produced document is consumed, i.e. the necessary fonts have to be installed there. + </p> + <p> + There are two different ways how you can specify that a font should be referenced: + </p> + <ol> + <li> + When using the old-style "font" element to configure a single font, font referencing + is controlled by the embed-url attribute. If you don't specify the embed-url attribute + the font will not be embedded, but will only be referenced. + </li> + <li> + For automatically configured fonts there's a different mechanism to specify which + fonts should be referenced rather than embedded. This is done in the "referenced-fonts" + element in the configuration. Here's an example: + </li> + </ol> + <source><![CDATA[ +<fop version="1.0"> + <fonts> + <referenced-fonts> + <match font-family="Helvetica"/> + <match font-family="DejaVu.*"/> + </referenced-fonts> + </fonts> +</fop>]]></source> + <p> + At the moment, you can only match fonts against their font-family. It is possible to use + regular expressions as is shown in the second example above ("DejaVu.*"). The syntax for + the regular expressions used here are the one used by the + <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/regex/package-summary.html"><code>java.util.regex</code> package</a>. + So, in the above snippet "Helvetica" and all variants of the "DejaVu" font family are + referenced. If you want to reference all fonts, just specify <code>font-family=".*"</code>. + </p> + <p> + Various notes related to embedded fonts: + </p> + <ul> + <li>The PostScript renderer does not yet support TrueType fonts, but can embed Type 1 fonts.</li> + <li>The font is simply embedded into the PDF file, it is not converted.</li> + <li>When FOP embeds a font, it adds a prefix to the fontname to ensure that the name will not match the fontname of an installed font. + This is helpful with older versions of Acrobat Reader that preferred installed fonts over embedded fonts.</li> + <li>When embedding PostScript fonts, the entire font is always embedded.</li> + <li>When embedding TrueType fonts (ttf) or TrueType Collections (ttc), a subset of the + original font, containing only the glyphs used, is embedded in the output document.</li> + </ul> </section> <section id="substitution"> <title>Substitution</title> diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index bd54ffb2f..c196e1204 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -692,12 +692,19 @@ public class FopFactory implements ImageContext { } /** - * Returns the font manager + * Returns the font manager. * @return the font manager */ public FontManager getFontManager() { if (fontManager == null) { - this.fontManager = new FontManager(this); + this.fontManager = new FontManager() { + + /** {@inheritDoc} */ + public void setFontBaseURL(String fontBase) throws MalformedURLException { + super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase)); + } + + }; } return this.fontManager; } diff --git a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java index 89388ae77..cc3ce2d0b 100644 --- a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java +++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java @@ -23,15 +23,17 @@ import java.io.File; import java.io.IOException; import java.net.MalformedURLException; +import org.xml.sax.SAXException; + import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.fonts.FontManager; import org.apache.fop.fonts.FontManagerConfigurator; import org.apache.fop.util.LogUtil; -import org.xml.sax.SAXException; /** * FopFactory configurator @@ -174,7 +176,7 @@ public class FopFactoryConfigurator { // configure font manager FontManager fontManager = factory.getFontManager(); FontManagerConfigurator fontManagerConfigurator = new FontManagerConfigurator(cfg); - fontManagerConfigurator.configure(fontManager); + fontManagerConfigurator.configure(fontManager, strict); } /** diff --git a/src/java/org/apache/fop/events/EventFormatter.xml b/src/java/org/apache/fop/events/EventFormatter.xml index 74e120407..4bff75f85 100644 --- a/src/java/org/apache/fop/events/EventFormatter.xml +++ b/src/java/org/apache/fop/events/EventFormatter.xml @@ -26,9 +26,10 @@ <message key="rule.childOfSPM">The element must be a child of fo:simple-page-master.</message> <message key="rule.childOfDeclarations">The element must be a child of fo:declarations.</message> <message key="rule.childOfSPMorDeclarations">The element must be a child of fo:declarations or fo:simple-page-master.</message> + <message key="rule.wrapperInvalidChildForParent">An fo:wrapper is only permitted to have children that would be permitted for its parent.</message> <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message> <message key="org.apache.fop.fo.FOValidationEventProducer.nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message> - <message key="org.apache.fop.fo.FOValidationEventProducer.invalidChild">"{offendingNode}" is not a valid child element of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message> + <message key="org.apache.fop.fo.FOValidationEventProducer.invalidChild">"{offendingNode}" is not a valid child of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message> <message key="org.apache.fop.fo.FOValidationEventProducer.missingChildElement">"{elementName}" is missing child elements.[ Required content model: {contentModel}]{{locator}}</message> <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Element "{elementName}" is missing required property "{propertyName}"!{{locator}}</message> diff --git a/src/java/org/apache/fop/fo/flow/Wrapper.java b/src/java/org/apache/fop/fo/flow/Wrapper.java index 4b1cfeeac..5ed594ecc 100644 --- a/src/java/org/apache/fop/fo/flow/Wrapper.java +++ b/src/java/org/apache/fop/fo/flow/Wrapper.java @@ -22,9 +22,10 @@ package org.apache.fop.fo.flow; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObjMixed; -import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; /** @@ -39,25 +40,15 @@ public class Wrapper extends FObjMixed { // used for FO validation private boolean blockOrInlineItemFound = false; - private boolean inlineChildrenAllowed = false; /** - * Base constructor + * Create a Wrapper instance that is a child of the + * given {@link FONode} * * @param parent {@link FONode} that is the parent of this object */ public Wrapper(FONode parent) { super(parent); - /* Check if the fo:wrapper is a child of a FO that allows mixed content - * (or a descendant in nested fo:wrapper sequence, the first of which - * is a child of a FO that allows mixed content) */ - FONode ancestor = this.parent; - while (ancestor instanceof Wrapper) { - ancestor = ancestor.getParent(); - } - if (ancestor instanceof FObjMixed ) { - inlineChildrenAllowed = true; - } } /** @@ -66,7 +57,6 @@ public class Wrapper extends FObjMixed { * <br><i>Additionally (unimplemented): "An fo:wrapper that is a child of an * fo:multi-properties is only permitted to have children that would * be permitted in place of the fo:multi-properties."</i> - * */ protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { @@ -77,8 +67,17 @@ public class Wrapper extends FObjMixed { "(#PCDATA|%inline;|%block;)"); } } else if (isBlockOrInlineItem(nsURI, localName)) { - //delegate validation to parent - FONode.validateChildNode(this.parent, loc, nsURI, localName); + /* delegate validation to parent, but keep the error reporting + * tidy. If we would simply call validateChildNode() on the + * parent, the user would get a wrong impression, as only the + * locator (if any) will contain a reference to the offending + * fo:wrapper. + */ + try { + FONode.validateChildNode(this.parent, loc, nsURI, localName); + } catch (ValidationException vex) { + invalidChildError(loc, getName(), FO_URI, localName, "rule.wrapperInvalidChildForParent"); + } blockOrInlineItemFound = true; } else { invalidChildError(loc, nsURI, localName); @@ -87,15 +86,27 @@ public class Wrapper extends FObjMixed { } /** {@inheritDoc} */ - protected void addCharacters( - char[] data, - int start, - int end, - PropertyList pList, - Locator locator) throws FOPException { - /* Only add text if the fo:wrapper's parent allows inline children */ - if (this.inlineChildrenAllowed) { - super.addCharacters(data, start, end, pList, locator); + protected void addChildNode(FONode child) throws FOPException { + super.addChildNode(child); + /* If the child is a text node, and it generates areas + * (i.e. contains either non-white-space or preserved + * white-space), then check whether the nearest non-wrapper + * ancestor allows this. + */ + if (child instanceof FOText + && ((FOText)child).willCreateArea()) { + FONode ancestor = parent; + while (ancestor.getNameId() == Constants.FO_WRAPPER) { + ancestor = ancestor.getParent(); + } + if (!(ancestor instanceof FObjMixed)) { + invalidChildError( + getLocator(), + getLocalName(), + FONode.FO_URI, + "#PCDATA", + "rule.wrapperInvalidChildForParent"); + } } } diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java index 9b60de740..ca1afa622 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java @@ -51,7 +51,7 @@ public abstract class TableFObj extends FObj { /** * Main constructor - * + * * @param parent the parent node */ public TableFObj(FONode parent) { @@ -76,7 +76,7 @@ public abstract class TableFObj extends FObj { } /** - * + * * @param side the side for which to return the border precedence * @return the "border-precedence" value for the given side */ @@ -98,9 +98,9 @@ public abstract class TableFObj extends FObj { /** * Convenience method to returns a reference * to the base Table instance - * + * * @return the base table instance - * + * */ public Table getTable() { // Will be overridden in Table; for any other Table-node, recursive call to @@ -142,7 +142,7 @@ public abstract class TableFObj extends FObj { * Check the value of the column-number property. * Return the parent's column index (initial value) in case * of a negative or zero value - * + * * @see org.apache.fop.fo.properties.PropertyMaker#make(PropertyList, String, FObj) */ public Property make(PropertyList propertyList, String value, FObj fo) @@ -155,15 +155,18 @@ public abstract class TableFObj extends FObj { int columnIndex = p.getNumeric().getValue(); int colSpan = propertyList.get(Constants.PR_NUMBER_COLUMNS_SPANNED) .getNumeric().getValue(); - int i = -1; - while (++i < colSpan) { - if (columnIndexManager.isColumnNumberUsed(columnIndex + i)) { + + int i = columnIndex - 1; + int lastIndex = (columnIndex + colSpan) - 1; + while (++i < lastIndex) { + if (columnIndexManager.isColumnNumberUsed(i)) { /* if column-number is already in use by another * cell/column => error! */ TableEventProducer eventProducer = TableEventProducer.Provider.get( fo.getUserAgent().getEventBroadcaster()); - eventProducer.cellOverlap(this, fo.getName(), columnIndex + 1, fo.getLocator()); + eventProducer.cellOverlap(this, propertyList.getFObj().getName(), + i, fo.getLocator()); } } diff --git a/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java b/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java index de74d4be6..6307ad1c5 100644 --- a/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java +++ b/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java @@ -41,7 +41,11 @@ public class BackgroundPositionShorthand extends ListProperty { */ public static class Maker extends ListProperty.Maker { - /** {@inheritDoc} */ + /** + * Construct an instance of a Maker for the given property. + * + * @param propId The Constant ID of the property to be made. + */ public Maker(int propId) { super(propId); } @@ -72,7 +76,7 @@ public class BackgroundPositionShorthand extends ListProperty { * Returns a {@link org.apache.fop.datatypes.PercentBase} whose * <code>getDimension()</code> returns 1. */ - public PercentBase getPercentBase() { + public PercentBase getPercentBase(PropertyList pl) { return new PercentBase() { /** {@inheritDoc} */ public int getBaseLength(PercentBaseContext context) throws PropertyException { diff --git a/src/java/org/apache/fop/fo/properties/CharacterProperty.java b/src/java/org/apache/fop/fo/properties/CharacterProperty.java index 1496f5f86..f42591fe8 100644 --- a/src/java/org/apache/fop/fo/properties/CharacterProperty.java +++ b/src/java/org/apache/fop/fo/properties/CharacterProperty.java @@ -48,7 +48,7 @@ public final class CharacterProperty extends Property { } /** cache containing all canonical CharacterProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(CharacterProperty.class); private final char character; diff --git a/src/java/org/apache/fop/fo/properties/ColorProperty.java b/src/java/org/apache/fop/fo/properties/ColorProperty.java index de648420b..a2a3d2150 100644 --- a/src/java/org/apache/fop/fo/properties/ColorProperty.java +++ b/src/java/org/apache/fop/fo/properties/ColorProperty.java @@ -33,7 +33,7 @@ import org.apache.fop.util.ColorUtil; public final class ColorProperty extends Property { /** cache holding canonical ColorProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(ColorProperty.class); /** * The color represented by this property. @@ -92,6 +92,8 @@ public final class ColorProperty extends Property { * * @param foUserAgent FOP user agent * @param value RGB value as String to be parsed + * @return the canonical ColorProperty instance corresponding + * to the given value * @throws PropertyException if the value can't be parsed * @see ColorUtil#parseColorString(FOUserAgent, String) */ @@ -99,19 +101,10 @@ public final class ColorProperty extends Property { ColorProperty instance = new ColorProperty( ColorUtil.parseColorString( foUserAgent, value)); - return (ColorProperty) cache.fetch(instance); + return (ColorProperty)cache.fetch(instance); } /** - * Returns an instance of a color property given a color - * @param color the color value - * @return the color property - */ - public static ColorProperty getInstance(Color color) { - return (ColorProperty) cache.fetch(new ColorProperty(color)); - } - - /** * Create a new ColorProperty with a given color. * * @param value the color to use. diff --git a/src/java/org/apache/fop/fo/properties/CommonFont.java b/src/java/org/apache/fop/fo/properties/CommonFont.java index a2d01ffa3..1e3f8d43d 100755 --- a/src/java/org/apache/fop/fo/properties/CommonFont.java +++ b/src/java/org/apache/fop/fo/properties/CommonFont.java @@ -37,7 +37,7 @@ 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(); + private static final PropertyCache cache = new PropertyCache(CommonFont.class); /** * Class holding canonical instances of bundles of the @@ -47,7 +47,7 @@ public final class CommonFont { protected static final class CachedCommonFont { /** cache holding all canonical instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(CachedCommonFont.class); private int hash = 0; diff --git a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java index dfafa3b16..a294b2bbd 100644 --- a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java +++ b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java @@ -38,7 +38,7 @@ public final class CommonHyphenation { /** Logger */ protected static Log log = LogFactory.getLog(CommonHyphenation.class); - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(CommonHyphenation.class); private int hash = 0; diff --git a/src/java/org/apache/fop/fo/properties/EnumNumber.java b/src/java/org/apache/fop/fo/properties/EnumNumber.java index 2cfc67f27..5e60b4e35 100755 --- a/src/java/org/apache/fop/fo/properties/EnumNumber.java +++ b/src/java/org/apache/fop/fo/properties/EnumNumber.java @@ -29,7 +29,7 @@ import org.apache.fop.fo.expr.PropertyException; public final class EnumNumber extends Property implements Numeric { /** cache holding all canonical EnumNumber instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(EnumNumber.class); private final EnumProperty enumProperty; diff --git a/src/java/org/apache/fop/fo/properties/EnumProperty.java b/src/java/org/apache/fop/fo/properties/EnumProperty.java index 328ed85a9..d3043c5c3 100644 --- a/src/java/org/apache/fop/fo/properties/EnumProperty.java +++ b/src/java/org/apache/fop/fo/properties/EnumProperty.java @@ -29,7 +29,7 @@ import org.apache.fop.fo.expr.PropertyException; public final class EnumProperty extends Property { /** cache holding all canonical EnumProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(EnumProperty.class); /** * Inner class for creating EnumProperty instances diff --git a/src/java/org/apache/fop/fo/properties/FixedLength.java b/src/java/org/apache/fop/fo/properties/FixedLength.java index 6debdd145..5636a4225 100644 --- a/src/java/org/apache/fop/fo/properties/FixedLength.java +++ b/src/java/org/apache/fop/fo/properties/FixedLength.java @@ -45,7 +45,7 @@ public final class FixedLength extends LengthProperty { public static final String MPT = "mpt"; /** cache holding all canonical FixedLength instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(FixedLength.class); /** canonical zero-length instance */ public static final FixedLength ZERO_FIXED_LENGTH = new FixedLength(0, FixedLength.MPT, 1.0f); diff --git a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java index 610882aa4..7404dbe9b 100644 --- a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java +++ b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java @@ -31,7 +31,7 @@ import org.apache.fop.fo.expr.PropertyException; public final class FontFamilyProperty extends ListProperty { /** cache holding all canonical FontFamilyProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(FontFamilyProperty.class); private int hash = 0; diff --git a/src/java/org/apache/fop/fo/properties/KeepProperty.java b/src/java/org/apache/fop/fo/properties/KeepProperty.java index 2701c3f59..67961b6e5 100644 --- a/src/java/org/apache/fop/fo/properties/KeepProperty.java +++ b/src/java/org/apache/fop/fo/properties/KeepProperty.java @@ -30,7 +30,7 @@ import org.apache.fop.fo.expr.PropertyException; public final class KeepProperty extends Property implements CompoundDatatype { /** class holding all canonical KeepProperty instances*/ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(KeepProperty.class); private boolean isCachedValue = false; private Property withinLine; diff --git a/src/java/org/apache/fop/fo/properties/NumberProperty.java b/src/java/org/apache/fop/fo/properties/NumberProperty.java index 58400d76e..0ba5300ae 100644 --- a/src/java/org/apache/fop/fo/properties/NumberProperty.java +++ b/src/java/org/apache/fop/fo/properties/NumberProperty.java @@ -103,7 +103,7 @@ public final class NumberProperty extends Property implements Numeric { } /** cache holding all canonical NumberProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(NumberProperty.class); private final Number number; diff --git a/src/java/org/apache/fop/fo/properties/PropertyCache.java b/src/java/org/apache/fop/fo/properties/PropertyCache.java index 26ec2e611..2c51bc081 100644 --- a/src/java/org/apache/fop/fo/properties/PropertyCache.java +++ b/src/java/org/apache/fop/fo/properties/PropertyCache.java @@ -42,7 +42,9 @@ public final class PropertyCache { /** the table of hash-buckets */ private CacheEntry[] table = new CacheEntry[8]; - boolean[] votesForRehash = new boolean[SEGMENT_MASK + 1]; + private Class runtimeType; + + final boolean[] votesForRehash = new boolean[SEGMENT_MASK + 1]; /* same hash function as used by java.util.HashMap */ private static int hash(Object x) { @@ -80,10 +82,10 @@ public final class PropertyCache { /* Wrapper objects to synchronize on */ private final class CacheSegment { private int count = 0; - private ReferenceQueue staleEntries = new ReferenceQueue(); + private volatile ReferenceQueue staleEntries = new ReferenceQueue(); } - private final void cleanSegment(int segmentIndex) { + private void cleanSegment(int segmentIndex) { CacheEntry entry; CacheSegment segment = segments[segmentIndex]; int bucketIndex; @@ -113,29 +115,26 @@ public final class PropertyCache { } synchronized (votesForRehash) { if (oldCount > segment.count) { - if (votesForRehash[segmentIndex]) { - votesForRehash[segmentIndex] = false; - } + votesForRehash[segmentIndex] = false; return; - } else { - /* cleanup had no effect */ - if (!votesForRehash[segmentIndex]) { - /* first time for this segment */ - votesForRehash[segmentIndex] = true; - int voteCount = 0; - for (int i = SEGMENT_MASK + 1; --i >= 0; ) { - if (votesForRehash[i]) { - voteCount++; - } + } + /* cleanup had no effect */ + if (!votesForRehash[segmentIndex]) { + /* first time for this segment */ + votesForRehash[segmentIndex] = true; + int voteCount = 0; + for (int i = SEGMENT_MASK + 1; --i >= 0; ) { + if (votesForRehash[i]) { + voteCount++; } - if (voteCount > SEGMENT_MASK / 4) { - rehash(SEGMENT_MASK); - /* reset votes */ - for (int i = SEGMENT_MASK + 1; --i >= 0;) { - votesForRehash[i] = false; - } - + } + if (voteCount > SEGMENT_MASK / 4) { + rehash(SEGMENT_MASK); + /* reset votes */ + for (int i = SEGMENT_MASK + 1; --i >= 0;) { + votesForRehash[i] = false; } + } } } @@ -148,7 +147,7 @@ public final class PropertyCache { * cleanup will be performed to try and remove obsolete * entries. */ - private final void put(Object o) { + private void put(Object o) { int hash = hash(o); CacheSegment segment = segments[hash & SEGMENT_MASK]; @@ -180,7 +179,7 @@ public final class PropertyCache { /* Gets a cached instance. Returns null if not found */ - private final Object get(Object o) { + private Object get(Object o) { int hash = hash(o); int index = hash & (table.length - 1); @@ -219,7 +218,7 @@ public final class PropertyCache { * extends the cache and redistributes the entries. * */ - private final void rehash(int index) { + private void rehash(int index) { CacheSegment seg = segments[index]; synchronized (seg) { @@ -258,12 +257,15 @@ public final class PropertyCache { } /** - * Default constructor. + * Default constructor. + * + * @param c Runtime type of the objects that will be stored in the cache */ - public PropertyCache() { + public PropertyCache(Class c) { for (int i = SEGMENT_MASK + 1; --i >= 0;) { segments[i] = new CacheSegment(); } + this.runtimeType = c; } /** @@ -275,7 +277,7 @@ public final class PropertyCache { * @param obj the Object to check for * @return the cached instance */ - private final Object fetch(Object obj) { + private Object fetch(Object obj) { if (obj == null) { return null; } @@ -339,4 +341,11 @@ public final class PropertyCache { return (CommonFont) fetch((Object) cf); } + + /** {@inheritDoc} */ + public String toString() { + return super.toString() + "[runtimeType=" + this.runtimeType + "]"; + } + + } diff --git a/src/java/org/apache/fop/fo/properties/StringProperty.java b/src/java/org/apache/fop/fo/properties/StringProperty.java index f84a76c83..194170fec 100644 --- a/src/java/org/apache/fop/fo/properties/StringProperty.java +++ b/src/java/org/apache/fop/fo/properties/StringProperty.java @@ -72,13 +72,16 @@ public final class StringProperty extends Property { value = str; } } - return new StringProperty(value); + return StringProperty.getInstance(value); } - } // end String.Maker + } /** cache containing all canonical StringProperty instances */ - private static final PropertyCache cache = new PropertyCache(); + private static final PropertyCache cache = new PropertyCache(StringProperty.class); + + /** canonical instance for empty strings */ + public static final StringProperty EMPTY_STRING_PROPERTY = new StringProperty(""); private final String str; @@ -97,40 +100,38 @@ public final class StringProperty extends Property { * @return the canonical instance */ public static StringProperty getInstance(String str) { - return (StringProperty)cache.fetch( - new StringProperty(str)); + if ("".equals(str) || str == null) { + return EMPTY_STRING_PROPERTY; + } else { + return (StringProperty)cache.fetch( + new StringProperty(str)); + } } - /** - * @return the Object equivalent of this property - */ + /** @return the Object equivalent of this property */ public Object getObject() { return this.str; } - /** - * @return the String equivalent of this property - */ + /** @return the String equivalent of this property */ public String getString() { return this.str; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj instanceof StringProperty) { StringProperty sp = (StringProperty)obj; return (sp.str == this.str || sp.str.equals(this.str)); - } else { - return false; } + return false; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public int hashCode() { return str.hashCode(); } diff --git a/src/java/org/apache/fop/fonts/EmbedFontInfo.java b/src/java/org/apache/fop/fonts/EmbedFontInfo.java index dc40da780..0d0bcafdb 100644 --- a/src/java/org/apache/fop/fonts/EmbedFontInfo.java +++ b/src/java/org/apache/fop/fonts/EmbedFontInfo.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts; +import java.io.IOException; import java.io.Serializable; import java.util.List; @@ -45,6 +46,8 @@ public class EmbedFontInfo implements Serializable { /** the list of associated font triplets */ private List/*<FontTriplet>*/ fontTriplets = null; + private transient boolean embedded = true; + /** * Main constructor * @param metricsFile Path to the xml file containing font metrics @@ -120,11 +123,37 @@ public class EmbedFontInfo implements Serializable { } /** - * {@inheritDoc} + * Indicates whether the font is only referenced rather than embedded. + * @return true if the font is embedded, false if it is referenced. + */ + public boolean isEmbedded() { + if (metricsFile != null && embedFile == null) { + return false; + } else { + return this.embedded; + } + } + + /** + * Defines whether the font is embedded or not. + * @param value true to embed the font, false to reference it */ + public void setEmbedded(boolean value) { + this.embedded = value; + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.embedded = true; + } + + /** {@inheritDoc} */ public String toString() { return "metrics-url=" + metricsFile + ",embed-url=" + embedFile + ", kerning=" + kerning + ", " + "font-triplet=" + fontTriplets - + (getSubFontName() != null ? ", sub-font=" + getSubFontName() : ""); + + (getSubFontName() != null ? ", sub-font=" + getSubFontName() : "") + + (isEmbedded() ? "" : ", NOT embedded"); } + } diff --git a/src/java/org/apache/fop/fonts/FontCache.java b/src/java/org/apache/fop/fonts/FontCache.java index c5b6ab9c7..ba9529c02 100644 --- a/src/java/org/apache/fop/fonts/FontCache.java +++ b/src/java/org/apache/fop/fonts/FontCache.java @@ -151,6 +151,11 @@ public final class FontCache implements Serializable { //We don't really care about the exception since it's just a cache file log.warn("I/O exception while reading font cache (" + ioe.getMessage() + "). Discarding font cache file."); + try { + cacheFile.delete(); + } catch (SecurityException ex) { + log.warn("Failed to delete font cache file: " + cacheFile.getAbsolutePath()); + } } } return null; diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java index 69c55ceae..2699ca6d5 100644 --- a/src/java/org/apache/fop/fonts/FontLoader.java +++ b/src/java/org/apache/fop/fonts/FontLoader.java @@ -39,9 +39,7 @@ import org.apache.fop.fonts.type1.Type1FontLoader; */ public abstract class FontLoader { - /** - * logging instance - */ + /** logging instance */ protected static Log log = LogFactory.getLog(FontLoader.class); /** URI representing the font file */ @@ -53,14 +51,18 @@ public abstract class FontLoader { /** true if the font has been loaded */ protected boolean loaded = false; + /** true if the font will be embedded, false if it will be referenced only. */ + protected boolean embedded = true; /** * Default constructor. * @param fontFileURI the URI to the PFB file of a Type 1 font + * @param embedded indicates whether the font is embedded or referenced * @param resolver the font resolver used to resolve URIs */ - public FontLoader(String fontFileURI, FontResolver resolver) { + public FontLoader(String fontFileURI, boolean embedded, FontResolver resolver) { this.fontFileURI = fontFileURI; + this.embedded = embedded; this.resolver = resolver; } @@ -72,46 +74,48 @@ public abstract class FontLoader { * Loads a custom font from a File. In the case of Type 1 fonts, the PFB file must be specified. * @param fontFile the File representation of the font * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) + * @param embedded indicates whether the font is embedded or referenced * @param resolver the font resolver to use when resolving URIs * @return the newly loaded font * @throws IOException In case of an I/O error */ - public static CustomFont loadFont(File fontFile, String subFontName, FontResolver resolver) - throws IOException { - return loadFont(fontFile.getAbsolutePath(), subFontName, resolver); + public static CustomFont loadFont(File fontFile, String subFontName, + boolean embedded, FontResolver resolver) throws IOException { + return loadFont(fontFile.getAbsolutePath(), subFontName, embedded, resolver); } /** * Loads a custom font from an URL. In the case of Type 1 fonts, the PFB file must be specified. * @param fontUrl the URL representation of the font * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) + * @param embedded indicates whether the font is embedded or referenced * @param resolver the font resolver to use when resolving URIs * @return the newly loaded font * @throws IOException In case of an I/O error */ - public static CustomFont loadFont(URL fontUrl, String subFontName, FontResolver resolver) - throws IOException { - return loadFont(fontUrl.toExternalForm(), subFontName, resolver); + public static CustomFont loadFont(URL fontUrl, String subFontName, + boolean embedded, FontResolver resolver) throws IOException { + return loadFont(fontUrl.toExternalForm(), subFontName, embedded, resolver); } - /** * Loads a custom font from a URI. In the case of Type 1 fonts, the PFB file must be specified. * @param fontFileURI the URI to the font * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) + * @param embedded indicates whether the font is embedded or referenced * @param resolver the font resolver to use when resolving URIs * @return the newly loaded font * @throws IOException In case of an I/O error */ - public static CustomFont loadFont(String fontFileURI, String subFontName, FontResolver resolver) - throws IOException { + public static CustomFont loadFont(String fontFileURI, String subFontName, + boolean embedded, FontResolver resolver) throws IOException { fontFileURI = fontFileURI.trim(); boolean type1 = isType1(fontFileURI); FontLoader loader; if (type1) { - loader = new Type1FontLoader(fontFileURI, resolver); + loader = new Type1FontLoader(fontFileURI, embedded, resolver); } else { - loader = new TTFFontLoader(fontFileURI, subFontName, resolver); + loader = new TTFFontLoader(fontFileURI, subFontName, embedded, resolver); } return loader.getFont(); } diff --git a/src/java/org/apache/fop/fonts/FontManager.java b/src/java/org/apache/fop/fonts/FontManager.java index 8ff85afb4..d25d2d390 100644 --- a/src/java/org/apache/fop/fonts/FontManager.java +++ b/src/java/org/apache/fop/fonts/FontManager.java @@ -25,7 +25,7 @@ import java.net.MalformedURLException; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; -import org.apache.fop.apps.FopFactory; +import org.apache.fop.fonts.FontTriplet.Matcher; import org.apache.fop.fonts.substitute.FontSubstitutions; import org.apache.fop.render.PrintRenderer; @@ -34,7 +34,8 @@ import org.apache.fop.render.PrintRenderer; // and start using POJO config/properties type classes /** - * The manager of fonts + * The manager of fonts. The class holds a reference to the font cache and information about + * font substitution, referenced fonts and similar. */ public class FontManager { /** Use cache (record previously detected font triplet info) */ @@ -49,42 +50,31 @@ public class FontManager { /** Font substitutions */ private FontSubstitutions fontSubstitutions = null; - private FopFactory fopFactory = null; - /** Allows enabling kerning on the base 14 fonts, default is false */ private boolean enableBase14Kerning = false; + /** FontTriplet matcher for fonts that shall be referenced rather than embedded. */ + private FontTriplet.Matcher referencedFontsMatcher; + /** * Main constructor - * - * @param fopFactory the fo URI resolver */ - public FontManager(FopFactory fopFactory) { - this(fopFactory, DEFAULT_USE_CACHE); - } - - /** - * Constructor - * - * @param fopFactory the fo URI resolver - * @param useCache true if the FontCache should be used - */ - public FontManager(FopFactory fopFactory, boolean useCache) { - this.fopFactory = fopFactory; - setUseCache(useCache); + public FontManager() { + setUseCache(DEFAULT_USE_CACHE); } /** * Sets the font base URL. * @param fontBase font base URL - * @throws MalformedURLException if there's a problem with a file URL + * @throws MalformedURLException if there's a problem with a URL */ public void setFontBaseURL(String fontBase) throws MalformedURLException { - this.fontBase = fopFactory.getFOURIResolver().checkBaseURL(fontBase); + this.fontBase = fontBase; } /** - * @return the font base URL + * Returns the font base URL. + * @return the font base URL (or null if none was set) */ public String getFontBaseURL() { return this.fontBase; @@ -218,4 +208,22 @@ public class FontManager { } }; } + + /** + * Sets the {@link FontTriplet.Matcher} that can be used to identify the fonts that shall + * be referenced rather than embedded. + * @param matcher the font triplet matcher + */ + public void setReferencedFontsMatcher(FontTriplet.Matcher matcher) { + this.referencedFontsMatcher = matcher; + } + + /** + * Gets the {@link FontTriplet.Matcher} that can be used to identify the fonts that shall + * be referenced rather than embedded. + * @return the font triplet matcher (or null if none is set) + */ + public Matcher getReferencedFontsMatcher() { + return this.referencedFontsMatcher; + } } diff --git a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java index edee46a0f..d44470e6b 100644 --- a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java +++ b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java @@ -20,11 +20,14 @@ package org.apache.fop.fonts; import java.net.MalformedURLException; +import java.util.List; +import java.util.regex.Pattern; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOPException; import org.apache.fop.fonts.substitute.FontSubstitutions; import org.apache.fop.fonts.substitute.FontSubstitutionsConfigurator; @@ -51,9 +54,11 @@ public class FontManagerConfigurator { /** * Initializes font settings from the user configuration * @param fontManager a font manager - * @throws FOPException fop exception + * @param strict true if strict checking of the configuration is enabled + * @throws FOPException if an exception occurs while processing the configuration */ - public void configure(FontManager fontManager) throws FOPException { + public void configure(FontManager fontManager, boolean strict) throws FOPException { + // caching (fonts) if (cfg.getChild("use-cache", false) != null) { try { @@ -84,6 +89,68 @@ public class FontManagerConfigurator { fontSubstitutionsConfigurator.configure(substitutions); fontManager.setFontSubstitutions(substitutions); } + + // referenced fonts (fonts which are not to be embedded) + Configuration referencedFontsCfg = fontsCfg.getChild("referenced-fonts", false); + if (referencedFontsCfg != null) { + createReferencedFontsMatcher(referencedFontsCfg, strict, fontManager); + } + } - } + } + + private static void createReferencedFontsMatcher(Configuration referencedFontsCfg, + boolean strict, FontManager fontManager) throws FOPException { + List matcherList = new java.util.ArrayList(); + Configuration[] matches = referencedFontsCfg.getChildren("match"); + for (int i = 0; i < matches.length; i++) { + try { + matcherList.add(new FontFamilyRegExFontTripletMatcher( + matches[i].getAttribute("font-family"))); + } catch (ConfigurationException ce) { + LogUtil.handleException(log, ce, strict); + continue; + } + } + FontTriplet.Matcher orMatcher = new OrFontTripletMatcher( + (FontTriplet.Matcher[])matcherList.toArray( + new FontTriplet.Matcher[matcherList.size()])); + fontManager.setReferencedFontsMatcher(orMatcher); + } + + private static class OrFontTripletMatcher implements FontTriplet.Matcher { + + private FontTriplet.Matcher[] matchers; + + public OrFontTripletMatcher(FontTriplet.Matcher[] matchers) { + this.matchers = matchers; + } + + /** {@inheritDoc} */ + public boolean matches(FontTriplet triplet) { + for (int i = 0, c = matchers.length; i < c; i++) { + if (matchers[i].matches(triplet)) { + return true; + } + } + return false; + } + + } + + private static class FontFamilyRegExFontTripletMatcher implements FontTriplet.Matcher { + + private Pattern regex; + + public FontFamilyRegExFontTripletMatcher(String regex) { + this.regex = Pattern.compile(regex); + } + + /** {@inheritDoc} */ + public boolean matches(FontTriplet triplet) { + return regex.matcher(triplet.getName()).matches(); + } + + } + } diff --git a/src/java/org/apache/fop/fonts/FontSetup.java b/src/java/org/apache/fop/fonts/FontSetup.java index ab893d385..9aa8440db 100644 --- a/src/java/org/apache/fop/fonts/FontSetup.java +++ b/src/java/org/apache/fop/fonts/FontSetup.java @@ -27,6 +27,7 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.fonts.base14.Courier; import org.apache.fop.fonts.base14.CourierBold; import org.apache.fop.fonts.base14.CourierBoldOblique; @@ -205,20 +206,12 @@ public class FontSetup { } String internalName = null; - //FontReader reader = null; for (int i = 0; i < embedFontInfoList.size(); i++) { EmbedFontInfo embedFontInfo = (EmbedFontInfo)embedFontInfoList.get(i); - //String metricsFile = configFontInfo.getMetricsFile(); internalName = "F" + num; num++; - /* - reader = new FontReader(metricsFile); - reader.useKerning(configFontInfo.getKerning()); - reader.setFontEmbedPath(configFontInfo.getEmbedFile()); - fontInfo.addMetrics(internalName, reader.getFont()); - */ LazyFont font = new LazyFont(embedFontInfo, resolver); fontInfo.addMetrics(internalName, font); diff --git a/src/java/org/apache/fop/fonts/FontTriplet.java b/src/java/org/apache/fop/fonts/FontTriplet.java index 9091219f5..a989cf0f7 100644 --- a/src/java/org/apache/fop/fonts/FontTriplet.java +++ b/src/java/org/apache/fop/fonts/FontTriplet.java @@ -128,5 +128,20 @@ public class FontTriplet implements Comparable, Serializable { public String toString() { return getKey(); } + + + /** + * Matcher interface for {@link FontTriplet}. + */ + public interface Matcher { + + /** + * Indicates whether the given {@link FontTriplet} matches a particular criterium. + * @param triplet the font triplet + * @return true if the font triplet is a match + */ + boolean matches(FontTriplet triplet); + } + } diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 5490e13f1..b4f7773a3 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -44,6 +44,7 @@ public class LazyFont extends Typeface implements FontDescriptor { private String metricsFileName = null; private String fontEmbedPath = null; private boolean useKerning = false; + private boolean embedded = true; private String subFontName = null; private boolean isMetricsLoaded = false; @@ -63,6 +64,7 @@ public class LazyFont extends Typeface implements FontDescriptor { this.fontEmbedPath = fontInfo.getEmbedFile(); this.useKerning = fontInfo.getKerning(); this.subFontName = fontInfo.getSubFontName(); + this.embedded = fontInfo.isEmbedded(); this.resolver = resolver; } @@ -118,14 +120,17 @@ public class LazyFont extends Typeface implements FontDescriptor { new URL(metricsFileName).openStream())); } reader.setKerningEnabled(useKerning); - reader.setFontEmbedPath(fontEmbedPath); + if (this.embedded) { + reader.setFontEmbedPath(fontEmbedPath); + } reader.setResolver(resolver); realFont = reader.getFont(); } else { if (fontEmbedPath == null) { throw new RuntimeException("Cannot load font. No font URIs available."); } - realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName, resolver); + realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName, + this.embedded, resolver); } if (realFont instanceof FontDescriptor) { realFontDescriptor = (FontDescriptor) realFont; diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java index b2ee4a274..987b7e918 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java @@ -223,7 +223,8 @@ public class FontInfoFinder { log.debug("Loading " + fontName); } try { - TTFFontLoader ttfLoader = new TTFFontLoader(fontFileURI, fontName, resolver); + TTFFontLoader ttfLoader = new TTFFontLoader( + fontFileURI, fontName, true, resolver); customFont = ttfLoader.getFont(); if (this.eventListener != null) { customFont.setEventListener(this.eventListener); @@ -247,7 +248,7 @@ public class FontInfoFinder { } else { // The normal case try { - customFont = FontLoader.loadFont(fontUrl, null, resolver); + customFont = FontLoader.loadFont(fontUrl, null, true, resolver); if (this.eventListener != null) { customFont.setEventListener(this.eventListener); } diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java index 9cd59b5b3..325c46971 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -27,11 +27,16 @@ import java.util.Map; import org.apache.commons.io.IOUtils; +import org.apache.xmlgraphics.fonts.Glyphs; + import org.apache.fop.fonts.BFEntry; import org.apache.fop.fonts.CIDFontType; import org.apache.fop.fonts.FontLoader; import org.apache.fop.fonts.FontResolver; +import org.apache.fop.fonts.FontType; import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.NamedCharacter; +import org.apache.fop.fonts.SingleByteFont; /** * Loads a TrueType font into memory directly from the original font file. @@ -39,6 +44,7 @@ import org.apache.fop.fonts.MultiByteFont; public class TTFFontLoader extends FontLoader { private MultiByteFont multiFont; + private SingleByteFont singleFont; private String subFontName; /** @@ -47,7 +53,7 @@ public class TTFFontLoader extends FontLoader { * @param resolver the FontResolver for font URI resolution */ public TTFFontLoader(String fontFileURI, FontResolver resolver) { - this(fontFileURI, null, resolver); + this(fontFileURI, null, true, resolver); } /** @@ -55,10 +61,12 @@ public class TTFFontLoader extends FontLoader { * @param fontFileURI the URI representing the font file * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal * TrueType fonts) + * @param embedded indicates whether the font is embedded or referenced * @param resolver the FontResolver for font URI resolution */ - public TTFFontLoader(String fontFileURI, String subFontName, FontResolver resolver) { - super(fontFileURI, resolver); + public TTFFontLoader(String fontFileURI, String subFontName, + boolean embedded, FontResolver resolver) { + super(fontFileURI, embedded, resolver); this.subFontName = subFontName; } @@ -95,43 +103,86 @@ public class TTFFontLoader extends FontLoader { throw new UnsupportedOperationException( "OpenType fonts with CFF data are not supported, yet"); } - multiFont = new MultiByteFont(); - multiFont.setResolver(this.resolver); - returnFont = multiFont; + + boolean isCid = this.embedded; + + if (isCid) { + multiFont = new MultiByteFont(); + returnFont = multiFont; + multiFont.setTTCName(ttcFontName); + } else { + singleFont = new SingleByteFont(); + returnFont = singleFont; + } + returnFont.setResolver(resolver); returnFont.setFontName(ttf.getPostScriptName()); returnFont.setFullName(ttf.getFullName()); returnFont.setFamilyNames(ttf.getFamilyNames()); returnFont.setFontSubFamilyName(ttf.getSubFamilyName()); - multiFont.setTTCName(ttcFontName); returnFont.setCapHeight(ttf.getCapHeight()); returnFont.setXHeight(ttf.getXHeight()); returnFont.setAscender(ttf.getLowerCaseAscent()); returnFont.setDescender(ttf.getLowerCaseDescent()); returnFont.setFontBBox(ttf.getFontBBox()); - //returnFont.setFirstChar(ttf.getFirstChar();) returnFont.setFlags(ttf.getFlags()); returnFont.setStemV(Integer.parseInt(ttf.getStemV())); //not used for TTF returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle())); returnFont.setMissingWidth(0); returnFont.setWeight(ttf.getWeightClass()); - - multiFont.setCIDType(CIDFontType.CIDTYPE2); + + if (isCid) { + multiFont.setCIDType(CIDFontType.CIDTYPE2); + int[] wx = ttf.getWidths(); + multiFont.setWidthArray(wx); + List entries = ttf.getCMaps(); + BFEntry[] bfentries = new BFEntry[entries.size()]; + int pos = 0; + Iterator iter = ttf.getCMaps().listIterator(); + while (iter.hasNext()) { + TTFCmapEntry ce = (TTFCmapEntry)iter.next(); + bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(), + ce.getGlyphStartIndex()); + pos++; + } + multiFont.setBFEntries(bfentries); + } else { + singleFont.setFontType(FontType.TRUETYPE); + singleFont.setEncoding(ttf.getCharSetName()); + returnFont.setFirstChar(ttf.getFirstChar()); + returnFont.setLastChar(ttf.getLastChar()); + copyWidthsSingleByte(ttf); + } + + copyKerning(ttf, isCid); + if (this.embedded && ttf.isEmbeddable()) { + multiFont.setEmbedFileName(this.fontFileURI); + } + } + + private void copyWidthsSingleByte(TTFFile ttf) { int[] wx = ttf.getWidths(); - multiFont.setWidthArray(wx); - List entries = ttf.getCMaps(); - BFEntry[] bfentries = new BFEntry[entries.size()]; - int pos = 0; + for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { + singleFont.setWidth(i, ttf.getCharWidth(i)); + } Iterator iter = ttf.getCMaps().listIterator(); while (iter.hasNext()) { TTFCmapEntry ce = (TTFCmapEntry)iter.next(); - bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(), - ce.getGlyphStartIndex()); - pos++; + if (ce.getUnicodeStart() < 0xFFFE) { + for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) { + int codePoint = singleFont.getEncoding().mapChar(u); + if (codePoint <= 0) { + String unicode = Character.toString(u); + String charName = Glyphs.stringToGlyph(unicode); + if (charName.length() > 0) { + NamedCharacter nc = new NamedCharacter(charName, unicode); + int glyphIndex = ce.getGlyphStartIndex() + u - ce.getUnicodeStart(); + singleFont.addUnencodedCharacter(nc, wx[glyphIndex]); + } + } + } + } } - multiFont.setBFEntries(bfentries); - copyKerning(ttf, true); - multiFont.setEmbedFileName(this.fontFileURI); } /** diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java index 3187379d5..ffb572109 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -44,12 +44,13 @@ public class Type1FontLoader extends FontLoader { /** * Constructs a new Type 1 font loader. * @param fontFileURI the URI to the PFB file of a Type 1 font + * @param embedded indicates whether the font is embedded or referenced * @param resolver the font resolver used to resolve URIs * @throws IOException In case of an I/O error */ - public Type1FontLoader(String fontFileURI, FontResolver resolver) + public Type1FontLoader(String fontFileURI, boolean embedded, FontResolver resolver) throws IOException { - super(fontFileURI, resolver); + super(fontFileURI, embedded, resolver); } private String getPFMURI(String pfbURI) { @@ -119,7 +120,9 @@ public class Type1FontLoader extends FontLoader { singleFont = new SingleByteFont(); singleFont.setFontType(FontType.TYPE1); singleFont.setResolver(this.resolver); - singleFont.setEmbedFileName(this.fontFileURI); + if (this.embedded) { + singleFont.setEmbedFileName(this.fontFileURI); + } returnFont = singleFont; handleEncoding(afm, pfm); diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index e9d529ebe..b208e4e9b 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -1527,10 +1527,20 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager protected void wrapPositionElements(List sourceList, List targetList, boolean force) { ListIterator listIter = sourceList.listIterator(); + Object tempElement; while (listIter.hasNext()) { - ListElement tempElement; - tempElement = (ListElement) listIter.next(); - wrapPositionElement(tempElement, targetList, force); + tempElement = listIter.next(); + if (tempElement instanceof ListElement) { + wrapPositionElement( + (ListElement) tempElement, + targetList, + force); + } else if (tempElement instanceof List) { + wrapPositionElements( + (List) tempElement, + targetList, + force); + } } } diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index c54f0ce12..9cd5c622d 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -30,6 +30,7 @@ import org.apache.fop.area.Area; import org.apache.fop.area.BlockParent; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; +import org.apache.fop.layoutmgr.inline.WrapperLayoutManager; /** * LayoutManager for an fo:flow object. @@ -75,7 +76,8 @@ public class FlowLayoutManager extends BlockStackingLayoutManager LinkedList returnList = new LinkedList(); while ((curLM = getChildLM()) != null) { - if (curLM instanceof InlineLevelLayoutManager) { + if (!(curLM instanceof WrapperLayoutManager) + && curLM instanceof InlineLevelLayoutManager) { log.error("inline area not allowed under flow - ignoring"); curLM.setFinished(true); continue; diff --git a/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java index 09e22d481..8108bbf40 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java @@ -20,7 +20,12 @@ package org.apache.fop.layoutmgr.inline; import org.apache.fop.area.inline.InlineArea; +import org.apache.fop.area.inline.InlineParent; +import org.apache.fop.area.Block; +import org.apache.fop.area.LineArea; import org.apache.fop.fo.flow.Wrapper; +import org.apache.fop.layoutmgr.BlockLayoutManager; +import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.TraitSetter; @@ -66,7 +71,19 @@ public class WrapperLayoutManager extends LeafNodeLayoutManager { if (fobj.hasId()) { addId(); InlineArea area = getEffectiveArea(); - parentLM.addChildArea(area); + if (parentLM instanceof BlockStackingLayoutManager + && !(parentLM instanceof BlockLayoutManager)) { + Block helperBlock = new Block(); + LineArea helperLine = new LineArea(); + InlineParent helperInline = new InlineParent(); + helperInline.addChildArea(area); + helperLine.addInlineArea(helperInline); + helperLine.updateExtentsFromChildren(); + helperBlock.addLineArea(helperLine); + parentLM.addChildArea(helperBlock); + } else { + parentLM.addChildArea(area); + } } while (posIter.hasNext()) { posIter.next(); diff --git a/src/java/org/apache/fop/render/PrintRendererConfigurator.java b/src/java/org/apache/fop/render/PrintRendererConfigurator.java index 6273b7050..50d06932d 100644 --- a/src/java/org/apache/fop/render/PrintRendererConfigurator.java +++ b/src/java/org/apache/fop/render/PrintRendererConfigurator.java @@ -95,8 +95,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator FontCache fontCache = fontManager.getFontCache(); List/*<EmbedFontInfo>*/ embedFontInfoList = buildFontListFromConfiguration(cfg, - userAgent.getFontBaseURL(), fontResolver, strict, - fontCache); + fontResolver, strict, fontManager); if (fontCache != null && fontCache.hasChanged()) { fontCache.save(); @@ -108,16 +107,17 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator * Builds a list of EmbedFontInfo objects for use with the setup() method. * * @param cfg Configuration object - * @param fontBaseURL the base URL to resolve relative font URLs with * @param fontResolver the FontResolver to use * @param strict true if an Exception should be thrown if an error is found. - * @param fontCache the font cache (or null if it is disabled) + * @param fontManager the font manager * @return a List of EmbedFontInfo objects. * @throws FOPException If an error occurs while processing the configuration */ public static List/*<EmbedFontInfo>*/ buildFontListFromConfiguration(Configuration cfg, - String fontBaseURL, FontResolver fontResolver, - boolean strict, FontCache fontCache) throws FOPException { + FontResolver fontResolver, + boolean strict, FontManager fontManager) throws FOPException { + FontCache fontCache = fontManager.getFontCache(); + String fontBaseURL = fontManager.getFontBaseURL(); List/*<EmbedFontInfo>*/ fontInfoList = new java.util.ArrayList/*<EmbedFontInfo>*/(); @@ -213,7 +213,10 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator fontInfoList.add(embedFontInfo); } } - + + // Update referenced fonts (fonts which are not to be embedded) + updateReferencedFonts(fontInfoList, fontManager.getReferencedFontsMatcher()); + if (log.isDebugEnabled()) { log.debug("Finished font configuration in " + (System.currentTimeMillis() - start) + "ms"); @@ -222,6 +225,25 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator return fontInfoList; } + private static void updateReferencedFonts(List fontInfoList, FontTriplet.Matcher matcher) { + if (matcher == null) { + return; //No referenced fonts + } + Iterator iter = fontInfoList.iterator(); + while (iter.hasNext()) { + EmbedFontInfo fontInfo = (EmbedFontInfo)iter.next(); + Iterator triplets = fontInfo.getFontTriplets().iterator(); + while (triplets.hasNext()) { + FontTriplet triplet = (FontTriplet)triplets.next(); + if (matcher.matches(triplet)) { + fontInfo.setEmbedded(false); + break; + } + } + } + } + + /** * Iterates over font file list adding font info to list * @param fontFileList font file list diff --git a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java index 771d3f2d7..4981905fa 100644 --- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java +++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java @@ -25,6 +25,7 @@ import javax.xml.transform.Source; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbedFontInfo; import org.apache.fop.fonts.FontCollection; @@ -87,7 +88,7 @@ public class ConfiguredFontCollection implements FontCollection { Source fontSource = resolver.resolve(configFontInfo.getEmbedFile()); font = new CustomFontMetricsMapper(fontMetrics, fontSource); } else { - CustomFont fontMetrics = FontLoader.loadFont(fontFile, null, resolver); + CustomFont fontMetrics = FontLoader.loadFont(fontFile, null, true, resolver); font = new CustomFontMetricsMapper(fontMetrics); } diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index 1566ef799..63b12c5c8 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -191,8 +191,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { } private static boolean isEmbeddable(CustomFont font) { - return font.isEmbeddable() - && (font.getEmbedFileName() != null || font.getEmbedResourceName() != null); + return font.isEmbeddable(); } private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font) diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java index 02e9d6da4..789a7c247 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java @@ -23,8 +23,8 @@ import java.util.List; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; + import org.apache.fop.apps.FOPException; -import org.apache.fop.fonts.FontCache; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontManager; import org.apache.fop.fonts.FontResolver; @@ -55,17 +55,18 @@ public class PDFDocumentGraphics2DConfigurator { //Fonts try { FontResolver fontResolver = FontManager.createMinimalFontResolver(); - //TODO The following could be optimized by retaining the FontCache somewhere - FontCache fontCache = FontCache.load(); - if (fontCache == null) { - fontCache = new FontCache(); - } - //TODO Provide fontBaseURL to this method call - final String fontBaseURL = null; + //TODO The following could be optimized by retaining the FontManager somewhere + FontManager fontManager = new FontManager(); + + //TODO Make use of fontBaseURL, font substitution and referencing configuration + //Requires a change to the expected configuration layout + List/*<EmbedFontInfo>*/ embedFontInfoList = PrintRendererConfigurator.buildFontListFromConfiguration( - cfg, fontBaseURL, fontResolver, false, fontCache); - fontCache.save(); + cfg, fontResolver, false, fontManager); + if (fontManager.useCache()) { + fontManager.getFontCache().save(); + } FontInfo fontInfo = new FontInfo(); FontSetup.setup(fontInfo, embedFontInfoList, fontResolver); graphics.setFontInfo(fontInfo); diff --git a/status.xml b/status.xml index 63a060896..1bb818326 100644 --- a/status.xml +++ b/status.xml @@ -60,10 +60,21 @@ <action context="Renderers" dev="AC" importance="high" type="add"> Added de-duplication and externalization support for IOCA and GOCA data resource objects. </action --> + <action context="Fonts" dev="JM" type="add"> + For auto-detected fonts it is now possible to specify that a font needs to be referenced + rather than embedded (for the output formats that support this distinction). + </action> + <action context="Layout" dev="AD" type="fix" fixes-bug="42423"> + Added support for the "id" attribute on fo:wrappers when used + as a child of the fo:flow. + </action> + <action context="Layout" dev="AD" type="fix" fixes-bug="41500"> + Fixed a ClassCastException when using an fo:wrapper as a child + of an fo:block-container. + </action> <action context="Fonts" dev="AC" type="add"> Add support for font substitution. </action> - <action context="Code" dev="AD" type="fix" fixed-bug="44203"> <action context="Renderers" dev="JM" type="fix" fixes-bug="43650"> PCL Renderer: Improved page format selection so it doesn't interfere with duplex printing. diff --git a/test/basictests.xml b/test/basictests.xml index 7b01d3f41..8f5dfc719 100644 --- a/test/basictests.xml +++ b/test/basictests.xml @@ -1,9 +1,9 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE testsuite SYSTEM "testsuite.dtd"> <testsuite profile="Basic Conformance Tests"> <testcases profile="Example"> <testcases profile="simpleblock" > - <test id="1" interaction="none" fo="block" xml="xml/1.xml" xsl="xsl/doc.xsl" level="basic"> + <test id="xml1" interaction="none" fo="block" xml="xml/1.xml" xsl="xsl/doc.xsl" level="basic"> simple test </test> <testresult id="1" agreement="full"> diff --git a/test/fotree/disabled-testcases.xml b/test/fotree/disabled-testcases.xml index 196eafcc4..0d0a76f6b 100644 --- a/test/fotree/disabled-testcases.xml +++ b/test/fotree/disabled-testcases.xml @@ -28,4 +28,14 @@ <description>The code currently evaluates this function according to the column in which the marker appears in the source document, rather than the column it is retrieved in.</description> </testcase> + <testcase> + <name>column-number_negative-or-zero</name> + <file>column-number_negative-or-zero.fo</file> + <description></description> + </testcase> + <testcase> + <name>column-number_non-integer</name> + <file>column-number_non-integer.fo</file> + <description></description> + </testcase> </disabled-testcases> diff --git a/test/layoutengine/disabled-testcases.xml b/test/layoutengine/disabled-testcases.xml index 12d7ed1aa..c17457a3d 100644 --- a/test/layoutengine/disabled-testcases.xml +++ b/test/layoutengine/disabled-testcases.xml @@ -234,12 +234,6 @@ case should be revisited.</description> </testcase> <testcase> - <name>fo:wrapper around block-level content (with id)</name> - <file>wrapper_block_id.xml</file> - <description>"id" attributes on fo:wrapper around block-level content don't get - added to the area tree.</description> - </testcase> - <testcase> <name>Soft hyphen with normal hyphenation enabled</name> <file>block_shy_linebreaking_hyph.xml</file> <description>A soft hyphen should be a preferred as break compared to a |