From: Jeremias Maerki Date: Mon, 13 Nov 2006 16:28:13 +0000 (+0000) Subject: Added initial support for loading fonts without a pre-created XML font metrics file. X-Git-Tag: fop-0_93~30 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=652b3cbc5586d794a41bee4d2c824539e18b3cfc;p=xmlgraphics-fop.git Added initial support for loading fonts without a pre-created XML font metrics file. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@474387 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java index 8f21a449d..5d7221c13 100644 --- a/src/java/org/apache/fop/fonts/CustomFont.java +++ b/src/java/org/apache/fop/fonts/CustomFont.java @@ -31,7 +31,7 @@ public abstract class CustomFont extends Typeface private String fontName = null; private String embedFileName = null; - private String embedResourceName = null; + protected String embedResourceName = null; private FontResolver resolver = null; private int capHeight = 0; diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java new file mode 100644 index 000000000..4377f9fab --- /dev/null +++ b/src/java/org/apache/fop/fonts/FontLoader.java @@ -0,0 +1,106 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.io.IOUtils; +import org.apache.fop.fonts.truetype.TTFFontLoader; +import org.apache.fop.fonts.type1.Type1FontLoader; + +/** + * Base class for font loaders. + */ +public abstract class FontLoader { + + /** + * 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 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) + throws IOException { + FontLoader loader; + fontFileURI = fontFileURI.trim(); + String name = fontFileURI.toLowerCase(); + String effURI; + boolean type1 = false; + if (name.endsWith(".pfb")) { + type1 = true; + effURI = name.substring(0, fontFileURI.length() - 4) + ".pfm"; + } else { + effURI = fontFileURI; + } + + InputStream in = openFontFile(resolver, effURI); + try { + if (type1) { + loader = new Type1FontLoader(fontFileURI, in, resolver); + } else { + loader = new TTFFontLoader(fontFileURI, in, resolver); + } + return loader.getFont(); + } finally { + IOUtils.closeQuietly(in); + } + } + + private static InputStream openFontFile(FontResolver resolver, String uri) + throws IOException, MalformedURLException { + InputStream in = null; + if (resolver != null) { + Source source = resolver.resolve(uri); + if (source == null) { + String err = "Cannot load font: failed to create Source for font file " + + uri; + throw new IOException(err); + } + if (source instanceof StreamSource) { + in = ((StreamSource) source).getInputStream(); + } + if (in == null && source.getSystemId() != null) { + in = new java.net.URL(source.getSystemId()).openStream(); + } + if (in == null) { + String err = "Cannot load font: failed to create InputStream from" + + " Source for font file " + uri; + throw new IOException(err); + } + } else { + in = new URL(uri).openStream(); + } + return in; + } + + /** + * @return the font loaded by this loader + */ + public abstract CustomFont getFont(); + + +} diff --git a/src/java/org/apache/fop/fonts/FontSetup.java b/src/java/org/apache/fop/fonts/FontSetup.java index 4c9e3b768..3362ce334 100644 --- a/src/java/org/apache/fop/fonts/FontSetup.java +++ b/src/java/org/apache/fop/fonts/FontSetup.java @@ -209,30 +209,28 @@ public class FontSetup { EmbedFontInfo configFontInfo = (EmbedFontInfo) fontInfoList.get(i); String metricsFile = configFontInfo.getMetricsFile(); - if (metricsFile != null) { - 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(configFontInfo.getEmbedFile(), - metricsFile, - configFontInfo.getKerning(), - resolver); - fontInfo.addMetrics(internalName, font); + 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(configFontInfo.getEmbedFile(), + metricsFile, + configFontInfo.getKerning(), + resolver); + fontInfo.addMetrics(internalName, font); - List triplets = configFontInfo.getFontTriplets(); - for (int c = 0; c < triplets.size(); c++) { - FontTriplet triplet = (FontTriplet) triplets.get(c); + List triplets = configFontInfo.getFontTriplets(); + for (int c = 0; c < triplets.size(); c++) { + FontTriplet triplet = (FontTriplet) triplets.get(c); - if (log.isDebugEnabled()) { - log.debug("Registering: " + triplet + " under " + internalName); - } - fontInfo.addFontProperties(internalName, triplet); + if (log.isDebugEnabled()) { + log.debug("Registering: " + triplet + " under " + internalName); } + fontInfo.addFontProperties(internalName, triplet); } } } @@ -270,7 +268,7 @@ public class FontSetup { } EmbedFontInfo efi; - efi = new EmbedFontInfo(font[i].getAttribute("metrics-url"), + efi = new EmbedFontInfo(font[i].getAttribute("metrics-url", null), font[i].getAttributeAsBoolean("kerning", false), tripleList, font[i].getAttribute("embed-url", null)); diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 4d0b57cee..658c0cf1e 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -65,52 +65,58 @@ public class LazyFont extends Typeface implements FontDescriptor { private void load(boolean fail) { if (!isMetricsLoaded) { try { - /**@todo Possible thread problem here */ - FontReader reader = null; - if (resolver != null) { - Source source = resolver.resolve(metricsFileName); - if (source == null) { - String err = "Cannot load font: failed to create Source from metrics file " - + metricsFileName; - if (fail) { - throw new RuntimeException(err); - } else { - log.error(err); + if (metricsFileName != null) { + /**@todo Possible thread problem here */ + FontReader reader = null; + if (resolver != null) { + Source source = resolver.resolve(metricsFileName); + if (source == null) { + String err = "Cannot load font: failed to create Source from metrics file " + + metricsFileName; + if (fail) { + throw new RuntimeException(err); + } else { + log.error(err); + } + return; } - return; - } - InputStream in = null; - if (source instanceof StreamSource) { - in = ((StreamSource) source).getInputStream(); - } - if (in == null && source.getSystemId() != null) { - in = new java.net.URL(source.getSystemId()).openStream(); - } - if (in == null) { - String err = "Cannot load font: failed to create InputStream from" - + " Source for metrics file " + metricsFileName; - if (fail) { - throw new RuntimeException(err); - } else { - log.error(err); + InputStream in = null; + if (source instanceof StreamSource) { + in = ((StreamSource) source).getInputStream(); } - return; + if (in == null && source.getSystemId() != null) { + in = new java.net.URL(source.getSystemId()).openStream(); + } + if (in == null) { + String err = "Cannot load font: failed to create InputStream from" + + " Source for metrics file " + metricsFileName; + if (fail) { + throw new RuntimeException(err); + } else { + log.error(err); + } + return; + } + InputSource src = new InputSource(in); + src.setSystemId(source.getSystemId()); + reader = new FontReader(src); + } else { + reader + = new FontReader(new InputSource(new URL(metricsFileName).openStream())); } - InputSource src = new InputSource(in); - src.setSystemId(source.getSystemId()); - reader = new FontReader(src); + reader.setKerningEnabled(useKerning); + reader.setFontEmbedPath(fontEmbedPath); + reader.setResolver(resolver); + realFont = reader.getFont(); } else { - reader - = new FontReader(new InputSource(new URL(metricsFileName).openStream())); + if (fontEmbedPath == null) { + throw new RuntimeException("Cannot load font. No font URIs available."); + } + realFont = FontLoader.loadFont(fontEmbedPath, resolver); } - reader.setKerningEnabled(useKerning); - reader.setFontEmbedPath(fontEmbedPath); - reader.setResolver(resolver); - realFont = reader.getFont(); if (realFont instanceof FontDescriptor) { realFontDescriptor = (FontDescriptor) realFont; } - // log.debug("Metrics " + metricsFileName + " loaded."); } catch (FOPException fopex) { log.error("Failed to read font metrics file " + metricsFileName, fopex); if (fail) { diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index bcb295741..2dd3846ed 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -33,8 +33,6 @@ public class MultiByteFont extends CIDFont { private String ttcName = null; private String encoding = "Identity-H"; - private String embedResourceName = null; - private int defaultWidth = 0; private CIDFontType cidType = CIDFontType.CIDTYPE2; diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java new file mode 100644 index 000000000..53be6a8d0 --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -0,0 +1,125 @@ +/* + * 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.truetype; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.fop.fonts.BFEntry; +import org.apache.fop.fonts.CIDFontType; +import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.FontLoader; +import org.apache.fop.fonts.FontResolver; +import org.apache.fop.fonts.MultiByteFont; + +/** + * Loads a font into memory directly from the original font file. + */ +public class TTFFontLoader extends FontLoader { + + private String fontFileURI; + private TTFFile ttf; + private MultiByteFont multiFont; + private CustomFont returnFont; + private FontResolver resolver; + + public TTFFontLoader(String fontFileURI, InputStream in, FontResolver resolver) + throws IOException { + this.fontFileURI = fontFileURI; + this.resolver = resolver; + + this.ttf = new TTFFile(); + FontFileReader reader = new FontFileReader(in); + boolean supported = ttf.readFont(reader, null); + if (!supported) { + throw new IOException("Could not load TrueType font: " + fontFileURI); + } + multiFont = new MultiByteFont(); + multiFont.setResolver(this.resolver); + returnFont = multiFont; + read(); + } + + private void read() throws IOException { + returnFont.setFontName(ttf.getFamilyName()); + //multiFont.setTTCName(ttcName) + 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); + 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); + copyKerning(ttf, true); + multiFont.setEmbedFileName(this.fontFileURI); + + } + + private void copyKerning(TTFFile ttf, boolean isCid) { + + // Get kerning + Iterator iter; + if (isCid) { + iter = ttf.getKerning().keySet().iterator(); + } else { + iter = ttf.getAnsiKerning().keySet().iterator(); + } + + while (iter.hasNext()) { + Integer kpx1 = (Integer)iter.next(); + + Map h2; + if (isCid) { + h2 = (Map)ttf.getKerning().get(kpx1); + } else { + h2 = (Map)ttf.getAnsiKerning().get(kpx1); + } + + returnFont.putKerningEntry(kpx1, h2); + } + } + + + /** @see org.apache.fop.fonts.FontLoader#getFont() */ + public CustomFont getFont() { + return this.returnFont; + } + +} diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java new file mode 100644 index 000000000..ef0d45767 --- /dev/null +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -0,0 +1,88 @@ +/* + * 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.type1; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.FontLoader; +import org.apache.fop.fonts.FontResolver; +import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.SingleByteFont; + +/** + * Loads a Type 1 font into memory directly from the original font file. + */ +public class Type1FontLoader extends FontLoader { + + private String fontFileURI; + private PFMFile pfm; + private SingleByteFont singleFont; + private CustomFont returnFont; + private FontResolver resolver; + + /** + * Constructs a new Type 1 font loader. + * @param fontFileURI the URI to the PFB file of a Type 1 font + * @param in the InputStream reading the PFM file of a Type 1 font + * @param resolver the font resolver used to resolve URIs + * @throws IOException In case of an I/O error + */ + public Type1FontLoader(String fontFileURI, InputStream in, FontResolver resolver) + throws IOException { + this.fontFileURI = fontFileURI; + this.resolver = resolver; + + pfm = new PFMFile(); + pfm.load(in); + singleFont = new SingleByteFont(); + singleFont.setFontType(FontType.TYPE1); + singleFont.setResolver(this.resolver); + returnFont = singleFont; + read(); + } + + private void read() throws IOException { + returnFont.setFontName(pfm.getPostscriptName()); + returnFont.setCapHeight(pfm.getCapHeight()); + returnFont.setXHeight(pfm.getXHeight()); + returnFont.setAscender(pfm.getLowerCaseAscent()); + returnFont.setDescender(pfm.getLowerCaseDescent()); + returnFont.setFontBBox(pfm.getFontBBox()); + returnFont.setFirstChar(pfm.getFirstChar()); + returnFont.setLastChar(pfm.getFirstChar()); + returnFont.setFlags(pfm.getFlags()); + returnFont.setStemV(pfm.getStemV()); + returnFont.setItalicAngle(pfm.getItalicAngle()); + returnFont.setMissingWidth(0); + for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) { + singleFont.setWidth(i, pfm.getCharWidth(i)); + } + singleFont.setEmbedFileName(this.fontFileURI); + + } + + /** @see org.apache.fop.fonts.FontLoader#getFont() */ + public CustomFont getFont() { + return this.returnFont; + } + +} diff --git a/status.xml b/status.xml index 5a3bcc431..e931f104e 100644 --- a/status.xml +++ b/status.xml @@ -28,6 +28,10 @@ + + Added initial support for loading fonts without a pre-created XML font metric + file. + Support for the rgb-icc() function and for a proprietary cmyk() function (for device CMYK colors only through the PDF renderer so far).