diff options
author | Adrian Cumiskey <acumiskey@apache.org> | 2008-04-03 09:40:10 +0000 |
---|---|---|
committer | Adrian Cumiskey <acumiskey@apache.org> | 2008-04-03 09:40:10 +0000 |
commit | 918928b5b464c3ce55af20b608f78a5a70da1b95 (patch) | |
tree | 69507ffadb30383fd6502292174d9df15d855750 /src/java/org/apache | |
parent | cfa5ebf3a6e05964b07c7adc06f68d80c2c911bc (diff) | |
download | xmlgraphics-fop-918928b5b464c3ce55af20b608f78a5a70da1b95.tar.gz xmlgraphics-fop-918928b5b464c3ce55af20b608f78a5a70da1b95.zip |
Merged revisions 644208,644213 via svnmerge from
https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk
........
r644208 | jeremias | 2008-04-03 09:05:14 +0100 (Thu, 03 Apr 2008) | 11 lines
Bugzilla #44737:
Added support for auto-configuring TrueType Collections. XML font metrics files for *.ttc fonts are not required anymore.
Submitted by: Jason Harrop <jason.at.plutext.org>
Changes to patch or in addition to the patch:
- Tab chars removed and Checkstyle issues fixed
- Some simplifications in the cache handling (CachedFontInfo is obsolete and less cache-private information is exposed to the outside).
- TTCs are fully detected and registered with FOP.
- TTCs can also be registered using a "font" element. The new "sub-font" attribute selected the sub-font in the TTC.
- Bug fixed in TTFFile: Font names were not decoded correctly (ex. font names in Chinese)
- Minimal docs.
........
r644213 | jeremias | 2008-04-03 09:13:50 +0100 (Thu, 03 Apr 2008) | 1 line
Oops. Fixed a last-minute change that broke the build.
........
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@644243 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache')
12 files changed, 439 insertions, 210 deletions
diff --git a/src/java/org/apache/fop/fonts/CachedFontInfo.java b/src/java/org/apache/fop/fonts/CachedFontInfo.java deleted file mode 100644 index 3de3cfd4e..000000000 --- a/src/java/org/apache/fop/fonts/CachedFontInfo.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.fonts; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - -import org.apache.commons.io.FileUtils; - -/** - * Font info stored in the cache - */ -public class CachedFontInfo extends EmbedFontInfo { - - /** Serialization Version UID */ - private static final long serialVersionUID = 240028291961081894L; - - /** file modify date (if available) */ - private long lastModified = -1; - - /** - * Returns a file given a list of urls - * @param urls array of possible font urls - * @return file font file - */ - public static File getFileFromUrls(String[] urls) { - for (int i = 0; i < urls.length; i++) { - String urlStr = urls[i]; - if (urlStr != null) { - File fontFile = null; - if (urlStr.startsWith("file:")) { - try { - URL url = new URL(urlStr); - fontFile = FileUtils.toFile(url); - } catch (MalformedURLException mfue) { - // do nothing - } - } - if (fontFile == null) { - fontFile = new File(urlStr); - } - if (fontFile.exists() && fontFile.canRead()) { - return fontFile; - } - } - } - return null; - } - - /** - * Default constructor - * @param metricsFile metrics file - * @param kerning kerning - * @param fontTriplets font triplets - * @param embedFile embed file - * @param lastModified timestamp that this font was last modified - */ - public CachedFontInfo(String metricsFile, boolean kerning, List fontTriplets, - String embedFile, long lastModified) { - super(metricsFile, kerning, fontTriplets, embedFile); - this.lastModified = lastModified; - } - - /** - * Constructor - * @param fontInfo an existing embed font info - */ - public CachedFontInfo(EmbedFontInfo fontInfo) { - super(fontInfo.metricsFile, fontInfo.kerning, fontInfo.fontTriplets, fontInfo.embedFile); - // try and determine modified date - File fontFile = getFileFromUrls(new String[] {embedFile, metricsFile}); - if (fontFile != null ) { - this.lastModified = fontFile.lastModified(); - } - } - - /** - * Gets the modified timestamp for font file (not always available) - * @return modified timestamp - */ - public long lastModified() { - return this.lastModified; - } - - /** - * Gets the modified timestamp for font file - * (used for the purposes of font info caching) - * @param lastModified modified font file timestamp - */ - public void setLastModified(long lastModified) { - this.lastModified = lastModified; - } - - /** - * @return string representation of this object - * {@inheritDoc} - */ - public String toString() { - return super.toString() + ", lastModified=" + lastModified; - } -} diff --git a/src/java/org/apache/fop/fonts/EmbedFontInfo.java b/src/java/org/apache/fop/fonts/EmbedFontInfo.java index 58dcbf142..8bda40532 100644 --- a/src/java/org/apache/fop/fonts/EmbedFontInfo.java +++ b/src/java/org/apache/fop/fonts/EmbedFontInfo.java @@ -28,7 +28,7 @@ import java.util.List; public class EmbedFontInfo implements Serializable { /** Serialization Version UID */ - private static final long serialVersionUID = -9075848379822693399L; + private static final long serialVersionUID = 8755432068669997367L; /** filename of the metrics file */ protected String metricsFile; @@ -38,20 +38,27 @@ public class EmbedFontInfo implements Serializable { protected boolean kerning; /** the list of associated font triplets */ protected List fontTriplets; - + + /** the PostScript name of the font */ + protected String postScriptName = null; + /** the sub-fontname of the font (used for TrueType Collections, null otherwise) */ + protected String subFontName = null; + /** * Main constructor * @param metricsFile Path to the xml file containing font metrics * @param kerning True if kerning should be enabled * @param fontTriplets List of font triplets to associate with this font * @param embedFile Path to the embeddable font file (may be null) + * @param subFontName the sub-fontname used for TrueType Collections (null otherwise) */ public EmbedFontInfo(String metricsFile, boolean kerning, - List fontTriplets, String embedFile) { + List fontTriplets, String embedFile, String subFontName) { this.metricsFile = metricsFile; this.embedFile = embedFile; this.kerning = kerning; this.fontTriplets = fontTriplets; + this.subFontName = subFontName; } /** @@ -86,9 +93,35 @@ public class EmbedFontInfo implements Serializable { return fontTriplets; } + /** + * Returns the sub-fontname name of the font. This is primarily used for TrueType Collections + * to select one of the sub-fonts. For all other fonts, this is always null. + * @return the sub-fontname (or null) + */ + public String getSubFontName() { + return this.subFontName; + } + + /** + * Returns the PostScript name of the font. + * @return the PostScript name + */ + public String getPostScriptName() { + return postScriptName; + } + + /** + * Sets the PostScript name of the font + * @param postScriptName the PostScript name + */ + public void setPostScriptName(String postScriptName) { + this.postScriptName = postScriptName; + } + /** {@inheritDoc} */ public String toString() { return "metrics-url=" + metricsFile + ",embed-url=" + embedFile - + ", kerning=" + kerning + ", font-triplet=" + fontTriplets; + + ", kerning=" + kerning + ", font-triplet=" + fontTriplets + + (getSubFontName() != null ? ", sub-font=" + getSubFontName() : ""); } } diff --git a/src/java/org/apache/fop/fonts/FontCache.java b/src/java/org/apache/fop/fonts/FontCache.java index 13afa5a65..fcf26dafb 100644 --- a/src/java/org/apache/fop/fonts/FontCache.java +++ b/src/java/org/apache/fop/fonts/FontCache.java @@ -26,8 +26,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; import java.util.Map; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -61,10 +65,11 @@ public final class FontCache implements Serializable { /** change lock */ private transient Object changeLock = new Object(); - /** master mapping of font url -> font info */ - private Map fontMap = new java.util.HashMap(); + /** master mapping of font url -> font info. This needs to be + * a list, since a TTC file may contain more than 1 font. */ + private Map fontfileMap = new java.util.HashMap(); //Map<String, CachedFontFile> - /** mapping of font url -> file modified date */ + /** mapping of font url -> file modified date (for all fonts that have failed to load) */ private Map failedFontMap = new java.util.HashMap(); /** @@ -216,7 +221,7 @@ public final class FontCache implements Serializable { */ public boolean containsFont(String embedUrl) { if (embedUrl != null) { - return fontMap.containsKey(embedUrl); + return fontfileMap.containsKey(embedUrl); } return false; } @@ -228,45 +233,100 @@ public final class FontCache implements Serializable { */ public boolean containsFont(EmbedFontInfo fontInfo) { if (fontInfo != null) { - return fontMap.containsKey(getCacheKey(fontInfo)); + return fontfileMap.containsKey(getCacheKey(fontInfo)); } return false; } /** - * adds a font info to cache + * Tries to identify a File instance from an array of URLs. If there's no file URL in the + * array, the method returns null. + * @param urls array of possible font urls + * @return file font file + */ + public static File getFileFromUrls(String[] urls) { + for (int i = 0; i < urls.length; i++) { + String urlStr = urls[i]; + if (urlStr != null) { + File fontFile = null; + if (urlStr.startsWith("file:")) { + try { + URL url = new URL(urlStr); + fontFile = FileUtils.toFile(url); + } catch (MalformedURLException mfue) { + // do nothing + } + } + if (fontFile == null) { + fontFile = new File(urlStr); + } + if (fontFile.exists() && fontFile.canRead()) { + return fontFile; + } + } + } + return null; + } + + /** + * Adds a font info to cache * @param fontInfo font info */ public void addFont(EmbedFontInfo fontInfo) { String cacheKey = getCacheKey(fontInfo); synchronized (changeLock) { - if (!containsFont(cacheKey)) { + CachedFontFile cachedFontFile; + if (containsFont(cacheKey)) { + cachedFontFile = (CachedFontFile)fontfileMap.get(cacheKey); + if (!cachedFontFile.containsFont(fontInfo)) { + cachedFontFile.put(fontInfo); + } + } else { + // try and determine modified date + File fontFile = getFileFromUrls(new String[] + {fontInfo.getEmbedFile(), fontInfo.getMetricsFile()}); + long lastModified = (fontFile != null ? fontFile.lastModified() : -1); + cachedFontFile = new CachedFontFile(lastModified); if (log.isTraceEnabled()) { log.trace("Font added to cache: " + cacheKey); } - if (fontInfo instanceof CachedFontInfo) { - fontMap.put(cacheKey, fontInfo); - } else { - fontMap.put(cacheKey, new CachedFontInfo(fontInfo)); - } + cachedFontFile.put(fontInfo); + fontfileMap.put(cacheKey, cachedFontFile); changed = true; } } } /** - * returns a font from the cache + * Returns a font from the cache. * @param embedUrl font info - * @return boolean + * @return CachedFontFile object */ - public CachedFontInfo getFont(String embedUrl) { + public CachedFontFile getFontFile(String embedUrl) { if (containsFont(embedUrl)) { - return (CachedFontInfo)fontMap.get(embedUrl); + return (CachedFontFile)fontfileMap.get(embedUrl); } return null; } /** + * Returns the EmbedFontInfo instances belonging to a font file. If the font file was + * modified since it was cached the entry is removed and null is returned. + * @param embedUrl the font URL + * @param lastModified the last modified date/time of the font file + * @return the EmbedFontInfo instances or null if there's no cached entry or if it is outdated + */ + public EmbedFontInfo[] getFontInfos(String embedUrl, long lastModified) { + CachedFontFile cff = getFontFile(embedUrl); + if (cff.lastModified() == lastModified) { + return cff.getEmbedFontInfos(); + } else { + removeFont(embedUrl); + return null; + } + } + + /** * removes font from cache * @param embedUrl embed url */ @@ -276,7 +336,7 @@ public final class FontCache implements Serializable { if (log.isTraceEnabled()) { log.trace("Font removed from cache: " + embedUrl); } - fontMap.remove(embedUrl); + fontfileMap.remove(embedUrl); changed = true; } } @@ -326,9 +386,87 @@ public final class FontCache implements Serializable { if (log.isTraceEnabled()) { log.trace("Font cache cleared."); } - fontMap.clear(); + fontfileMap.clear(); failedFontMap.clear(); changed = true; } } + + /** + * Retrieve the last modified date/time of a URL. + * @param url the URL + * @return the last modified date/time + */ + public static long getLastModified(URL url) { + try { + URLConnection conn = url.openConnection(); + try { + return conn.getLastModified(); + } finally { + //An InputStream is created even if it's not accessed, but we need to close it. + IOUtils.closeQuietly(conn.getInputStream()); + } + } catch (IOException e) { + // Should never happen, because URL must be local + log.debug("IOError: " + e.getMessage()); + return 0; + } + } + + private static class CachedFontFile implements Serializable { + /** file modify date (if available) */ + private long lastModified = -1; + + private Map filefontsMap = new java.util.HashMap(); //Map<String, EmbedFontInfo> + + public CachedFontFile(long lastModified) { + setLastModified(lastModified); + } + + void put(EmbedFontInfo efi) { + filefontsMap.put(efi.getPostScriptName(), efi); + } + + public boolean containsFont(EmbedFontInfo efi) { + if (efi.getPostScriptName() != null) { + return filefontsMap.containsKey(efi.getPostScriptName()); + } + return false; + } + + public Map getFilefontsMap() { + return filefontsMap; + } + + public EmbedFontInfo[] getEmbedFontInfos() { + return (EmbedFontInfo[])this.filefontsMap.values().toArray( + new EmbedFontInfo[this.filefontsMap.size()]); + } + + /** + * Gets the modified timestamp for font file (not always available) + * @return modified timestamp + */ + public long lastModified() { + return this.lastModified; + } + + /** + * Gets the modified timestamp for font file + * (used for the purposes of font info caching) + * @param lastModified modified font file timestamp + */ + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + + /** + * @return string representation of this object + * {@inheritDoc} + */ + public String toString() { + return super.toString() + ", lastModified=" + lastModified; + } + + } } diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java index 1fa328a0a..69c55ceae 100644 --- a/src/java/org/apache/fop/fonts/FontLoader.java +++ b/src/java/org/apache/fop/fonts/FontLoader.java @@ -71,36 +71,39 @@ 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 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, FontResolver resolver) + public static CustomFont loadFont(File fontFile, String subFontName, FontResolver resolver) throws IOException { - return loadFont(fontFile.getAbsolutePath(), resolver); + return loadFont(fontFile.getAbsolutePath(), subFontName, 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 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, FontResolver resolver) + public static CustomFont loadFont(URL fontUrl, String subFontName, FontResolver resolver) throws IOException { - return loadFont(fontUrl.toExternalForm(), resolver); + return loadFont(fontUrl.toExternalForm(), subFontName, 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 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, FontResolver resolver) + public static CustomFont loadFont(String fontFileURI, String subFontName, FontResolver resolver) throws IOException { fontFileURI = fontFileURI.trim(); boolean type1 = isType1(fontFileURI); @@ -108,7 +111,7 @@ public abstract class FontLoader { if (type1) { loader = new Type1FontLoader(fontFileURI, resolver); } else { - loader = new TTFFontLoader(fontFileURI, resolver); + loader = new TTFFontLoader(fontFileURI, subFontName, resolver); } return loader.getFont(); } @@ -121,7 +124,7 @@ public abstract class FontLoader { * @throws IOException In case of an I/O error * @throws MalformedURLException If an invalid URL is built */ - protected static InputStream openFontUri(FontResolver resolver, String uri) + public static InputStream openFontUri(FontResolver resolver, String uri) throws IOException, MalformedURLException { InputStream in = null; if (resolver != null) { diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 8997069d4..07b5be305 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 String subFontName = null; private boolean isMetricsLoaded = false; private Typeface realFont = null; @@ -61,6 +62,7 @@ public class LazyFont extends Typeface implements FontDescriptor { this.metricsFileName = fontInfo.getMetricsFile(); this.fontEmbedPath = fontInfo.getEmbedFile(); this.useKerning = fontInfo.getKerning(); + this.subFontName = fontInfo.getSubFontName(); this.resolver = resolver; } @@ -123,7 +125,7 @@ public class LazyFont extends Typeface implements FontDescriptor { if (fontEmbedPath == null) { throw new RuntimeException("Cannot load font. No font URIs available."); } - realFont = FontLoader.loadFont(fontEmbedPath, resolver); + realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName, resolver); } if (realFont instanceof FontDescriptor) { realFontDescriptor = (FontDescriptor) realFont; diff --git a/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java index c35faf65d..d0f7b4f14 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java @@ -72,14 +72,15 @@ public class FontFileFinder extends DirectoryWalker implements FontFinder { } /** - * Font file filter. Currently searches for files with .ttf and .pfb extensions. + * Font file filter. Currently searches for files with .ttf, .ttc, .otf, and .pfb extensions. * @return IOFileFilter font file filter */ protected static IOFileFilter getFileFilter() { return FileFilterUtils.andFileFilter( FileFilterUtils.fileFileFilter(), - new WildcardFileFilter(new String[] {"*.ttf", "*.otf", "*.pfb"}, IOCase.INSENSITIVE) - //TODO Add *.ttc when support for it has been added to the auto-detection mech. + new WildcardFileFilter( + new String[] {"*.ttf", "*.otf", "*.pfb", "*.ttc"}, + IOCase.INSENSITIVE) ); } diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java index 5fc0525c3..8207eb140 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java @@ -19,9 +19,8 @@ package org.apache.fop.fonts.autodetect; -import java.io.IOException; +import java.io.InputStream; import java.net.URL; -import java.net.URLConnection; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -32,7 +31,6 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.fonts.CachedFontInfo; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbedFontInfo; import org.apache.fop.fonts.Font; @@ -41,6 +39,10 @@ import org.apache.fop.fonts.FontLoader; import org.apache.fop.fonts.FontResolver; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.FontUtil; +import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.TTFFile; +import org.apache.fop.fonts.truetype.TTFFontLoader; /** * Attempts to determine correct FontInfo @@ -131,8 +133,13 @@ public class FontInfoFinder { generateTripletsFromFont(customFont, fontTripletList); String embedUrl; embedUrl = fontUrl.toExternalForm(); + String subFontName = null; + if (customFont instanceof MultiByteFont) { + subFontName = ((MultiByteFont)customFont).getTTCName(); + } EmbedFontInfo fontInfo = new EmbedFontInfo(null, customFont.isKerningEnabled(), - fontTripletList, embedUrl); + fontTripletList, embedUrl, subFontName); + fontInfo.setPostScriptName(customFont.getFontName()); if (fontCache != null) { fontCache.addFont(fontInfo); } @@ -145,35 +152,21 @@ public class FontInfoFinder { * @param fontUrl font URL. Assumed to be local. * @param resolver font resolver used to resolve font * @param fontCache font cache (may be null) - * @return newly created embed font info + * @return an array of newly created embed font info. Generally, this array + * will have only one entry, unless the fontUrl is a TrueType Collection */ - public EmbedFontInfo find(URL fontUrl, FontResolver resolver, FontCache fontCache) { + public EmbedFontInfo[] find(URL fontUrl, FontResolver resolver, FontCache fontCache) { String embedUrl = null; embedUrl = fontUrl.toExternalForm(); long fileLastModified = -1; if (fontCache != null) { - try { - URLConnection conn = fontUrl.openConnection(); - try { - fileLastModified = conn.getLastModified(); - } finally { - //An InputStream is created even if it's not accessed, but we need to close it. - IOUtils.closeQuietly(conn.getInputStream()); - } - } catch (IOException e) { - // Should never happen, because URL must be local - log.debug("IOError: " + e.getMessage()); - fileLastModified = 0; - } + fileLastModified = FontCache.getLastModified(fontUrl); // firstly try and fetch it from cache before loading/parsing the font file if (fontCache.containsFont(embedUrl)) { - CachedFontInfo fontInfo = fontCache.getFont(embedUrl); - if (fontInfo.lastModified() == fileLastModified) { - return fontInfo; - } else { - // out of date cache item - fontCache.removeFont(embedUrl); + EmbedFontInfo[] fontInfos = fontCache.getFontInfos(embedUrl, fileLastModified); + if (fontInfos != null) { + return fontInfos; } // is this a previously failed parsed font? } else if (fontCache.isFailedFont(embedUrl, fileLastModified)) { @@ -184,20 +177,81 @@ public class FontInfoFinder { } } + // try to determine triplet information from font file CustomFont customFont = null; - try { - customFont = FontLoader.loadFont(fontUrl, resolver); - } catch (Exception e) { - //TODO Too verbose (it's an error but we don't care if some fonts can't be loaded) - if (log.isErrorEnabled()) { - log.error("Unable to load font file: " + embedUrl + ". Reason: " + e.getMessage()); + + if (fontUrl.toExternalForm().endsWith(".ttc")) { + // Get a list of the TTC Font names + List ttcNames = null; //List<String> + String fontFileURI = fontUrl.toExternalForm().trim(); + TTFFontLoader ttfLoader = new TTFFontLoader(fontFileURI, resolver); + InputStream in = null; + try { + in = FontLoader.openFontUri(resolver, fontFileURI); + TTFFile ttf = new TTFFile(); + FontFileReader reader = new FontFileReader(in); + ttcNames = ttf.getTTCnames(reader); + } catch (Exception e) { + log.error(e); + } finally { + IOUtils.closeQuietly(in); } - if (fontCache != null) { - fontCache.registerFailedFont(embedUrl, fileLastModified); + + List embedFontInfoList = new java.util.ArrayList(); //List<EmbedFontInfo> + + // For each font name ... + //for (String fontName : ttcNames) { + Iterator ttcNamesIterator = ttcNames.iterator(); + while (ttcNamesIterator.hasNext()) { + String fontName = (String)ttcNamesIterator.next(); + + if (log.isDebugEnabled()) { + log.debug("Loading " + fontName); + } + try { + ttfLoader = new TTFFontLoader(fontFileURI, fontName, resolver); + customFont = ttfLoader.getFont(); + } catch (Exception e) { + //TODO Too verbose (it's an error but we don't care if some fonts can't be loaded) + //if (log.isErrorEnabled()) { + log.error("Unable to load font file: " + embedUrl + ". Reason: " + e.getMessage()); + //} + if (fontCache != null) { + fontCache.registerFailedFont(embedUrl, fileLastModified); + } + continue; + } + EmbedFontInfo fi = fontInfoFromCustomFont(fontUrl, customFont, fontCache); + if (fi != null) { + embedFontInfoList.add(fi); + } + } + return (EmbedFontInfo[])embedFontInfoList.toArray( + new EmbedFontInfo[embedFontInfoList.size()]); + } else { + // The normal case + try { + customFont = FontLoader.loadFont(fontUrl, null, resolver); + } catch (Exception e) { + //TODO Too verbose (it's an error but we don't care if some fonts can't be loaded) + //if (log.isErrorEnabled()) { + log.error("Unable to load font file: " + embedUrl + ". Reason: " + e.getMessage()); + //} + if (fontCache != null) { + fontCache.registerFailedFont(embedUrl, fileLastModified); + } + return null; + } + EmbedFontInfo fi = fontInfoFromCustomFont(fontUrl, customFont, fontCache); + if (fi != null) { + return new EmbedFontInfo[] {fi}; + } else { + return null; } - return null; } - return fontInfoFromCustomFont(fontUrl, customFont, fontCache); + + } + } diff --git a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java index 18ac7ad0f..0d1af4785 100644 --- a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java +++ b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java @@ -19,9 +19,9 @@ package org.apache.fop.fonts.truetype; -import java.io.InputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import org.apache.commons.io.IOUtils; @@ -314,6 +314,26 @@ public class FontFileReader { } /** + * Read an ISO-8859-1 string of len bytes. + * + * @param len The length of the string to read + * @return A String + * @throws IOException If EOF is reached + */ + public final String readTTFString(int len, int encodingID) throws IOException { + if ((len + current) > fsize) { + throw new java.io.EOFException("Reached EOF, file size=" + fsize); + } + + byte[] tmp = new byte[len]; + System.arraycopy(file, current, tmp, 0, len); + current += len; + final String encoding; + encoding = "UTF-16BE"; //Use this for all known encoding IDs for now + return new String(tmp, encoding); + } + + /** * Return a copy of the internal array * * @param offset The absolute offset to start reading from diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java index e60722331..8c5211e67 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java @@ -988,7 +988,10 @@ public class TTFFile { if (dirTabs.get("OS/2") != null) { seekTab(in, "OS/2", 2 * 2); this.usWeightClass = in.readTTFUShort(); + + // usWidthClass in.skip(2); + int fsType = in.readTTFUShort(); if (fsType == 2) { isEmbeddable = false; @@ -1123,7 +1126,12 @@ public class TTFFile { if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))) { in.seekSet(j + in.readTTFUShort()); - String txt = in.readTTFString(l); + String txt; + if (platformID == 3) { + txt = in.readTTFString(l, encodingID); + } else { + txt = in.readTTFString(l); + } if (log.isDebugEnabled()) { log.debug(platformID + " " @@ -1147,7 +1155,7 @@ public class TTFFile { } break; case 4: - if (fullName.length() == 0) { + if (fullName.length() == 0 || (platformID == 3 && languageID == 1033)) { fullName = txt; } break; @@ -1474,6 +1482,59 @@ public class TTFFile { } } + /** + * Return TTC font names + * @param in FontFileReader to read from + * @return True if not collection or font name present, false otherwise + * @throws IOException In case of an I/O problem + */ + public final List getTTCnames(FontFileReader in) throws IOException { + List fontNames = new java.util.ArrayList(); + + String tag = in.readTTFString(4); + + if ("ttcf".equals(tag)) { + // This is a TrueType Collection + in.skip(4); + + // Read directory offsets + int numDirectories = (int)in.readTTFULong(); + long[] dirOffsets = new long[numDirectories]; + for (int i = 0; i < numDirectories; i++) { + dirOffsets[i] = in.readTTFULong(); + } + + if (log.isDebugEnabled()) { + log.debug("This is a TrueType collection file with " + + numDirectories + " fonts"); + log.debug("Containing the following fonts: "); + } + + for (int i = 0; (i < numDirectories); i++) { + in.seekSet(dirOffsets[i]); + readDirTabs(in); + + readName(in); + + log.debug(fullName); + fontNames.add(fullName); + + // Reset names + notice = ""; + fullName = ""; + familyNames.clear(); + postScriptName = ""; + subFamilyName = ""; + } + + in.seekSet(0); + return fontNames; + } else { + log.error("Not a TTC!"); + return null; + } + } + /* * Helper classes, they are not very efficient, but that really * doesn't matter... @@ -1536,8 +1597,8 @@ public class TTFFile { * @throws IOException if unicodeIndex not found */ private Integer unicodeToGlyph(int unicodeIndex) throws IOException { - final Integer result = - (Integer) unicodeToGlyphMap.get(new Integer(unicodeIndex)); + final Integer result + = (Integer) unicodeToGlyphMap.get(new Integer(unicodeIndex)); if (result == null) { throw new IOException( "Glyph index not found for unicode value " + unicodeIndex); diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java index 60a6948fc..9cd59b5b3 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -34,11 +34,12 @@ import org.apache.fop.fonts.FontResolver; import org.apache.fop.fonts.MultiByteFont; /** - * Loads a font into memory directly from the original font file. + * Loads a TrueType font into memory directly from the original font file. */ public class TTFFontLoader extends FontLoader { private MultiByteFont multiFont; + private String subFontName; /** * Default constructor @@ -46,27 +47,50 @@ public class TTFFontLoader extends FontLoader { * @param resolver the FontResolver for font URI resolution */ public TTFFontLoader(String fontFileURI, FontResolver resolver) { + this(fontFileURI, null, resolver); + } + + /** + * Additional constructor for TrueType Collections. + * @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 resolver the FontResolver for font URI resolution + */ + public TTFFontLoader(String fontFileURI, String subFontName, FontResolver resolver) { super(fontFileURI, resolver); + this.subFontName = subFontName; } /** {@inheritDoc} */ protected void read() throws IOException { + read(this.subFontName); + } + + /** + * Reads a TrueType font. + * @param ttcFontName the TrueType sub-font name of TrueType Collection (may be null for + * normal TrueType fonts) + * @throws IOException if an I/O error occurs + */ + private void read(String ttcFontName) throws IOException { InputStream in = openFontUri(resolver, this.fontFileURI); try { TTFFile ttf = new TTFFile(); FontFileReader reader = new FontFileReader(in); - boolean supported = ttf.readFont(reader, null); + boolean supported = ttf.readFont(reader, ttcFontName); if (!supported) { throw new IOException("TrueType font is not supported: " + fontFileURI); } - buildFont(ttf); + buildFont(ttf, ttcFontName); loaded = true; } finally { IOUtils.closeQuietly(in); } } - - private void buildFont(TTFFile ttf) { + + + private void buildFont(TTFFile ttf, String ttcFontName) { if (ttf.isCFF()) { throw new UnsupportedOperationException( "OpenType fonts with CFF data are not supported, yet"); @@ -79,7 +103,7 @@ public class TTFFontLoader extends FontLoader { returnFont.setFullName(ttf.getFullName()); returnFont.setFamilyNames(ttf.getFamilyNames()); returnFont.setFontSubFamilyName(ttf.getSubFamilyName()); - //multiFont.setTTCName(ttcName) + multiFont.setTTCName(ttcFontName); returnFont.setCapHeight(ttf.getCapHeight()); returnFont.setXHeight(ttf.getXHeight()); returnFont.setAscender(ttf.getLowerCaseAscent()); @@ -91,7 +115,7 @@ public class TTFFontLoader extends FontLoader { returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle())); returnFont.setMissingWidth(0); returnFont.setWeight(ttf.getWeightClass()); - + multiFont.setCIDType(CIDFontType.CIDTYPE2); int[] wx = ttf.getWidths(); multiFont.setWidthArray(wx); diff --git a/src/java/org/apache/fop/render/PrintRendererConfigurator.java b/src/java/org/apache/fop/render/PrintRendererConfigurator.java index 0f0acd60d..ccff3baa1 100644 --- a/src/java/org/apache/fop/render/PrintRendererConfigurator.java +++ b/src/java/org/apache/fop/render/PrintRendererConfigurator.java @@ -36,10 +36,11 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.util.ClasspathResource; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; -import org.apache.fop.fonts.CachedFontInfo; import org.apache.fop.fonts.EmbedFontInfo; import org.apache.fop.fonts.FontCache; import org.apache.fop.fonts.FontInfo; @@ -50,7 +51,6 @@ import org.apache.fop.fonts.FontUtil; import org.apache.fop.fonts.autodetect.FontFileFinder; import org.apache.fop.fonts.autodetect.FontInfoFinder; import org.apache.fop.util.LogUtil; -import org.apache.xmlgraphics.util.ClasspathResource; /** * Base Print renderer configurator (mostly handles font configuration) @@ -230,9 +230,20 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator URL fontUrl = (URL)iter.next(); // parse font to ascertain font info FontInfoFinder finder = new FontInfoFinder(); - EmbedFontInfo fontInfo = finder.find(fontUrl, resolver, fontCache); - if (fontInfo != null) { - fontInfoList.add(fontInfo); + //EmbedFontInfo fontInfo = finder.find(fontUrl, resolver, fontCache); + + //List<EmbedFontInfo> embedFontInfoList = finder.find(fontUrl, resolver, fontCache); + EmbedFontInfo[] embedFontInfos = finder.find(fontUrl, resolver, fontCache); + + if (embedFontInfos == null) { + return; + } + + for (int i = 0, c = embedFontInfos.length; i < c; i++) { + EmbedFontInfo fontInfo = embedFontInfos[i]; + if (fontInfo != null) { + fontInfoList.add(fontInfo); + } } } } @@ -257,9 +268,10 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator */ public static EmbedFontInfo getFontInfoFromConfiguration( Configuration fontCfg, FontResolver fontResolver, boolean strict, FontCache fontCache) - throws FOPException { + throws FOPException { String metricsUrl = fontCfg.getAttribute("metrics-url", null); String embedUrl = fontCfg.getAttribute("embed-url", null); + String subFont = fontCfg.getAttribute("sub-font", null); if (metricsUrl == null && embedUrl == null) { LogUtil.handleError(log, "Font configuration without metric-url or embed-url", strict); @@ -296,7 +308,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator LogUtil.handleError(log, "font without font-triplet", strict); // if not strict try to determine font info from the embed/metrics url - File fontFile = CachedFontInfo.getFileFromUrls(new String[] {embedUrl, metricsUrl}); + File fontFile = FontCache.getFileFromUrls(new String[] {embedUrl, metricsUrl}); URL fontUrl; try { fontUrl = fontFile.toURI().toURL(); @@ -307,7 +319,8 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator } if (fontFile != null) { FontInfoFinder finder = new FontInfoFinder(); - return finder.find(fontUrl, fontResolver, fontCache); + EmbedFontInfo[] infos = finder.find(fontUrl, fontResolver, fontCache); + return infos[0]; //When subFont is set, only one font is returned } else { return null; } @@ -337,7 +350,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator } } - fontInfo = new EmbedFontInfo(metricsUrl, useKerning, tripleList, embedUrl); + fontInfo = new EmbedFontInfo(metricsUrl, useKerning, tripleList, embedUrl, subFont); if (fontCache != null) { if (!fontCache.containsFont(fontInfo)) { diff --git a/src/java/org/apache/fop/render/java2d/FontSetup.java b/src/java/org/apache/fop/render/java2d/FontSetup.java index 752335521..ade457d95 100644 --- a/src/java/org/apache/fop/render/java2d/FontSetup.java +++ b/src/java/org/apache/fop/render/java2d/FontSetup.java @@ -304,7 +304,7 @@ public class FontSetup { Source fontSource = resolver.resolve(configFontInfo.getEmbedFile()); font = new CustomFontMetricsMapper(fontMetrics, fontSource); } else { - CustomFont fontMetrics = FontLoader.loadFont(fontFile, resolver); + CustomFont fontMetrics = FontLoader.loadFont(fontFile, null, resolver); font = new CustomFontMetricsMapper(fontMetrics); } |