/* * 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.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.apps.FOPException; import org.apache.fop.configuration.Configuration; import org.apache.fop.configuration.ConfigurationException; import org.apache.fop.events.EventProducer; import org.apache.fop.util.LogUtil; /** * The font configuration data for the more generic fonts such as TTF and Type1, that are used by * most the renderers. */ public final class DefaultFontConfig implements FontConfig { private static final Log log = LogFactory.getLog(DefaultFontConfig.class); private final List directories = new ArrayList(); private final List fonts = new ArrayList(); private final List referencedFontFamilies = new ArrayList(); private final boolean autoDetectFonts; private DefaultFontConfig(boolean autoDetectFonts) { this.autoDetectFonts = autoDetectFonts; } /** * Parses the morge generic font information. */ public static final class DefaultFontConfigParser implements FontConfig.FontConfigParser { /** * Parses the font configuration and return the configuration object. * * @param cfg the configuration data * @param strict whether or not to enforce strict validation * @return the font configuration object * @throws FOPException if an error occurs when creating the configuration object */ public DefaultFontConfig parse(Configuration cfg, boolean strict) throws FOPException { return new ParserHelper(cfg, strict).instance; } /** {@inheritDoc} */ public DefaultFontConfig parse(Configuration cfg, boolean strict, FontEventAdapter eventAdapter) throws FOPException { return new ParserHelper(cfg, strict, eventAdapter).instance; } /** {@inheritDoc} */ public FontConfig parse(Configuration cfg, FontManager fontManager, boolean strict, EventProducer eventProducer) throws FOPException { return parse(cfg, strict); } } private static final class ParserHelper { private boolean strict; private Configuration config; private Configuration fontInfoCfg; private FontEventAdapter eventAdapter; private DefaultFontConfig instance; private ParserHelper(Configuration cfg, boolean strict) throws FOPException { this(cfg, strict, null); } private ParserHelper(Configuration cfg, boolean strict, FontEventAdapter eventAdapter) throws FOPException { this.eventAdapter = eventAdapter; if (cfg == null || cfg.getChild("fonts", false) == null) { instance = null; } else { this.strict = strict; this.config = cfg; this.fontInfoCfg = cfg.getChild("fonts", false); instance = new DefaultFontConfig(fontInfoCfg.getChild("auto-detect", false) != null); parse(); } } private void parse() throws FOPException { parseFonts(); parseReferencedFonts(); parseDirectories(); } private void parseFonts() throws FOPException { for (Configuration fontCfg : fontInfoCfg.getChildren("font")) { String embed = fontCfg.getAttribute("embed-url", null); if (embed == null) { LogUtil.handleError(log, "Font configuration without embed-url attribute", strict); continue; } Font font = new Font(fontCfg.getAttribute("metrics-url", null), embed, fontCfg.getAttribute("embed-url-afm", null), fontCfg.getAttribute("embed-url-pfm", null), fontCfg.getAttribute("sub-font", null), fontCfg.getAttributeAsBoolean("kerning", true), fontCfg.getAttributeAsBoolean("advanced", true), fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()), fontCfg.getAttribute("embedding-mode", EncodingMode.AUTO.getName()), fontCfg.getAttributeAsBoolean("simulate-style", false), fontCfg.getAttributeAsBoolean("embed-as-type1", false)); instance.fonts.add(font); boolean hasTriplets = false; for (Configuration tripletCfg : fontCfg.getChildren("font-triplet")) { FontTriplet fontTriplet = getFontTriplet(tripletCfg, strict); font.tripletList.add(fontTriplet); hasTriplets = true; } // no font triplet info if (!hasTriplets) { LogUtil.handleError(log, "font without font-triplet", strict); } try { if (eventAdapter != null && font.getSimulateStyle() && !config.getAttribute("mime").equals("application/pdf")) { eventAdapter.fontFeatureNotSuppprted(this, "simulate-style", "PDF"); } if (eventAdapter != null && font.getEmbedAsType1() && !config.getAttribute("mime").equals("application/postscript")) { throw new FOPException("The embed-as-type1 attribute is only supported in postscript"); } } catch (ConfigurationException ex) { LogUtil.handleException(log, ex, true); } } } private void parseReferencedFonts() throws FOPException { Configuration referencedFontsCfg = fontInfoCfg.getChild("referenced-fonts", false); if (referencedFontsCfg != null) { for (Configuration match : referencedFontsCfg.getChildren("match")) { try { instance.referencedFontFamilies.add(match.getAttribute("font-family")); } catch (ConfigurationException ce) { LogUtil.handleException(log, ce, strict); continue; } } } } private void parseDirectories() throws FOPException { for (Configuration directoriesCfg : fontInfoCfg.getChildren("directory")) { boolean recursive = directoriesCfg.getAttributeAsBoolean("recursive", false); String directory; try { directory = directoriesCfg.getValue(); } catch (ConfigurationException e) { LogUtil.handleException(log, e, strict); continue; } if (directory == null) { LogUtil.handleException(log, new FOPException("directory defined without value"), strict); continue; } instance.directories.add(new Directory(directory, recursive)); } } /** * Creates a new FontTriplet given a triple Configuration * * @param tripletCfg a triplet configuration * @return a font triplet font key * @throws FOPException thrown if a FOP exception occurs */ private FontTriplet getFontTriplet(Configuration tripletCfg, boolean strict) throws FOPException { try { String name = tripletCfg.getAttribute("name"); if (name == null) { LogUtil.handleError(log, "font-triplet without name", strict); return null; } String weightStr = tripletCfg.getAttribute("weight"); if (weightStr == null) { LogUtil.handleError(log, "font-triplet without weight", strict); return null; } int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr)); String style = tripletCfg.getAttribute("style"); if (style == null) { LogUtil.handleError(log, "font-triplet without style", strict); return null; } else { style = FontUtil.stripWhiteSpace(style); } return FontInfo.createFontKey(name, style, weight); } catch (ConfigurationException e) { LogUtil.handleException(log, e, strict); } return null; } } /** * Returns the list of fonts that were parsed. * @return a list of fonts */ public List getFonts() { return Collections.unmodifiableList(fonts); } /** * Returns a list of directories that were parsed. * @return a list of directories */ public List getDirectories() { return Collections.unmodifiableList(directories); } /** * Returns a list of referenced font families. * @return the referenced font families */ public List getReferencedFontFamily() { return Collections.unmodifiableList(referencedFontFamilies); } /** * Whether or not to enable auto-detecting of fonts in the system. * @return true to enable auto-detect */ public boolean isAutoDetectFonts() { return autoDetectFonts; } /** * The directory to find fonts within. */ public static final class Directory { private final String directory; private final boolean recursive; private Directory(String directory, boolean recurse) { this.directory = directory; this.recursive = recurse; } /** * Returns a String representing the directory to find fonts within. * @return the directory */ public String getDirectory() { return directory; } /** * Returns whether or not to recurse through the directory when finding fonts. * @return true to recurse through the directory and sub-directories */ public boolean isRecursive() { return recursive; } } /** * Represents a font object within the FOP conf. */ public static final class Font { private final String metrics; private final String embedUri; private String afm; private String pfm; private final String subFont; private final boolean kerning; private final boolean advanced; private final String encodingMode; private final String embeddingMode; public String getEncodingMode() { return encodingMode; } private final boolean embedAsType1; private final boolean simulateStyle; private final List tripletList = new ArrayList(); public List getTripletList() { return Collections.unmodifiableList(tripletList); } private Font(String metrics, String embed, String afm, String pfm, String subFont, boolean kerning, boolean advanced, String encodingMode, String embeddingMode, boolean simulateStyle, boolean embedAsType1) { this.metrics = metrics; this.embedUri = embed; this.afm = afm; this.pfm = pfm; this.subFont = subFont; this.kerning = kerning; this.advanced = advanced; this.encodingMode = encodingMode; this.embeddingMode = embeddingMode; this.simulateStyle = simulateStyle; this.embedAsType1 = embedAsType1; } /** * Whether or not to allow kerning of glyphs. * @return true to allow glyph kerning */ public boolean isKerning() { return kerning; } public boolean isAdvanced() { return advanced; } /** * Gets the String representing the metrics file. * @return the metrics file */ public String getMetrics() { return metrics; } /** * Gets the URI of the font to embed. * @return the font URI */ public String getEmbedURI() { return embedUri; } /** * Gets the sub font within, for example, a TTC. * @return the sub font name */ public String getSubFont() { return subFont; } public String getEmbeddingMode() { return embeddingMode; } public String getAfm() { return afm; } public String getPfm() { return pfm; } public boolean getSimulateStyle() { return simulateStyle; } public boolean getEmbedAsType1() { return embedAsType1; } } }