| /* |
| * 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.autodetect; |
| |
| import java.io.InputStream; |
| import java.net.URI; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import org.apache.fop.apps.io.InternalResourceResolver; |
| import org.apache.fop.fonts.CustomFont; |
| import org.apache.fop.fonts.EmbedFontInfo; |
| import org.apache.fop.fonts.EmbeddingMode; |
| import org.apache.fop.fonts.EncodingMode; |
| import org.apache.fop.fonts.Font; |
| import org.apache.fop.fonts.FontCache; |
| import org.apache.fop.fonts.FontEventListener; |
| import org.apache.fop.fonts.FontLoader; |
| import org.apache.fop.fonts.FontTriplet; |
| import org.apache.fop.fonts.FontUris; |
| 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.OFFontLoader; |
| import org.apache.fop.fonts.truetype.TTFFile; |
| |
| /** |
| * Attempts to determine correct FontInfo |
| */ |
| public class FontInfoFinder { |
| |
| /** logging instance */ |
| private final Log log = LogFactory.getLog(FontInfoFinder.class); |
| |
| private FontEventListener eventListener; |
| |
| /** |
| * Sets the font event listener that can be used to receive events about particular events |
| * in this class. |
| * @param listener the font event listener |
| */ |
| public void setEventListener(FontEventListener listener) { |
| this.eventListener = listener; |
| } |
| |
| /** |
| * Attempts to determine FontTriplets from a given CustomFont. |
| * It seems to be fairly accurate but will probably require some tweaking over time |
| * |
| * @param customFont CustomFont |
| * @param triplets Collection that will take the generated triplets |
| */ |
| private void generateTripletsFromFont(CustomFont customFont, Collection<FontTriplet> triplets) { |
| if (log.isTraceEnabled()) { |
| log.trace("Font: " + customFont.getFullName() |
| + ", family: " + customFont.getFamilyNames() |
| + ", PS: " + customFont.getFontName() |
| + ", EmbedName: " + customFont.getEmbedFontName()); |
| } |
| |
| // default style and weight triplet vales (fallback) |
| String strippedName = stripQuotes(customFont.getStrippedFontName()); |
| //String subName = customFont.getFontSubName(); |
| String fullName = stripQuotes(customFont.getFullName()); |
| String searchName = fullName.toLowerCase(); |
| |
| String style = guessStyle(customFont, searchName); |
| int weight; //= customFont.getWeight(); |
| int guessedWeight = FontUtil.guessWeight(searchName); |
| //We always take the guessed weight for now since it yield much better results. |
| //OpenType's OS/2 usWeightClass value proves to be unreliable. |
| weight = guessedWeight; |
| |
| //Full Name usually includes style/weight info so don't use these traits |
| //If we still want to use these traits, we have to make FontInfo.fontLookup() smarter |
| triplets.add(new FontTriplet(fullName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); |
| if (!fullName.equals(strippedName)) { |
| triplets.add(new FontTriplet(strippedName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); |
| } |
| Set<String> familyNames = customFont.getFamilyNames(); |
| for (String familyName : familyNames) { |
| familyName = stripQuotes(familyName); |
| if (!fullName.equals(familyName)) { |
| /* Heuristic: |
| * The more similar the family name to the full font name, |
| * the higher the priority of its triplet. |
| * (Lower values indicate higher priorities.) */ |
| int priority = fullName.startsWith(familyName) |
| ? fullName.length() - familyName.length() |
| : fullName.length(); |
| triplets.add(new FontTriplet(familyName, style, weight, priority)); |
| } |
| } |
| } |
| |
| private final Pattern quotePattern = Pattern.compile("'"); |
| |
| private String stripQuotes(String name) { |
| return quotePattern.matcher(name).replaceAll(""); |
| } |
| |
| private String guessStyle(CustomFont customFont, String fontName) { |
| // style |
| String style = Font.STYLE_NORMAL; |
| if (customFont.getItalicAngle() > 0) { |
| style = Font.STYLE_ITALIC; |
| } else { |
| style = FontUtil.guessStyle(fontName); |
| } |
| return style; |
| } |
| |
| /** |
| * Attempts to determine FontInfo from a given custom font |
| * @param fontUri the font URI |
| * @param customFont the custom font |
| * @param fontCache font cache (may be null) |
| * @return FontInfo from the given custom font |
| */ |
| private EmbedFontInfo getFontInfoFromCustomFont(URI fontUri, CustomFont customFont, |
| FontCache fontCache, InternalResourceResolver resourceResolver) { |
| FontUris fontUris = new FontUris(fontUri, null); |
| List<FontTriplet> fontTripletList = new java.util.ArrayList<FontTriplet>(); |
| generateTripletsFromFont(customFont, fontTripletList); |
| String subFontName = null; |
| if (customFont instanceof MultiByteFont) { |
| subFontName = ((MultiByteFont) customFont).getTTCName(); |
| } |
| EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, customFont.isKerningEnabled(), |
| customFont.isAdvancedEnabled(), fontTripletList, subFontName); |
| fontInfo.setPostScriptName(customFont.getFontName()); |
| if (fontCache != null) { |
| fontCache.addFont(fontInfo, resourceResolver); |
| } |
| return fontInfo; |
| } |
| |
| /** |
| * Attempts to determine EmbedFontInfo from a given font file. |
| * |
| * @param fontURI the URI of the font resource |
| * @param resourceResolver font resolver used to resolve font |
| * @param fontCache font cache (may be null) |
| * @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(URI fontURI, InternalResourceResolver resourceResolver, FontCache fontCache) { |
| URI embedUri = resourceResolver.resolveFromBase(fontURI); |
| String embedStr = embedUri.toASCIIString(); |
| boolean useKerning = true; |
| boolean useAdvanced = true; |
| |
| long fileLastModified = -1; |
| if (fontCache != null) { |
| fileLastModified = FontCache.getLastModified(fontURI); |
| // firstly try and fetch it from cache before loading/parsing the font file |
| if (fontCache.containsFont(embedStr)) { |
| EmbedFontInfo[] fontInfos = fontCache.getFontInfos(embedStr, fileLastModified); |
| if (fontInfos != null) { |
| return fontInfos; |
| } |
| // is this a previously failed parsed font? |
| } else if (fontCache.isFailedFont(embedStr, fileLastModified)) { |
| if (log.isDebugEnabled()) { |
| log.debug("Skipping font file that failed to load previously: " + embedUri); |
| } |
| return null; |
| } |
| } |
| |
| |
| // try to determine triplet information from font file |
| CustomFont customFont = null; |
| if (fontURI.toASCIIString().toLowerCase().endsWith(".ttc")) { |
| // Get a list of the TTC Font names |
| List<String> ttcNames = null; |
| InputStream in = null; |
| try { |
| in = resourceResolver.getResource(fontURI); |
| TTFFile ttf = new TTFFile(false, false); |
| FontFileReader reader = new FontFileReader(in); |
| ttcNames = ttf.getTTCnames(reader); |
| } catch (Exception e) { |
| if (this.eventListener != null) { |
| this.eventListener.fontLoadingErrorAtAutoDetection(this, |
| fontURI.toASCIIString(), e); |
| } |
| return null; |
| } finally { |
| IOUtils.closeQuietly(in); |
| } |
| |
| List<EmbedFontInfo> embedFontInfoList = new java.util.ArrayList<EmbedFontInfo>(); |
| |
| // For each font name ... |
| for (String fontName : ttcNames) { |
| if (log.isDebugEnabled()) { |
| log.debug("Loading " + fontName); |
| } |
| try { |
| OFFontLoader ttfLoader = new OFFontLoader(fontURI, fontName, true, |
| EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useAdvanced, |
| resourceResolver, false, false); |
| customFont = ttfLoader.getFont(); |
| if (this.eventListener != null) { |
| customFont.setEventListener(this.eventListener); |
| } |
| } catch (Exception e) { |
| if (fontCache != null) { |
| fontCache.registerFailedFont(embedUri.toASCIIString(), fileLastModified); |
| } |
| if (this.eventListener != null) { |
| this.eventListener.fontLoadingErrorAtAutoDetection(this, |
| embedUri.toASCIIString(), e); |
| } |
| continue; |
| } |
| EmbedFontInfo fi = getFontInfoFromCustomFont(fontURI, customFont, fontCache, |
| resourceResolver); |
| if (fi != null) { |
| embedFontInfoList.add(fi); |
| } |
| } |
| return embedFontInfoList.toArray( |
| new EmbedFontInfo[embedFontInfoList.size()]); |
| } else { |
| // The normal case |
| try { |
| FontUris fontUris = new FontUris(fontURI, null); |
| customFont = FontLoader.loadFont(fontUris, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, |
| useKerning, useAdvanced, resourceResolver, false, false); |
| if (this.eventListener != null) { |
| customFont.setEventListener(this.eventListener); |
| } |
| } catch (Exception e) { |
| if (fontCache != null) { |
| fontCache.registerFailedFont(embedUri.toASCIIString(), fileLastModified); |
| } |
| if (this.eventListener != null) { |
| this.eventListener.fontLoadingErrorAtAutoDetection(this, |
| embedUri.toASCIIString(), e); |
| } |
| return null; |
| } |
| EmbedFontInfo fi = getFontInfoFromCustomFont(fontURI, customFont, fontCache, resourceResolver); |
| if (fi != null) { |
| return new EmbedFontInfo[] {fi}; |
| } else { |
| return null; |
| } |
| } |
| |
| } |
| |
| } |