| /* |
| * 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.render.afp; |
| |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| 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.afp.AFPEventProducer; |
| import org.apache.fop.afp.fonts.AFPFont; |
| import org.apache.fop.afp.fonts.AFPFontInfo; |
| import org.apache.fop.afp.fonts.CharacterSet; |
| import org.apache.fop.afp.fonts.CharacterSetBuilder; |
| import org.apache.fop.afp.fonts.CharacterSetType; |
| import org.apache.fop.afp.fonts.DoubleByteFont; |
| import org.apache.fop.afp.fonts.OutlineFont; |
| import org.apache.fop.afp.fonts.RasterFont; |
| import org.apache.fop.afp.util.AFPResourceAccessor; |
| import org.apache.fop.apps.FOPException; |
| import org.apache.fop.apps.io.InternalResourceResolver; |
| import org.apache.fop.configuration.Configuration; |
| import org.apache.fop.configuration.ConfigurationException; |
| import org.apache.fop.events.EventProducer; |
| import org.apache.fop.fonts.EmbedFontInfo; |
| import org.apache.fop.fonts.EmbeddingMode; |
| import org.apache.fop.fonts.EncodingMode; |
| import org.apache.fop.fonts.FontConfig; |
| import org.apache.fop.fonts.FontManager; |
| import org.apache.fop.fonts.FontManagerConfigurator; |
| import org.apache.fop.fonts.FontTriplet; |
| import org.apache.fop.fonts.FontTriplet.Matcher; |
| import org.apache.fop.fonts.FontType; |
| import org.apache.fop.fonts.FontUris; |
| import org.apache.fop.fonts.FontUtil; |
| import org.apache.fop.fonts.LazyFont; |
| import org.apache.fop.fonts.Typeface; |
| |
| /** |
| * The config object for AFP fonts, these differ from the the more generic fonts (TTF and Type1). |
| */ |
| public final class AFPFontConfig implements FontConfig { |
| |
| private static final Log LOG = LogFactory.getLog(AFPFontConfig.class); |
| |
| private final List<AFPFontConfigData> fontsConfig; |
| |
| private AFPFontConfig() { |
| fontsConfig = new ArrayList<AFPFontConfigData>(); |
| } |
| |
| /** |
| * Returns a list of AFP font configuration data. |
| * @return the AFP font config data |
| */ |
| public List<AFPFontConfigData> getFontConfig() { |
| return fontsConfig; |
| } |
| |
| /** |
| * The parser for AFP font data. |
| */ |
| static final class AFPFontInfoConfigParser implements FontConfigParser { |
| |
| /** {@inheritDoc}} */ |
| public AFPFontConfig parse(Configuration cfg, FontManager fontManager, boolean strict, |
| EventProducer eventProducer) throws FOPException { |
| try { |
| return new ParserHelper(cfg, fontManager, strict, |
| (AFPEventProducer) eventProducer).fontConfig; |
| } catch (ConfigurationException ce) { |
| throw new FOPException(ce); |
| } |
| } |
| |
| AFPFontConfig getEmptyConfig() { |
| return new AFPFontConfig(); |
| } |
| } |
| |
| private static final class AggregateMatcher implements Matcher { |
| |
| private final List<Matcher> matchers; |
| |
| private AggregateMatcher(Matcher... matchers) { |
| this.matchers = new ArrayList<Matcher>(); |
| for (Matcher matcher : matchers) { |
| if (matcher != null) { |
| this.matchers.add(matcher); |
| } |
| } |
| } |
| |
| public boolean matches(FontTriplet triplet) { |
| for (Matcher matcher : matchers) { |
| if (matcher.matches(triplet)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } |
| |
| private static final class ParserHelper { |
| |
| private static final Log LOG = LogFactory.getLog(ParserHelper.class); |
| |
| private final AFPFontConfig fontConfig; |
| |
| private final Matcher matcher; |
| |
| private ParserHelper(Configuration cfg, FontManager fontManager, boolean strict, |
| AFPEventProducer eventProducer) throws FOPException, ConfigurationException { |
| Configuration fonts = cfg.getChild("fonts"); |
| Matcher localMatcher = null; |
| Configuration referencedFontsCfg = fonts.getChild("referenced-fonts", false); |
| if (referencedFontsCfg != null) { |
| localMatcher = FontManagerConfigurator.createFontsMatcher(referencedFontsCfg, strict); |
| } |
| matcher = new AggregateMatcher(fontManager.getReferencedFontsMatcher(), localMatcher); |
| fontConfig = new AFPFontConfig(); |
| for (Configuration font : fonts.getChildren("font")) { |
| buildFont(font, eventProducer); |
| } |
| } |
| |
| private void buildFont(Configuration fontCfg, AFPEventProducer eventProducer) |
| throws ConfigurationException { |
| //FontManager fontManager = this.userAgent.getFontManager(); |
| Configuration[] triplets = fontCfg.getChildren("font-triplet"); |
| List<FontTriplet> tripletList = new ArrayList<FontTriplet>(); |
| if (triplets.length == 0) { |
| eventProducer.fontConfigMissing(this, "<font-triplet...", fontCfg.getLocation()); |
| return; |
| } |
| for (Configuration triplet : triplets) { |
| int weight = FontUtil.parseCSS2FontWeight(triplet.getAttribute("weight")); |
| FontTriplet fontTriplet = new FontTriplet(triplet.getAttribute("name"), |
| triplet.getAttribute("style"), weight); |
| tripletList.add(fontTriplet); |
| } |
| |
| String tturi = fontCfg.getAttribute("embed-url", null); |
| if (tturi != null) { |
| fontFromType(tripletList, "truetype", null, "UTF-16BE", fontCfg, eventProducer, tturi); |
| return; |
| } |
| |
| //build the fonts |
| Configuration[] config = fontCfg.getChildren("afp-font"); |
| if (config.length == 0) { |
| eventProducer.fontConfigMissing(this, "<afp-font...", fontCfg.getLocation()); |
| return; |
| } |
| Configuration afpFontCfg = config[0]; |
| String uri = afpFontCfg.getAttribute("base-uri", null); |
| try { |
| String type = afpFontCfg.getAttribute("type"); |
| if (type == null) { |
| eventProducer.fontConfigMissing(this, "type attribute", fontCfg.getLocation()); |
| return; |
| } |
| String codepage = afpFontCfg.getAttribute("codepage"); |
| if (codepage == null) { |
| eventProducer.fontConfigMissing(this, "codepage attribute", |
| fontCfg.getLocation()); |
| return; |
| } |
| String encoding = afpFontCfg.getAttribute("encoding"); |
| if (encoding == null) { |
| eventProducer.fontConfigMissing(this, "encoding attribute", |
| fontCfg.getLocation()); |
| return; |
| } |
| |
| fontFromType(tripletList, type, codepage, encoding, afpFontCfg, eventProducer, uri); |
| } catch (ConfigurationException ce) { |
| eventProducer.invalidConfiguration(this, ce); |
| } |
| } |
| |
| private void fontFromType(List<FontTriplet> fontTriplets, String type, String codepage, |
| String encoding, Configuration cfg, AFPEventProducer eventProducer, String embedURI) |
| throws ConfigurationException { |
| AFPFontConfigData config = null; |
| if ("raster".equalsIgnoreCase(type)) { |
| config = getRasterFont(fontTriplets, type, codepage, encoding, cfg, eventProducer, |
| embedURI); |
| } else if ("outline".equalsIgnoreCase(type)) { |
| config = getOutlineFont(fontTriplets, type, codepage, encoding, cfg, eventProducer, |
| embedURI); |
| } else if ("CIDKeyed".equalsIgnoreCase(type)) { |
| config = getCIDKeyedFont(fontTriplets, type, codepage, encoding, cfg, |
| eventProducer, embedURI); |
| } else if ("truetype".equalsIgnoreCase(type)) { |
| config = getTruetypeFont(fontTriplets, type, codepage, encoding, cfg, eventProducer, embedURI); |
| } else { |
| LOG.error("No or incorrect type attribute: " + type); |
| } |
| if (config != null) { |
| fontConfig.fontsConfig.add(config); |
| } |
| } |
| |
| private CIDKeyedFontConfig getCIDKeyedFont(List<FontTriplet> fontTriplets, String type, |
| String codepage, String encoding, Configuration cfg, AFPEventProducer eventProducer, |
| String uri) throws ConfigurationException { |
| String characterset = cfg.getAttribute("characterset"); |
| if (characterset == null) { |
| eventProducer.fontConfigMissing(this, "characterset attribute", |
| cfg.getLocation()); |
| return null; |
| } |
| String name = cfg.getAttribute("name", characterset); |
| CharacterSetType charsetType = cfg.getAttributeAsBoolean("ebcdic-dbcs", false) |
| ? CharacterSetType.DOUBLE_BYTE_LINE_DATA : CharacterSetType.DOUBLE_BYTE; |
| return new CIDKeyedFontConfig(fontTriplets, type, codepage, encoding, characterset, |
| name, charsetType, isEmbbedable(fontTriplets), uri); |
| } |
| |
| private OutlineFontConfig getOutlineFont(List<FontTriplet> fontTriplets, String type, |
| String codepage, String encoding, Configuration cfg, |
| AFPEventProducer eventProducer, String uri) throws ConfigurationException { |
| String characterset = cfg.getAttribute("characterset"); |
| if (characterset == null) { |
| eventProducer.fontConfigMissing(this, "characterset attribute", |
| cfg.getLocation()); |
| return null; |
| } |
| String name = cfg.getAttribute("name", characterset); |
| String base14 = cfg.getAttribute("base14-font", null); |
| return new OutlineFontConfig(fontTriplets, type, codepage, encoding, characterset, |
| name, base14, isEmbbedable(fontTriplets), uri); |
| } |
| |
| private TrueTypeFontConfig getTruetypeFont(List<FontTriplet> fontTriplets, String type, String codepage, |
| String encoding, Configuration cfg, AFPEventProducer eventProducer, |
| String uri) throws ConfigurationException { |
| String name = cfg.getAttribute("name", null); |
| if (name == null) { |
| eventProducer.fontConfigMissing(this, "font name attribute", cfg.getLocation()); |
| return null; |
| } |
| String subfont = cfg.getAttribute("sub-font", null); |
| return new TrueTypeFontConfig(fontTriplets, type, codepage, encoding, "", |
| name, subfont, isEmbbedable(fontTriplets), uri); |
| } |
| |
| private RasterFontConfig getRasterFont(List<FontTriplet> triplets, String type, |
| String codepage, String encoding, Configuration cfg, |
| AFPEventProducer eventProducer, String uri) |
| throws ConfigurationException { |
| String name = cfg.getAttribute("name", "Unknown"); |
| // Create a new font object |
| Configuration[] rasters = cfg.getChildren("afp-raster-font"); |
| if (rasters.length == 0) { |
| eventProducer.fontConfigMissing(this, "<afp-raster-font...", |
| cfg.getLocation()); |
| return null; |
| } |
| List<RasterCharactersetData> charsetData = new ArrayList<RasterCharactersetData>(); |
| for (Configuration rasterCfg : rasters) { |
| String characterset = rasterCfg.getAttribute("characterset"); |
| if (characterset == null) { |
| eventProducer.fontConfigMissing(this, "characterset attribute", |
| cfg.getLocation()); |
| return null; |
| } |
| float size = rasterCfg.getAttributeAsFloat("size"); |
| int sizeMpt = (int) (size * 1000); |
| String base14 = rasterCfg.getAttribute("base14-font", null); |
| charsetData.add(new RasterCharactersetData(characterset, sizeMpt, base14)); |
| } |
| return new RasterFontConfig(triplets, type, codepage, encoding, null, name, uri, charsetData, |
| isEmbbedable(triplets)); |
| } |
| |
| private boolean isEmbbedable(List<FontTriplet> triplets) { |
| for (FontTriplet triplet : triplets) { |
| if (matcher.matches(triplet)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } |
| |
| abstract static class AFPFontConfigData { |
| protected final List<FontTriplet> triplets; |
| private final String codePage; |
| private final String encoding; |
| private final String name; |
| private final boolean embeddable; |
| protected final String uri; |
| |
| AFPFontConfigData(List<FontTriplet> triplets, String type, String codePage, |
| String encoding, String name, boolean embeddable, String uri) { |
| this.triplets = Collections.unmodifiableList(triplets); |
| this.codePage = codePage; |
| this.encoding = encoding; |
| this.name = name; |
| this.embeddable = embeddable; |
| this.uri = uri; |
| } |
| |
| static AFPFontInfo getFontInfo(AFPFont font, AFPFontConfigData config) { |
| return font != null ? new AFPFontInfo(font, config.triplets) : null; |
| } |
| |
| abstract AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, |
| AFPEventProducer eventProducer) throws IOException; |
| |
| AFPResourceAccessor getAccessor(InternalResourceResolver resourceResolver) { |
| return new AFPResourceAccessor(resourceResolver, uri); |
| } |
| } |
| |
| static final class CIDKeyedFontConfig extends AFPFontConfigData { |
| |
| private final CharacterSetType charsetType; |
| |
| private final String characterset; |
| |
| private CIDKeyedFontConfig(List<FontTriplet> triplets, String type, String codePage, String encoding, |
| String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) { |
| super(triplets, type, codePage, encoding, name, embeddable, uri); |
| this.characterset = characterset; |
| this.charsetType = charsetType; |
| } |
| |
| @Override |
| AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer) |
| throws IOException { |
| AFPResourceAccessor accessor = getAccessor(resourceResolver); |
| CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().buildDBCS( |
| characterset, super.codePage, super.encoding, charsetType, accessor, eventProducer); |
| return getFontInfo(new DoubleByteFont(super.codePage, super.embeddable, characterSet, |
| eventProducer), this); |
| } |
| } |
| |
| static final class TrueTypeFontConfig extends AFPFontConfigData { |
| private String characterset; |
| private String subfont; |
| private String fontUri; |
| |
| private TrueTypeFontConfig(List<FontTriplet> triplets, String type, String codePage, |
| String encoding, String characterset, String name, String subfont, |
| boolean embeddable, String uri) { |
| super(triplets, type, codePage, encoding, name, embeddable, null); |
| this.characterset = characterset; |
| this.subfont = subfont; |
| this.fontUri = uri; |
| } |
| |
| @Override |
| AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer) |
| throws IOException { |
| try { |
| FontUris fontUris = new FontUris(new URI(fontUri), null); |
| EmbedFontInfo embedFontInfo = new EmbedFontInfo(fontUris, false, true, null, subfont, EncodingMode.AUTO, |
| EmbeddingMode.FULL, false, false, true); |
| Typeface tf = new LazyFont(embedFontInfo, resourceResolver, false).getRealFont(); |
| AFPResourceAccessor accessor = getAccessor(resourceResolver); |
| CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().build(characterset, |
| super.codePage, super.encoding, tf, accessor, eventProducer); |
| OutlineFont font = new AFPTrueTypeFont(super.name, super.embeddable, characterSet, |
| eventProducer, subfont, new URI(fontUri)); |
| return getFontInfo(font, this); |
| } catch (URISyntaxException e) { |
| throw new IOException(e); |
| } |
| } |
| } |
| |
| public static class AFPTrueTypeFont extends OutlineFont { |
| private String ttc; |
| private URI uri; |
| public AFPTrueTypeFont(String name, boolean embeddable, CharacterSet charSet, AFPEventProducer eventProducer, |
| String ttc, URI uri) { |
| super(name, embeddable, charSet, eventProducer); |
| this.ttc = ttc; |
| this.uri = uri; |
| } |
| |
| public FontType getFontType() { |
| return FontType.TRUETYPE; |
| } |
| |
| public String getTTC() { |
| return ttc; |
| } |
| |
| public URI getUri() { |
| return uri; |
| } |
| } |
| |
| static final class OutlineFontConfig extends AFPFontConfigData { |
| private final String base14; |
| private final String characterset; |
| |
| private OutlineFontConfig(List<FontTriplet> triplets, String type, String codePage, |
| String encoding, String characterset, String name, String base14, boolean embeddable, String uri) { |
| super(triplets, type, codePage, encoding, name, embeddable, uri); |
| this.characterset = characterset; |
| this.base14 = base14; |
| } |
| |
| @Override |
| AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer) |
| throws IOException { |
| CharacterSet characterSet = null; |
| if (base14 != null) { |
| try { |
| Typeface tf = getTypeFace(base14); |
| characterSet = CharacterSetBuilder.getSingleByteInstance() |
| .build(characterset, super.codePage, |
| super.encoding, tf, eventProducer); |
| } catch (ClassNotFoundException cnfe) { |
| String msg = "The base 14 font class for " + characterset |
| + " could not be found"; |
| LOG.error(msg); |
| } |
| } else { |
| AFPResourceAccessor accessor = getAccessor(resourceResolver); |
| characterSet = CharacterSetBuilder.getSingleByteInstance().buildSBCS( |
| characterset, super.codePage, super.encoding, accessor, eventProducer); |
| } |
| return getFontInfo(new OutlineFont(super.name, super.embeddable, characterSet, |
| eventProducer), this); |
| } |
| } |
| |
| private static Typeface getTypeFace(String base14Name) throws ClassNotFoundException { |
| try { |
| Class<? extends Typeface> clazz = Class.forName("org.apache.fop.fonts.base14." |
| + base14Name).asSubclass(Typeface.class); |
| return clazz.getDeclaredConstructor().newInstance(); |
| } catch (IllegalAccessException iae) { |
| LOG.error(iae.getMessage()); |
| } catch (ClassNotFoundException cnfe) { |
| LOG.error(cnfe.getMessage()); |
| } catch (InstantiationException ie) { |
| LOG.error(ie.getMessage()); |
| } catch (NoSuchMethodException e) { |
| LOG.error(e.getMessage()); |
| } catch (InvocationTargetException e) { |
| LOG.error(e.getMessage()); |
| } |
| throw new ClassNotFoundException("Couldn't load file for AFP font with base14 name: " |
| + base14Name); |
| } |
| |
| static final class RasterFontConfig extends AFPFontConfigData { |
| private final List<RasterCharactersetData> charsets; |
| |
| private RasterFontConfig(List<FontTriplet> triplets, String type, String codePage, |
| String encoding, String characterset, String name, String uri, |
| List<RasterCharactersetData> csetData, boolean embeddable) { |
| super(triplets, type, codePage, encoding, name, embeddable, uri); |
| this.charsets = Collections.unmodifiableList(csetData); |
| } |
| |
| @Override |
| AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer) |
| throws IOException { |
| RasterFont rasterFont = new RasterFont(super.name, super.embeddable); |
| for (RasterCharactersetData charset : charsets) { |
| if (charset.base14 != null) { |
| try { |
| Typeface tf = getTypeFace(charset.base14); |
| rasterFont.addCharacterSet(charset.size, |
| CharacterSetBuilder.getSingleByteInstance().build( |
| charset.characterset, super.codePage, super.encoding, |
| tf, eventProducer)); |
| |
| } catch (ClassNotFoundException cnfe) { |
| String msg = "The base 14 font class for " + charset.characterset |
| + " could not be found"; |
| LOG.error(msg); |
| } catch (IOException ie) { |
| String msg = "The base 14 font class " + charset.characterset |
| + " could not be instantiated"; |
| LOG.error(msg); |
| } |
| } else { |
| AFPResourceAccessor accessor = getAccessor(resourceResolver); |
| rasterFont.addCharacterSet(charset.size, |
| CharacterSetBuilder.getSingleByteInstance().buildSBCS(charset.characterset, |
| super.codePage, super.encoding, accessor, eventProducer)); |
| } |
| } |
| return getFontInfo(rasterFont, this); |
| } |
| } |
| |
| static final class RasterCharactersetData { |
| private final String characterset; |
| private final int size; |
| private final String base14; |
| |
| private RasterCharactersetData(String characterset, int size, String base14) { |
| this.characterset = characterset; |
| this.size = size; |
| this.base14 = base14; |
| } |
| } |
| } |