aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVincent Hennebert <vhennebert@apache.org>2012-03-30 19:09:29 +0000
committerVincent Hennebert <vhennebert@apache.org>2012-03-30 19:09:29 +0000
commit7c8f02c57302d672fe4d0a8fc5332af6d9f0e610 (patch)
tree9be4d0493b7aa44854254ae99dc842de739d9bea /src
parent7a51f3fdb1f7ae1f526ea75f804e8f016d653cb8 (diff)
downloadxmlgraphics-fop-7c8f02c57302d672fe4d0a8fc5332af6d9f0e610.tar.gz
xmlgraphics-fop-7c8f02c57302d672fe4d0a8fc5332af6d9f0e610.zip
Bugzilla #50483: Improved support for TrueType fonts in PostScript
Refactored code and added unit tests Patch by Mehdi Houshmand git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript@1307574 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java8
-rw-r--r--src/java/org/apache/fop/fonts/BFEntry.java35
-rw-r--r--src/java/org/apache/fop/fonts/CustomFont.java18
-rw-r--r--src/java/org/apache/fop/fonts/CustomFontCollection.java2
-rw-r--r--src/java/org/apache/fop/fonts/EmbedFontInfo.java23
-rw-r--r--src/java/org/apache/fop/fonts/EmbeddingMode.java56
-rw-r--r--src/java/org/apache/fop/fonts/EncodingMode.java2
-rw-r--r--src/java/org/apache/fop/fonts/FontInfoConfigurator.java6
-rw-r--r--src/java/org/apache/fop/fonts/FontLoader.java23
-rw-r--r--src/java/org/apache/fop/fonts/LazyFont.java11
-rw-r--r--src/java/org/apache/fop/fonts/MutableFont.java6
-rw-r--r--src/java/org/apache/fop/fonts/apps/TTFReader.java30
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java7
-rw-r--r--src/java/org/apache/fop/fonts/truetype/FontFileReader.java29
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java122
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java8
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFile.java587
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java36
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java48
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java51
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java532
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFTableName.java158
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java37
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java8
-rw-r--r--src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java5
-rw-r--r--src/java/org/apache/fop/render/ps/PSFontUtils.java234
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java53
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java104
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java72
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java64
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java58
31 files changed, 1510 insertions, 923 deletions
diff --git a/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java b/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
index 6786e43f0..0945c52bb 100644
--- a/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
+++ b/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
@@ -37,18 +37,16 @@ import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
-import org.w3c.dom.Node;
-
import org.apache.commons.io.IOUtils;
+import org.apache.fop.events.model.EventModel;
+import org.apache.fop.events.model.EventProducerModel;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.selectors.FilenameSelector;
-
-import org.apache.fop.events.model.EventModel;
-import org.apache.fop.events.model.EventProducerModel;
+import org.w3c.dom.Node;
/**
* Ant task which inspects a file set for Java interfaces which extend the
diff --git a/src/java/org/apache/fop/fonts/BFEntry.java b/src/java/org/apache/fop/fonts/BFEntry.java
index c8311a9d2..654c1952b 100644
--- a/src/java/org/apache/fop/fonts/BFEntry.java
+++ b/src/java/org/apache/fop/fonts/BFEntry.java
@@ -22,14 +22,13 @@ package org.apache.fop.fonts;
/**
* This is just a holder class for bfentries, groups of characters of a base font (bf).
*/
-public class BFEntry {
+public final class BFEntry {
//TODO Think about renaming this class to CMapRange or something.
- //TODO Copy equals() and hashCode() from TTFCmapEntry
- private int unicodeStart;
- private int unicodeEnd;
- private int glyphStartIndex;
+ private final int unicodeStart;
+ private final int unicodeEnd;
+ private final int glyphStartIndex;
/**
* Main constructor.
@@ -44,6 +43,32 @@ public class BFEntry {
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int hc = 17;
+ hc = 31 * hc + unicodeStart;
+ hc = 31 * hc + unicodeEnd;
+ hc = 31 * hc + glyphStartIndex;
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BFEntry) {
+ BFEntry ce = (BFEntry) o;
+ return ce.unicodeStart == this.unicodeStart
+ && ce.unicodeEnd == this.unicodeEnd
+ && ce.glyphStartIndex == this.glyphStartIndex;
+ }
+ return false;
+ }
+
+ /**
* Returns the unicodeStart.
* @return the Unicode start index
*/
diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java
index bc64e0f33..a7b630578 100644
--- a/src/java/org/apache/fop/fonts/CustomFont.java
+++ b/src/java/org/apache/fop/fonts/CustomFont.java
@@ -42,6 +42,7 @@ public abstract class CustomFont extends Typeface
private String embedFileName = null;
private String embedResourceName = null;
private FontResolver resolver = null;
+ private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
private int capHeight = 0;
private int xHeight = 0;
@@ -114,6 +115,14 @@ public abstract class CustomFont extends Typeface
}
/**
+ * Returns the embedding mode for this font.
+ * @return embedding mode
+ */
+ public EmbeddingMode getEmbeddingMode() {
+ return embeddingMode;
+ }
+
+ /**
* Returns a Source representing an embeddable font file.
* @return Source for an embeddable font file
* @throws IOException if embedFileName is not null but Source is not found
@@ -262,7 +271,7 @@ public abstract class CustomFont extends Typeface
return lastChar;
}
- /**
+ /**MutableFont
* Used to determine if kerning is enabled.
* @return True if kerning is enabled.
*/
@@ -330,6 +339,13 @@ public abstract class CustomFont extends Typeface
/**
* {@inheritDoc}
*/
+ public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+ this.embeddingMode = embeddingMode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void setCapHeight(int capHeight) {
this.capHeight = capHeight;
}
diff --git a/src/java/org/apache/fop/fonts/CustomFontCollection.java b/src/java/org/apache/fop/fonts/CustomFontCollection.java
index 9f98814a3..3bd1fa7c7 100644
--- a/src/java/org/apache/fop/fonts/CustomFontCollection.java
+++ b/src/java/org/apache/fop/fonts/CustomFontCollection.java
@@ -71,7 +71,7 @@ public class CustomFontCollection implements FontCollection {
List<FontTriplet> triplets = embedFontInfo.getFontTriplets();
for (int tripletIndex = 0; tripletIndex < triplets.size(); tripletIndex++) {
- FontTriplet triplet = (FontTriplet) triplets.get(tripletIndex);
+ FontTriplet triplet = triplets.get(tripletIndex);
fontInfo.addFontProperties(internalName, triplet);
}
}
diff --git a/src/java/org/apache/fop/fonts/EmbedFontInfo.java b/src/java/org/apache/fop/fonts/EmbedFontInfo.java
index b53cdfdd6..2bc8bb452 100644
--- a/src/java/org/apache/fop/fonts/EmbedFontInfo.java
+++ b/src/java/org/apache/fop/fonts/EmbedFontInfo.java
@@ -25,6 +25,8 @@ import java.util.List;
/**
* FontInfo contains meta information on fonts (where is the metrics file etc.)
+ * TODO: We need to remove this class and think about more intelligent design patterns
+ * (Data classes => Procedural code)
*/
public class EmbedFontInfo implements Serializable {
@@ -39,6 +41,8 @@ public class EmbedFontInfo implements Serializable {
protected boolean kerning;
/** the requested encoding mode for the font */
protected EncodingMode encodingMode = EncodingMode.AUTO;
+ /** the requested embedding mode for this font */
+ protected EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
/** the PostScript name of the font */
protected String postScriptName = null;
@@ -137,6 +141,14 @@ public class EmbedFontInfo implements Serializable {
}
/**
+ * Returns the embedding mode for this font.
+ * @return the embedding mode.
+ */
+ public EmbeddingMode getEmbeddingMode() {
+ return embeddingMode;
+ }
+
+ /**
* Defines whether the font is embedded or not.
* @param value true to embed the font, false to reference it
*/
@@ -163,6 +175,17 @@ public class EmbedFontInfo implements Serializable {
this.encodingMode = mode;
}
+ /**
+ * Sets the embedding mode for this font, currently not supported for type1 fonts.
+ * @param embeddingMode the new embedding mode.
+ */
+ public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+ if (embeddingMode == null) {
+ throw new NullPointerException("embeddingMode must not be null");
+ }
+ this.embeddingMode = embeddingMode;
+ }
+
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
diff --git a/src/java/org/apache/fop/fonts/EmbeddingMode.java b/src/java/org/apache/fop/fonts/EmbeddingMode.java
new file mode 100644
index 000000000..89f56cfd7
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/EmbeddingMode.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * This enumerates the embedding mode of fonts; full; subset; auto (auto defaults to full for
+ * type1 fonts and subset for truetype fonts.
+ */
+public enum EmbeddingMode {
+ /** Default option: assumes FULL for type1 fonts and SUBSET for truetype fonts. */
+ AUTO,
+ /** Full font embedding: This means the whole of the font is written to file. */
+ FULL,
+ /** Subset font embedding: Only the mandatory tables and a subset of glyphs are written
+ * to file.*/
+ SUBSET;
+
+ /**
+ * Returns the name of this embedding mode.
+ * @return String - lower case.
+ */
+ public String getName() {
+ return this.toString().toLowerCase();
+ }
+
+ /**
+ * Returns {@link EmbeddingMode} by name.
+ * @param value String - the name of the embedding mode (not case sensitive).
+ * @return embedding mode constant.
+ */
+ public static EmbeddingMode getValue(String value) {
+ for (EmbeddingMode mode : EmbeddingMode.values()) {
+ if (mode.toString().equalsIgnoreCase(value)) {
+ return mode;
+ }
+ }
+ throw new IllegalArgumentException("Invalid embedding-mode: " + value);
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/EncodingMode.java b/src/java/org/apache/fop/fonts/EncodingMode.java
index 8a40d6593..78ffb7ac6 100644
--- a/src/java/org/apache/fop/fonts/EncodingMode.java
+++ b/src/java/org/apache/fop/fonts/EncodingMode.java
@@ -52,7 +52,7 @@ public enum EncodingMode {
* @param name the name of the encoding mode to look up
* @return the encoding mode constant
*/
- public static EncodingMode getEncodingMode(String name) {
+ public static EncodingMode getValue(String name) {
for (EncodingMode em : EncodingMode.values()) {
if (name.equalsIgnoreCase(em.getName())) {
return em;
diff --git a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
index 67bb2e295..bf4c01453 100644
--- a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
+++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
@@ -251,11 +251,15 @@ public class FontInfoConfigurator {
}
boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
- EncodingMode encodingMode = EncodingMode.getEncodingMode(
+ EncodingMode encodingMode = EncodingMode.getValue(
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()));
+ EmbeddingMode embeddingMode = EmbeddingMode.getValue(
+ fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString()));
EmbedFontInfo embedFontInfo
= new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont);
embedFontInfo.setEncodingMode(encodingMode);
+ embedFontInfo.setEmbeddingMode(embeddingMode);
+
if (fontCache != null) {
if (!fontCache.containsFont(embedFontInfo)) {
fontCache.addFont(embedFontInfo);
diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java
index 02c09a1a1..b033e517e 100644
--- a/src/java/org/apache/fop/fonts/FontLoader.java
+++ b/src/java/org/apache/fop/fonts/FontLoader.java
@@ -30,7 +30,6 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.apache.fop.fonts.truetype.TTFFontLoader;
import org.apache.fop.fonts.type1.Type1FontLoader;
@@ -80,15 +79,17 @@ public abstract class FontLoader {
* @param fontFile the File representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode
* @param encodingMode the requested encoding mode
* @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, String subFontName,
- boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ FontResolver resolver) throws IOException {
return loadFont(fontFile.toURI().toURL(), subFontName,
- embedded, encodingMode, resolver);
+ embedded, embeddingMode, encodingMode, resolver);
}
/**
@@ -96,16 +97,17 @@ public abstract class FontLoader {
* @param fontUrl the URL representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @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, String subFontName,
- boolean embedded, EncodingMode encodingMode,
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
FontResolver resolver) throws IOException {
return loadFont(fontUrl.toExternalForm(), subFontName,
- embedded, encodingMode, true,
+ embedded, embeddingMode, encodingMode, true,
resolver);
}
@@ -114,6 +116,7 @@ public abstract class FontLoader {
* @param fontFileURI the URI to the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param useKerning indicates whether kerning information should be loaded if available
* @param resolver the font resolver to use when resolving URIs
@@ -121,8 +124,8 @@ public abstract class FontLoader {
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, boolean useKerning,
- FontResolver resolver) throws IOException {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ boolean useKerning, FontResolver resolver) throws IOException {
fontFileURI = fontFileURI.trim();
boolean type1 = isType1(fontFileURI);
FontLoader loader;
@@ -131,10 +134,14 @@ public abstract class FontLoader {
throw new IllegalArgumentException(
"CID encoding mode not supported for Type 1 fonts");
}
+ if (embeddingMode == EmbeddingMode.SUBSET) {
+ throw new IllegalArgumentException(
+ "Subset embedding for Type 1 fonts is not supported");
+ }
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver);
} else {
loader = new TTFFontLoader(fontFileURI, subFontName,
- embedded, encodingMode, useKerning, resolver);
+ embedded, embeddingMode, encodingMode, useKerning, resolver);
}
return loader.getFont();
}
diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java
index a8fd447c4..4d63dafea 100644
--- a/src/java/org/apache/fop/fonts/LazyFont.java
+++ b/src/java/org/apache/fop/fonts/LazyFont.java
@@ -27,12 +27,10 @@ import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
-import org.xml.sax.InputSource;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.apache.fop.apps.FOPException;
+import org.xml.sax.InputSource;
/**
* This class is used to defer the loading of a font until it is really used.
@@ -45,6 +43,7 @@ public class LazyFont extends Typeface implements FontDescriptor {
private String fontEmbedPath = null;
private boolean useKerning = false;
private EncodingMode encodingMode = EncodingMode.AUTO;
+ private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
private boolean embedded = true;
private String subFontName = null;
@@ -65,6 +64,7 @@ public class LazyFont extends Typeface implements FontDescriptor {
this.fontEmbedPath = fontInfo.getEmbedFile();
this.useKerning = fontInfo.getKerning();
this.encodingMode = fontInfo.getEncodingMode();
+ this.embeddingMode = fontInfo.getEmbeddingMode();
this.subFontName = fontInfo.getSubFontName();
this.embedded = fontInfo.isEmbedded();
this.resolver = resolver;
@@ -131,8 +131,9 @@ public class LazyFont extends Typeface implements FontDescriptor {
if (fontEmbedPath == null) {
throw new RuntimeException("Cannot load font. No font URIs available.");
}
- realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName,
- this.embedded, this.encodingMode, useKerning, resolver);
+ realFont = FontLoader.loadFont(fontEmbedPath, subFontName,
+ embedded, embeddingMode, encodingMode,
+ useKerning, resolver);
}
if (realFont instanceof FontDescriptor) {
realFontDescriptor = (FontDescriptor) realFont;
diff --git a/src/java/org/apache/fop/fonts/MutableFont.java b/src/java/org/apache/fop/fonts/MutableFont.java
index bcbcadbdc..c19dca6b5 100644
--- a/src/java/org/apache/fop/fonts/MutableFont.java
+++ b/src/java/org/apache/fop/fonts/MutableFont.java
@@ -61,6 +61,12 @@ public interface MutableFont {
void setEmbedResourceName(String name);
/**
+ * Set the embedding mode for this font.
+ * @param embeddingMode the embedding mode.
+ */
+ void setEmbeddingMode(EmbeddingMode embeddingMode);
+
+ /**
* Sets the capital height value.
* @param capHeight capital height
*/
diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java
index 6e64f9144..bd2fe1257 100644
--- a/src/java/org/apache/fop/fonts/apps/TTFReader.java
+++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java
@@ -20,7 +20,6 @@
package org.apache.fop.fonts.apps;
import java.io.IOException;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -28,9 +27,9 @@ import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.Version;
+import org.apache.fop.fonts.BFEntry;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.truetype.FontFileReader;
-import org.apache.fop.fonts.truetype.TTFCmapEntry;
import org.apache.fop.fonts.truetype.TTFFile;
import org.apache.fop.util.CommandLineLogger;
import org.w3c.dom.Document;
@@ -272,9 +271,9 @@ public class TTFReader extends AbstractFontReader {
root.appendChild(el);
el.appendChild(doc.createTextNode(ttf.getFullName()));
}
- Set familyNames = ttf.getFamilyNames();
+ Set<String> familyNames = ttf.getFamilyNames();
if (familyNames.size() > 0) {
- String familyName = (String)familyNames.iterator().next();
+ String familyName = familyNames.iterator().next();
el = doc.createElement("family-name");
root.appendChild(el);
el.appendChild(doc.createTextNode(familyName));
@@ -370,9 +369,7 @@ public class TTFReader extends AbstractFontReader {
el = doc.createElement("bfranges");
mel.appendChild(el);
- Iterator iter = ttf.getCMaps().listIterator();
- while (iter.hasNext()) {
- TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+ for (BFEntry ce : ttf.getCMaps()) {
Element el2 = doc.createElement("bf");
el.appendChild(el2);
el2.setAttribute("us", String.valueOf(ce.getUnicodeStart()));
@@ -427,31 +424,28 @@ public class TTFReader extends AbstractFontReader {
Document doc = parent.getOwnerDocument();
// Get kerning
- Iterator iter;
+ Set<Integer> kerningKeys;
if (isCid) {
- iter = ttf.getKerning().keySet().iterator();
+ kerningKeys = ttf.getKerning().keySet();
} else {
- iter = ttf.getAnsiKerning().keySet().iterator();
+ kerningKeys = ttf.getAnsiKerning().keySet();
}
- while (iter.hasNext()) {
- Integer kpx1 = (Integer)iter.next();
+ for (Integer kpx1 : kerningKeys) {
el = doc.createElement("kerning");
el.setAttribute("kpx1", kpx1.toString());
parent.appendChild(el);
Element el2 = null;
- Map h2;
+ Map<Integer, Integer> h2;
if (isCid) {
- h2 = (Map)ttf.getKerning().get(kpx1);
+ h2 = ttf.getKerning().get(kpx1);
} else {
- h2 = (Map)ttf.getAnsiKerning().get(kpx1);
+ h2 = ttf.getAnsiKerning().get(kpx1);
}
- Iterator iter2 = h2.keySet().iterator();
- while (iter2.hasNext()) {
- Integer kpx2 = (Integer)iter2.next();
+ for (Integer kpx2 : h2.keySet()) {
if (isCid || kpx2.intValue() < 256) {
el2 = doc.createElement("pair");
el2.setAttribute("kpx2", kpx2.toString());
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index 714af0e57..a3e74ada7 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -31,6 +31,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
@@ -218,7 +219,8 @@ public class FontInfoFinder {
}
try {
TTFFontLoader ttfLoader = new TTFFontLoader(
- fontFileURL, fontName, true, EncodingMode.AUTO, true, resolver);
+ fontFileURL, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO,
+ true, resolver);
customFont = ttfLoader.getFont();
if (this.eventListener != null) {
customFont.setEventListener(this.eventListener);
@@ -242,7 +244,8 @@ public class FontInfoFinder {
} else {
// The normal case
try {
- customFont = FontLoader.loadFont(fontURL, null, true, EncodingMode.AUTO, resolver);
+ customFont = FontLoader.loadFont(fontURL, null, true, EmbeddingMode.AUTO,
+ EncodingMode.AUTO, resolver);
if (this.eventListener != null) {
customFont.setEventListener(this.eventListener);
}
diff --git a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
index a6db7b6d0..e394b98ed 100644
--- a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
+++ b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
@@ -90,23 +90,13 @@ public class FontFileReader {
}
/**
- * Set current file position to offset
- *
- * @param add The number of bytes to advance
- * @throws IOException In case of an I/O problem
- */
- public void seekAdd(long add) throws IOException {
- seekSet(current + add);
- }
-
- /**
* Skip a given number of bytes.
*
* @param add The number of bytes to advance
* @throws IOException In case of an I/O problem
*/
public void skip(long add) throws IOException {
- seekAdd(add);
+ seekSet(current + add);
}
/**
@@ -133,7 +123,7 @@ public class FontFileReader {
* @return One byte
* @throws IOException If EOF is reached
*/
- public byte read() throws IOException {
+ private byte read() throws IOException {
if (current >= fsize) {
throw new java.io.EOFException("Reached EOF, file size=" + fsize);
}
@@ -277,14 +267,14 @@ public class FontFileReader {
public final String readTTFString() throws IOException {
int i = current;
while (file[i++] != 0) {
- if (i > fsize) {
+ if (i >= fsize) {
throw new java.io.EOFException("Reached EOF, file size="
+ fsize);
}
}
- byte[] tmp = new byte[i - current];
- System.arraycopy(file, current, tmp, 0, i - current);
+ byte[] tmp = new byte[i - current - 1];
+ System.arraycopy(file, current, tmp, 0, i - current - 1);
return new String(tmp, "ISO-8859-1");
}
@@ -352,6 +342,11 @@ public class FontFileReader {
System.arraycopy(file, offset, ret, 0, length);
return ret;
}
-
-
+ /**
+ * Returns the full byte array representation of the file.
+ * @return byte array.
+ */
+ public byte[] getAllBytes() {
+ return file;
+ }
} \ No newline at end of file
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
deleted file mode 100644
index a63f76def..000000000
--- a/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
+++ /dev/null
@@ -1,122 +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.truetype;
-
-/**
- * The CMap entry contains information of a Unicode range and the
- * the glyph indexes related to the range
- */
-public class TTFCmapEntry {
-
- //TODO this class is redundant: BFEntry does the same but doesn't have an intuitive name
-
- private int unicodeStart;
- private int unicodeEnd;
- private int glyphStartIndex;
-
- TTFCmapEntry() {
- unicodeStart = 0;
- unicodeEnd = 0;
- glyphStartIndex = 0;
- }
-
- TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
- this.unicodeStart = unicodeStart;
- this.unicodeEnd = unicodeEnd;
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- int hc = super.hashCode();
- hc ^= ( hc * 11 ) + unicodeStart;
- hc ^= ( hc * 19 ) + unicodeEnd;
- hc ^= ( hc * 23 ) + glyphStartIndex;
- return hc;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object o) {
- if (o instanceof TTFCmapEntry) {
- TTFCmapEntry ce = (TTFCmapEntry)o;
- if (ce.unicodeStart == this.unicodeStart
- && ce.unicodeEnd == this.unicodeEnd
- && ce.glyphStartIndex == this.glyphStartIndex) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the glyphStartIndex.
- * @return int
- */
- public int getGlyphStartIndex() {
- return glyphStartIndex;
- }
-
- /**
- * Returns the unicodeEnd.
- * @return int
- */
- public int getUnicodeEnd() {
- return unicodeEnd;
- }
-
- /**
- * Returns the unicodeStart.
- * @return int
- */
- public int getUnicodeStart() {
- return unicodeStart;
- }
-
- /**
- * Sets the glyphStartIndex.
- * @param glyphStartIndex The glyphStartIndex to set
- */
- public void setGlyphStartIndex(int glyphStartIndex) {
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * Sets the unicodeEnd.
- * @param unicodeEnd The unicodeEnd to set
- */
- public void setUnicodeEnd(int unicodeEnd) {
- this.unicodeEnd = unicodeEnd;
- }
-
- /**
- * Sets the unicodeStart.
- * @param unicodeStart The unicodeStart to set
- */
- public void setUnicodeStart(int unicodeStart) {
- this.unicodeStart = unicodeStart;
- }
-
-}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
index 405a71395..ca121a609 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
@@ -33,6 +33,14 @@ class TTFDirTabEntry {
private long offset;
private long length;
+ public TTFDirTabEntry() {
+ }
+
+ public TTFDirTabEntry(long offset, long length) {
+ this.offset = offset;
+ this.length = length;
+ }
+
/**
* Read Dir Tab, return tag name
*/
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
index 5aea4a66b..74d32a769 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
@@ -20,14 +20,22 @@
package org.apache.fop.fonts.truetype;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.BitSet;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fonts.BFEntry;
import org.apache.fop.fonts.FontUtil;
import org.apache.xmlgraphics.fonts.Glyphs;
@@ -127,6 +135,8 @@ public class TTFFile {
"ccaron", "dcroat"
};
+ /** The FontFileReader used to read this truetype font */
+ protected FontFileReader fontFile;
/** Set to true to get even more debug output than with level DEBUG */
public static final boolean TRACE_ENABLED = false;
@@ -138,11 +148,11 @@ public class TTFFile {
/**
* Table directory
*/
- protected Map dirTabs;
- private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
+ protected Map<TTFTableName, TTFDirTabEntry> dirTabs;
+ private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
- private List<TTFCmapEntry> cmaps;
- private List unicodeMapping;
+ private List<BFEntry> cmaps;
+ private List<UnicodeMapping> unicodeMapping;
private int upem; // unitsPerEm from "head" table
private int nhmtx; // Number of horizontal metrics
@@ -153,18 +163,16 @@ public class TTFFile {
*/
protected long lastLoca = 0;
private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
- private int nmGlyphs; // Used in fixWidths - remove?
/**
* Contains glyph data
*/
protected TTFMtxEntry[] mtxTab; // Contains glyph data
- private final int[] mtxEncoded = null;
private String postScriptName = "";
private String fullName = "";
private String notice = "";
- private final Set familyNames = new java.util.HashSet(); //Set<String>
+ private Set<String> familyNames = new HashSet<String>();
private String subFamilyName = "";
private long italicAngle = 0;
@@ -193,12 +201,12 @@ public class TTFFile {
private short lastChar = 0;
private int[] ansiWidth;
- private Map ansiIndex;
+ private Map<Integer, List<Integer>> ansiIndex;
// internal mapping of glyph indexes to unicode indexes
// used for quick mappings in this class
- private final Map glyphToUnicodeMap = new java.util.HashMap();
- private final Map unicodeToGlyphMap = new java.util.HashMap();
+ private Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer> ();
+ private Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer> ();
private TTFDirTabEntry currentDirTab;
@@ -210,9 +218,9 @@ public class TTFFile {
protected Log log = LogFactory.getLog(TTFFile.class);
/**
- * Key-value helper class
+ * Key-value helper class (immutable)
*/
- class UnicodeMapping {
+ final class UnicodeMapping {
private final int unicodeIndex;
private final int glyphIndex;
@@ -244,33 +252,26 @@ public class TTFFile {
/**
* Version of the PostScript table (<q>post</q>) contained in this font.
*/
- public static final class PostScriptVersion {
-
+ public static enum PostScriptVersion {
/** PostScript table version 1.0. */
- public static final PostScriptVersion V1 = new PostScriptVersion();
-
+ V1,
/** PostScript table version 2.0. */
- public static final PostScriptVersion V2 = new PostScriptVersion();
-
+ V2,
/** PostScript table version 3.0. */
- public static final PostScriptVersion V3 = new PostScriptVersion();
-
+ V3,
/** Unknown version of the PostScript table. */
- public static final PostScriptVersion UNKNOWN = new PostScriptVersion();
-
- private PostScriptVersion() { }
-
+ UNKNOWN;
}
/**
* Position inputstream to position indicated
* in the dirtab offset + offset
*/
- boolean seekTab(FontFileReader in, String name,
+ boolean seekTab(FontFileReader in, TTFTableName tableName,
long offset) throws IOException {
- TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
+ TTFDirTabEntry dt = dirTabs.get(tableName);
if (dt == null) {
- log.error("Dirtab " + name + " not found.");
+ log.error("Dirtab " + tableName.getName() + " not found.");
return false;
} else {
in.seekSet(dt.getOffset() + offset);
@@ -306,12 +307,12 @@ public class TTFFile {
* Set the unicodeIndex in the TTFMtxEntries and fills in the
* cmaps vector.
*/
- private boolean readCMAP(FontFileReader in) throws IOException {
+ private boolean readCMAP() throws IOException {
- unicodeMapping = new java.util.ArrayList();
+ unicodeMapping = new ArrayList<UnicodeMapping>();
- seekTab(in, "cmap", 2);
- int numCMap = in.readTTFUShort(); // Number of cmap subtables
+ seekTab(fontFile, TTFTableName.CMAP, 2);
+ int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables
long cmapUniOffset = 0;
long symbolMapOffset = 0;
@@ -321,9 +322,9 @@ public class TTFFile {
//Read offset for all tables. We are only interested in the unicode table
for (int i = 0; i < numCMap; i++) {
- int cmapPID = in.readTTFUShort();
- int cmapEID = in.readTTFUShort();
- long cmapOffset = in.readTTFULong();
+ int cmapPID = fontFile.readTTFUShort();
+ int cmapEID = fontFile.readTTFUShort();
+ long cmapOffset = fontFile.readTTFLong();
if (log.isDebugEnabled()) {
log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
@@ -338,9 +339,9 @@ public class TTFFile {
}
if (cmapUniOffset > 0) {
- return readUnicodeCmap(in, cmapUniOffset, 1);
+ return readUnicodeCmap(cmapUniOffset, 1);
} else if (symbolMapOffset > 0) {
- return readUnicodeCmap(in, symbolMapOffset, 0);
+ return readUnicodeCmap(symbolMapOffset, 0);
} else {
log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
+ " not present. Aborting");
@@ -349,26 +350,26 @@ public class TTFFile {
}
private boolean readUnicodeCmap // CSOK: MethodLength
- (FontFileReader in, long cmapUniOffset, int encodingID)
+ (long cmapUniOffset, int encodingID)
throws IOException {
//Read CMAP table and correct mtxTab.index
int mtxPtr = 0;
// Read unicode cmap
- seekTab(in, "cmap", cmapUniOffset);
- int cmapFormat = in.readTTFUShort();
- /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
+ seekTab(fontFile, TTFTableName.CMAP, cmapUniOffset);
+ int cmapFormat = fontFile.readTTFUShort();
+ /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length
if (log.isDebugEnabled()) {
log.debug("CMAP format: " + cmapFormat);
}
if (cmapFormat == 4) {
- in.skip(2); // Skip version number
- int cmapSegCountX2 = in.readTTFUShort();
- int cmapSearchRange = in.readTTFUShort();
- int cmapEntrySelector = in.readTTFUShort();
- int cmapRangeShift = in.readTTFUShort();
+ fontFile.skip(2); // Skip version number
+ int cmapSegCountX2 = fontFile.readTTFUShort();
+ int cmapSearchRange = fontFile.readTTFUShort();
+ int cmapEntrySelector = fontFile.readTTFUShort();
+ int cmapRangeShift = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("segCountX2 : " + cmapSegCountX2);
@@ -384,26 +385,26 @@ public class TTFFile {
int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapEndCounts[i] = in.readTTFUShort();
+ cmapEndCounts[i] = fontFile.readTTFUShort();
}
- in.skip(2); // Skip reservedPad
+ fontFile.skip(2); // Skip reservedPad
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapStartCounts[i] = in.readTTFUShort();
+ cmapStartCounts[i] = fontFile.readTTFUShort();
}
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapDeltas[i] = in.readTTFShort();
+ cmapDeltas[i] = fontFile.readTTFShort();
}
//int startRangeOffset = in.getCurrentPos();
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapRangeOffsets[i] = in.readTTFUShort();
+ cmapRangeOffsets[i] = fontFile.readTTFUShort();
}
- int glyphIdArrayOffset = in.getCurrentPos();
+ int glyphIdArrayOffset = fontFile.getCurrentPos();
BitSet eightBitGlyphs = new BitSet(256);
@@ -445,8 +446,8 @@ public class TTFFile {
+ (j - cmapStartCounts[i])
+ (i)
- cmapSegCountX2 / 2) * 2;
- in.seekSet(glyphOffset);
- glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+ fontFile.seekSet(glyphOffset);
+ glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
& 0xffff;
unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
@@ -465,11 +466,9 @@ public class TTFFile {
}
// Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
+ List<Integer> v = ansiIndex.get(new Integer(j));
if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
+ for (Integer aIdx : v) {
ansiWidth[aIdx.intValue()]
= mtxTab[glyphIdx].getWx();
@@ -510,11 +509,9 @@ public class TTFFile {
}
// Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
+ List<Integer> v = ansiIndex.get(new Integer(j));
if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
+ for (Integer aIdx : v) {
ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
}
}
@@ -592,14 +589,14 @@ public class TTFFile {
// Create an index hash to the ansiWidth
// Can't just index the winAnsiEncoding when inserting widths
// same char (eg bullet) is repeated more than one place
- ansiIndex = new java.util.HashMap();
+ ansiIndex = new HashMap<Integer, List<Integer>>();
for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
Integer ansi = new Integer(i);
Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]);
- List v = (List)ansiIndex.get(uni);
+ List<Integer> v = ansiIndex.get(uni);
if (v == null) {
- v = new java.util.ArrayList();
+ v = new ArrayList<Integer>();
ansiIndex.put(uni, v);
}
v.add(ansi);
@@ -618,12 +615,12 @@ public class TTFFile {
* @throws IOException In case of an I/O problem
*/
public boolean readFont(FontFileReader in, String name) throws IOException {
-
+ fontFile = in;
/*
* Check if TrueType collection, and that the name
* exists in the collection
*/
- if (!checkTTC(in, name)) {
+ if (!checkTTC(name)) {
if (name == null) {
throw new IllegalArgumentException(
"For TrueType collection you must specify which font "
@@ -634,26 +631,26 @@ public class TTFFile {
}
}
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
+ readDirTabs();
+ readFontHeader();
+ getNumGlyphs();
if (log.isDebugEnabled()) {
log.debug("Number of glyphs in font: " + numberOfGlyphs);
}
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
+ readHorizontalHeader();
+ readHorizontalMetrics();
initAnsiWidths();
- readPostScript(in);
- readOS2(in);
+ readPostScript();
+ readOS2();
determineAscDesc();
if (!isCFF) {
- readIndexToLocation(in);
- readGlyf(in);
+ readIndexToLocation();
+ readGlyf();
}
- readName(in);
- boolean pcltFound = readPCLT(in);
+ readName();
+ boolean pcltFound = readPCLT();
// Read cmap table and fill in ansiwidths
- boolean valid = readCMAP(in);
+ boolean valid = readCMAP();
if (!valid) {
return false;
}
@@ -661,38 +658,52 @@ public class TTFFile {
createCMaps();
// print_max_min();
- readKerning(in);
+ readKerning();
guessVerticalMetricsFromGlyphBBox();
return true;
}
+ /**
+ * Reads a font.
+ *
+ * @param in FontFileReader to read from
+ * @param name Name to be checked for in the font file
+ * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+ * new index as (Integer) value)
+ * @throws IOException in case of an I/O problem
+ */
+ public void readFont(FontFileReader in, String name,
+ Map<Integer, Integer> glyphs) throws IOException {
+ readFont(in, name);
+ }
+
private void createCMaps() {
- this.cmaps = new java.util.ArrayList<TTFCmapEntry>();
- TTFCmapEntry tce = new TTFCmapEntry();
+ cmaps = new ArrayList<BFEntry>();
+ int unicodeStart;
+ int glyphStart;
+ int unicodeEnd;
- Iterator e = unicodeMapping.listIterator();
- UnicodeMapping um = (UnicodeMapping)e.next();
+ Iterator<UnicodeMapping> e = unicodeMapping.listIterator();
+ UnicodeMapping um = e.next();
UnicodeMapping lastMapping = um;
- tce.setUnicodeStart(um.getUnicodeIndex());
- tce.setGlyphStartIndex(um.getGlyphIndex());
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
while (e.hasNext()) {
- um = (UnicodeMapping)e.next();
+ um = e.next();
if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
|| ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
- tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
- cmaps.add(tce);
-
- tce = new TTFCmapEntry();
- tce.setUnicodeStart(um.getUnicodeIndex());
- tce.setGlyphStartIndex(um.getGlyphIndex());
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
}
lastMapping = um;
}
- tce.setUnicodeEnd(um.getUnicodeIndex());
- cmaps.add(tce);
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
}
/**
@@ -715,7 +726,7 @@ public class TTFFile {
* Returns the font family names of the font.
* @return Set The family names (a Set of Strings)
*/
- public Set getFamilyNames() {
+ public Set<String> getFamilyNames() {
return familyNames;
}
@@ -760,19 +771,30 @@ public class TTFFile {
}
/**
+ * Returns the number of bytes necessary to pad the currentPosition so that a table begins
+ * on a 4-byte boundary.
+ * @param currentPosition the position to pad.
+ * @return int the number of bytes to pad.
+ */
+ protected int getPadSize(int currentPosition) {
+ int padSize = 4 - (currentPosition % 4);
+ return padSize < 4 ? padSize : 0;
+ }
+
+ /**
* Returns the Flags attribute of the font.
* @return int The Flags
*/
public int getFlags() {
int flags = 32; // Use Adobe Standard charset
if (italicAngle != 0) {
- flags = flags | 64;
+ flags |= 64;
}
if (isFixedPitch != 0) {
- flags = flags | 2;
+ flags |= 2;
}
if (hasSerifs) {
- flags = flags | 1;
+ flags |= 1;
}
return flags;
}
@@ -810,7 +832,6 @@ public class TTFFile {
}
/**
- * Returns the font bounding box.
* @return int[] The font bbox
*/
public int[] getFontBBox() {
@@ -916,11 +937,10 @@ public class TTFFile {
* FontFileReader and fill the global HashMap dirTabs
* with the table name (String) as key and a TTFDirTabEntry
* as value.
- * @param in FontFileReader to read the table directory from
* @throws IOException in case of an I/O problem
*/
- protected void readDirTabs(FontFileReader in) throws IOException {
- int sfntVersion = in.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
+ protected void readDirTabs() throws IOException {
+ int sfntVersion = fontFile.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
switch (sfntVersion) {
case 0x10000:
log.debug("sfnt version: OpenType 1.0");
@@ -939,42 +959,45 @@ public class TTFFile {
log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
break;
}
- int ntabs = in.readTTFUShort();
- in.skip(6); // 3xTTF_USHORT_SIZE
+ int ntabs = fontFile.readTTFUShort();
+ fontFile.skip(6); // 3xTTF_USHORT_SIZE
- dirTabs = new java.util.HashMap();
+ dirTabs = new HashMap<TTFTableName, TTFDirTabEntry>();
TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
log.debug("Reading " + ntabs + " dir tables");
+
for (int i = 0; i < ntabs; i++) {
pd[i] = new TTFDirTabEntry();
- dirTabs.put(pd[i].read(in), pd[i]);
+ String tableName = pd[i].read(fontFile);
+ dirTabs.put(TTFTableName.getValue(tableName), pd[i]);
}
+ dirTabs.put(TTFTableName.DIRECTORY_TABLE,
+ new TTFDirTabEntry(0L, fontFile.getCurrentPos()));
log.debug("dir tables: " + dirTabs.keySet());
}
/**
* Read the "head" table, this reads the bounding box and
* sets the upem (unitsPerEM) variable
- * @param in FontFileReader to read the header from
* @throws IOException in case of an I/O problem
*/
- protected void readFontHeader(FontFileReader in) throws IOException {
- seekTab(in, "head", 2 * 4 + 2 * 4);
- int flags = in.readTTFUShort();
+ protected void readFontHeader() throws IOException {
+ seekTab(fontFile, TTFTableName.HEAD, 2 * 4 + 2 * 4);
+ int flags = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("flags: " + flags + " - " + Integer.toString(flags, 2));
}
- upem = in.readTTFUShort();
+ upem = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("unit per em: " + upem);
}
- in.skip(16);
+ fontFile.skip(16);
- fontBBox1 = in.readTTFShort();
- fontBBox2 = in.readTTFShort();
- fontBBox3 = in.readTTFShort();
- fontBBox4 = in.readTTFShort();
+ fontBBox1 = fontFile.readTTFShort();
+ fontBBox2 = fontFile.readTTFShort();
+ fontBBox3 = fontFile.readTTFShort();
+ fontBBox4 = fontFile.readTTFShort();
if (log.isDebugEnabled()) {
log.debug("font bbox: xMin=" + fontBBox1
+ " yMin=" + fontBBox2
@@ -982,19 +1005,18 @@ public class TTFFile {
+ " yMax=" + fontBBox4);
}
- in.skip(2 + 2 + 2);
+ fontFile.skip(2 + 2 + 2);
- locaFormat = in.readTTFShort();
+ locaFormat = fontFile.readTTFShort();
}
/**
* Read the number of glyphs from the "maxp" table
- * @param in FontFileReader to read the number of glyphs from
* @throws IOException in case of an I/O problem
*/
- protected void getNumGlyphs(FontFileReader in) throws IOException {
- seekTab(in, "maxp", 4);
- numberOfGlyphs = in.readTTFUShort();
+ protected void getNumGlyphs() throws IOException {
+ seekTab(fontFile, TTFTableName.MAXP, 4);
+ numberOfGlyphs = fontFile.readTTFUShort();
}
@@ -1002,17 +1024,16 @@ public class TTFFile {
* Read the "hhea" table to find the ascender and descender and
* size of "hmtx" table, as a fixed size font might have only
* one width.
- * @param in FontFileReader to read the hhea table from
* @throws IOException in case of an I/O problem
*/
- protected void readHorizontalHeader(FontFileReader in)
+ protected void readHorizontalHeader()
throws IOException {
- seekTab(in, "hhea", 4);
- hheaAscender = in.readTTFShort();
- hheaDescender = in.readTTFShort();
+ seekTab(fontFile, TTFTableName.HHEA, 4);
+ hheaAscender = fontFile.readTTFShort();
+ hheaDescender = fontFile.readTTFShort();
- in.skip(2 + 2 + 3 * 2 + 8 * 2);
- nhmtx = in.readTTFUShort();
+ fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
+ nhmtx = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
@@ -1026,12 +1047,11 @@ public class TTFFile {
* in the mtxTab array. If the number of metrics is less
* than the number of glyphs (eg fixed size fonts), extend
* the mtxTab array and fill in the missing widths
- * @param in FontFileReader to read the hmtx table from
* @throws IOException in case of an I/O problem
*/
- protected void readHorizontalMetrics(FontFileReader in)
+ protected void readHorizontalMetrics()
throws IOException {
- seekTab(in, "hmtx", 0);
+ seekTab(fontFile, TTFTableName.HMTX, 0);
int mtxSize = Math.max(numberOfGlyphs, nhmtx);
mtxTab = new TTFMtxEntry[mtxSize];
@@ -1043,8 +1063,8 @@ public class TTFFile {
mtxTab[i] = new TTFMtxEntry();
}
for (int i = 0; i < nhmtx; i++) {
- mtxTab[i].setWx(in.readTTFUShort());
- mtxTab[i].setLsb(in.readTTFUShort());
+ mtxTab[i].setWx(fontFile.readTTFUShort());
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
if (TRACE_ENABLED) {
if (log.isDebugEnabled()) {
@@ -1059,7 +1079,7 @@ public class TTFFile {
int lastWidth = mtxTab[nhmtx - 1].getWx();
for (int i = nhmtx; i < mtxSize; i++) {
mtxTab[i].setWx(lastWidth);
- mtxTab[i].setLsb(in.readTTFUShort());
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
}
}
}
@@ -1069,16 +1089,16 @@ public class TTFFile {
* Read the "post" table
* containing the PostScript names of the glyphs.
*/
- private void readPostScript(FontFileReader in) throws IOException {
- seekTab(in, "post", 0);
- int postFormat = in.readTTFLong();
- italicAngle = in.readTTFULong();
- underlinePosition = in.readTTFShort();
- underlineThickness = in.readTTFShort();
- isFixedPitch = in.readTTFULong();
+ private void readPostScript() throws IOException {
+ seekTab(fontFile, TTFTableName.POST, 0);
+ int postFormat = fontFile.readTTFLong();
+ italicAngle = fontFile.readTTFULong();
+ underlinePosition = fontFile.readTTFShort();
+ underlineThickness = fontFile.readTTFShort();
+ isFixedPitch = fontFile.readTTFULong();
//Skip memory usage values
- in.skip(4 * 4);
+ fontFile.skip(4 * 4);
log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
switch (postFormat) {
@@ -1095,11 +1115,11 @@ public class TTFFile {
int numGlyphStrings = 0;
// Read Number of Glyphs
- int l = in.readTTFUShort();
+ int l = fontFile.readTTFUShort();
// Read indexes
for (int i = 0; i < l; i++) {
- mtxTab[i].setIndex(in.readTTFUShort());
+ mtxTab[i].setIndex(fontFile.readTTFUShort());
if (mtxTab[i].getIndex() > 257) {
//Index is not in the Macintosh standard set
@@ -1119,7 +1139,7 @@ public class TTFFile {
+ " set. Total number of glyphs=" + l);
}
for (int i = 0; i < psGlyphsBuffer.length; i++) {
- psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
+ psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
}
//Set glyph names
@@ -1156,60 +1176,60 @@ public class TTFFile {
/**
* Read the "OS/2" table
*/
- private void readOS2(FontFileReader in) throws IOException {
+ private void readOS2() throws IOException {
// Check if font is embeddable
- TTFDirTabEntry os2Entry = (TTFDirTabEntry)dirTabs.get("OS/2");
+ TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2);
if (os2Entry != null) {
- seekTab(in, "OS/2", 0);
- int version = in.readTTFUShort();
+ seekTab(fontFile, TTFTableName.OS2, 0);
+ int version = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("OS/2 table: version=" + version
+ ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
}
- in.skip(2); //xAvgCharWidth
- this.usWeightClass = in.readTTFUShort();
+ fontFile.skip(2); //xAvgCharWidth
+ this.usWeightClass = fontFile.readTTFUShort();
// usWidthClass
- in.skip(2);
+ fontFile.skip(2);
- int fsType = in.readTTFUShort();
+ int fsType = fontFile.readTTFUShort();
if (fsType == 2) {
isEmbeddable = false;
} else {
isEmbeddable = true;
}
- in.skip(11 * 2);
- in.skip(10); //panose array
- in.skip(4 * 4); //unicode ranges
- in.skip(4);
- in.skip(3 * 2);
+ fontFile.skip(11 * 2);
+ fontFile.skip(10); //panose array
+ fontFile.skip(4 * 4); //unicode ranges
+ fontFile.skip(4);
+ fontFile.skip(3 * 2);
int v;
- os2Ascender = in.readTTFShort(); //sTypoAscender
- os2Descender = in.readTTFShort(); //sTypoDescender
+ os2Ascender = fontFile.readTTFShort(); //sTypoAscender
+ os2Descender = fontFile.readTTFShort(); //sTypoDescender
if (log.isDebugEnabled()) {
log.debug("sTypoAscender: " + os2Ascender
+ " -> internal " + convertTTFUnit2PDFUnit(os2Ascender));
log.debug("sTypoDescender: " + os2Descender
+ " -> internal " + convertTTFUnit2PDFUnit(os2Descender));
}
- v = in.readTTFShort(); //sTypoLineGap
+ v = fontFile.readTTFShort(); //sTypoLineGap
if (log.isDebugEnabled()) {
log.debug("sTypoLineGap: " + v);
}
- v = in.readTTFUShort(); //usWinAscent
+ v = fontFile.readTTFUShort(); //usWinAscent
if (log.isDebugEnabled()) {
log.debug("usWinAscent: " + formatUnitsForDebug(v));
}
- v = in.readTTFUShort(); //usWinDescent
+ v = fontFile.readTTFUShort(); //usWinDescent
if (log.isDebugEnabled()) {
log.debug("usWinDescent: " + formatUnitsForDebug(v));
}
//version 1 OS/2 table might end here
if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) {
- in.skip(2 * 4);
- this.os2xHeight = in.readTTFShort(); //sxHeight
- this.os2CapHeight = in.readTTFShort(); //sCapHeight
+ fontFile.skip(2 * 4);
+ this.os2xHeight = fontFile.readTTFShort(); //sxHeight
+ this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight
if (log.isDebugEnabled()) {
log.debug("sxHeight: " + this.os2xHeight);
log.debug("sCapHeight: " + this.os2CapHeight);
@@ -1223,42 +1243,40 @@ public class TTFFile {
/**
* Read the "loca" table.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- protected final void readIndexToLocation(FontFileReader in)
+ protected final void readIndexToLocation()
throws IOException {
- if (!seekTab(in, "loca", 0)) {
+ if (!seekTab(fontFile, TTFTableName.LOCA, 0)) {
throw new IOException("'loca' table not found, happens when the font file doesn't"
+ " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
}
for (int i = 0; i < numberOfGlyphs; i++) {
- mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
+ mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
- lastLoca = (locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
+ lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
/**
* Read the "glyf" table to find the bounding boxes.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readGlyf(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
+ private void readGlyf() throws IOException {
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.GLYF);
if (dirTab == null) {
throw new IOException("glyf table not found, cannot continue");
}
for (int i = 0; i < (numberOfGlyphs - 1); i++) {
if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
- in.skip(2);
+ fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+ fontFile.skip(2);
final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
mtxTab[i].setBoundingBox(bbox);
} else {
mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
@@ -1266,17 +1284,17 @@ public class TTFFile {
}
- long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
+ long n = (dirTabs.get(TTFTableName.GLYF)).getOffset();
for (int i = 0; i < numberOfGlyphs; i++) {
if ((i + 1) >= mtxTab.length
|| mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(n + mtxTab[i].getOffset());
- in.skip(2);
+ fontFile.seekSet(n + mtxTab[i].getOffset());
+ fontFile.skip(2);
final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
mtxTab[i].setBoundingBox(bbox);
} else {
/**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
@@ -1297,34 +1315,33 @@ public class TTFFile {
/**
* Read the "name" table.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readName(FontFileReader in) throws IOException {
- seekTab(in, "name", 2);
- int i = in.getCurrentPos();
- int n = in.readTTFUShort();
- int j = in.readTTFUShort() + i - 2;
+ private void readName() throws IOException {
+ seekTab(fontFile, TTFTableName.NAME, 2);
+ int i = fontFile.getCurrentPos();
+ int n = fontFile.readTTFUShort();
+ int j = fontFile.readTTFUShort() + i - 2;
i += 2 * 2;
while (n-- > 0) {
// getLogger().debug("Iteration: " + n);
- in.seekSet(i);
- final int platformID = in.readTTFUShort();
- final int encodingID = in.readTTFUShort();
- final int languageID = in.readTTFUShort();
+ fontFile.seekSet(i);
+ final int platformID = fontFile.readTTFUShort();
+ final int encodingID = fontFile.readTTFUShort();
+ final int languageID = fontFile.readTTFUShort();
- int k = in.readTTFUShort();
- int l = in.readTTFUShort();
+ int k = fontFile.readTTFUShort();
+ int l = fontFile.readTTFUShort();
if (((platformID == 1 || platformID == 3)
&& (encodingID == 0 || encodingID == 1))) {
- in.seekSet(j + in.readTTFUShort());
+ fontFile.seekSet(j + fontFile.readTTFUShort());
String txt;
if (platformID == 3) {
- txt = in.readTTFString(l, encodingID);
+ txt = fontFile.readTTFString(l, encodingID);
} else {
- txt = in.readTTFString(l);
+ txt = fontFile.readTTFString(l);
}
if (log.isDebugEnabled()) {
@@ -1368,21 +1385,20 @@ public class TTFFile {
/**
* Read the "PCLT" table to find xHeight and capHeight.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private boolean readPCLT(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
+ private boolean readPCLT() throws IOException {
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT);
if (dirTab != null) {
- in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
- xHeight = in.readTTFUShort();
+ fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+ xHeight = fontFile.readTTFUShort();
log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
- in.skip(2 * 2);
- capHeight = in.readTTFUShort();
+ fontFile.skip(2 * 2);
+ capHeight = fontFile.readTTFUShort();
log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
- in.skip(2 + 16 + 8 + 6 + 1 + 1);
+ fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
- int serifStyle = in.readTTFUByte();
+ int serifStyle = fontFile.readTTFUByte();
serifStyle = serifStyle >> 6;
serifStyle = serifStyle & 3;
if (serifStyle == 1) {
@@ -1512,19 +1528,18 @@ public class TTFFile {
/**
* Read the kerning table, create a table for both CIDs and
* winAnsiEncoding.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readKerning(FontFileReader in) throws IOException {
+ private void readKerning() throws IOException {
// Read kerning
- kerningTab = new java.util.HashMap();
- ansiKerningTab = new java.util.HashMap();
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
+ kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN);
if (dirTab != null) {
- seekTab(in, "kern", 2);
- for (int n = in.readTTFUShort(); n > 0; n--) {
- in.skip(2 * 2);
- int k = in.readTTFUShort();
+ seekTab(fontFile, TTFTableName.KERN, 2);
+ for (int n = fontFile.readTTFUShort(); n > 0; n--) {
+ fontFile.skip(2 * 2);
+ int k = fontFile.readTTFUShort();
if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
return;
}
@@ -1532,12 +1547,12 @@ public class TTFFile {
continue;
}
- k = in.readTTFUShort();
- in.skip(3 * 2);
+ k = fontFile.readTTFUShort();
+ fontFile.skip(3 * 2);
while (k-- > 0) {
- int i = in.readTTFUShort();
- int j = in.readTTFUShort();
- int kpx = in.readTTFShort();
+ int i = fontFile.readTTFUShort();
+ int j = fontFile.readTTFUShort();
+ int kpx = fontFile.readTTFShort();
if (kpx != 0) {
// CID kerning table entry, using unicode indexes
final Integer iObj = glyphToUnicode(i);
@@ -1551,9 +1566,9 @@ public class TTFFile {
log.debug("Ignoring kerning pair because Unicode index was"
+ " found for the second glyph " + i);
} else {
- Map adjTab = kerningTab.get(iObj);
+ Map<Integer, Integer> adjTab = kerningTab.get(iObj);
if (adjTab == null) {
- adjTab = new java.util.HashMap();
+ adjTab = new HashMap<Integer, Integer>();
}
adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
kerningTab.put(iObj, adjTab);
@@ -1565,16 +1580,12 @@ public class TTFFile {
// Create winAnsiEncoded kerning table from kerningTab
// (could probably be simplified, for now we remap back to CID indexes and
// then to winAnsi)
- Iterator ae = kerningTab.keySet().iterator();
- while (ae.hasNext()) {
- Integer unicodeKey1 = (Integer)ae.next();
+ for (Integer unicodeKey1 : kerningTab.keySet()) {
Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
- Map<Integer, Integer> akpx = new java.util.HashMap();
- Map ckpx = kerningTab.get(unicodeKey1);
+ Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
+ Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
- Iterator aee = ckpx.keySet().iterator();
- while (aee.hasNext()) {
- Integer unicodeKey2 = (Integer)aee.next();
+ for (Integer unicodeKey2 : ckpx.keySet()) {
Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
Integer kern = (Integer)ckpx.get(unicodeKey2);
@@ -1603,10 +1614,70 @@ public class TTFFile {
}
/**
+ * Streams a font.
+ * @param ttfOut The interface for streaming True Type tables.
+ * @exception IOException file write error
+ */
+ public void stream(TTFOutputStream ttfOut) throws IOException {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs = sortDirTabMap(dirTabs);
+ byte[] file = fontFile.getAllBytes();
+ TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+ TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+ ttfOut.startFontStream();
+ for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
+ int offset = (int) entry.getValue().getOffset();
+ int paddedLength = (int) entry.getValue().getLength();
+ paddedLength += getPadSize(offset + paddedLength);
+ if (entry.getKey().equals(TTFTableName.GLYF)) {
+ streamGlyf(glyphOut, file, offset, paddedLength);
+ } else {
+ tableOut.streamTable(file, offset, paddedLength);
+ }
+ }
+ ttfOut.endFontStream();
+ }
+
+ private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset,
+ int tableLength) throws IOException {
+ //Stream all but the last glyph
+ int glyphStart = 0;
+ int glyphEnd = 0;
+ glyphOut.startGlyphStream();
+ for (int i = 0; i < mtxTab.length - 1; i++) {
+ glyphStart = (int) mtxTab[i].getOffset() + tableOffset;
+ glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset;
+ glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart);
+ }
+ glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd);
+ glyphOut.endGlyphStream();
+ }
+
+ /**
+ * This returns the order in which the tables in a truetype font should be written to file.
+ * @param directoryTabs the map that is to be sorted.
+ * @return TTFTablesNames[] an array of table names sorted in the order they should appear in
+ * the TTF file.
+ */
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>>
+ sortDirTabMap(Map<TTFTableName, TTFDirTabEntry> directoryTabs) {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedSet
+ = new TreeSet<Map.Entry<TTFTableName, TTFDirTabEntry>>(
+ new Comparator<Map.Entry<TTFTableName, TTFDirTabEntry>>() {
+
+ public int compare(Entry<TTFTableName, TTFDirTabEntry> o1,
+ Entry<TTFTableName, TTFDirTabEntry> o2) {
+ return (int) (o1.getValue().getOffset() - o2.getValue().getOffset());
+ }
+ });
+ sortedSet.addAll(directoryTabs.entrySet());
+ return sortedSet;
+ }
+
+ /**
* Return a List with TTFCmapEntry.
* @return A list of TTFCmapEntry objects
*/
- public List<TTFCmapEntry> getCMaps() {
+ public List<BFEntry> getCMaps() {
return cmaps;
}
@@ -1615,24 +1686,23 @@ public class TTFFile {
* name exists in the collection.
* If it does, set offset in fontfile to the beginning of
* the Table Directory for that font.
- * @param in FontFileReader to read from
* @param name The name to check
* @return True if not collection or font name present, false otherwise
* @throws IOException In case of an I/O problem
*/
- protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
- String tag = in.readTTFString(4);
+ protected final boolean checkTTC(String name) throws IOException {
+ String tag = fontFile.readTTFString(4);
if ("ttcf".equals(tag)) {
// This is a TrueType Collection
- in.skip(4);
+ fontFile.skip(4);
// Read directory offsets
- int numDirectories = (int)in.readTTFULong();
+ int numDirectories = (int)fontFile.readTTFULong();
// int numDirectories=in.readTTFUShort();
long[] dirOffsets = new long[numDirectories];
for (int i = 0; i < numDirectories; i++) {
- dirOffsets[i] = in.readTTFULong();
+ dirOffsets[i] = fontFile.readTTFULong();
}
log.info("This is a TrueType collection file with "
@@ -1646,10 +1716,10 @@ public class TTFFile {
// Is found, just to show all the names
long dirTabOffset = 0;
for (int i = 0; (i < numDirectories); i++) {
- in.seekSet(dirOffsets[i]);
- readDirTabs(in);
+ fontFile.seekSet(dirOffsets[i]);
+ readDirTabs();
- readName(in);
+ readName();
if (fullName.equals(name)) {
found = true;
@@ -1667,10 +1737,10 @@ public class TTFFile {
subFamilyName = "";
}
- in.seekSet(dirTabOffset);
+ fontFile.seekSet(dirTabOffset);
return found;
} else {
- in.seekSet(0);
+ fontFile.seekSet(0);
return true;
}
}
@@ -1682,8 +1752,7 @@ public class TTFFile {
* @throws IOException In case of an I/O problem
*/
public final List<String> getTTCnames(FontFileReader in) throws IOException {
- List<String> fontNames = new java.util.ArrayList<String>();
-
+ List<String> fontNames = new ArrayList<String>();
String tag = in.readTTFString(4);
if ("ttcf".equals(tag)) {
@@ -1705,9 +1774,9 @@ public class TTFFile {
for (int i = 0; (i < numDirectories); i++) {
in.seekSet(dirOffsets[i]);
- readDirTabs(in);
+ readDirTabs();
- readName(in);
+ readName();
log.debug(fullName);
fontNames.add(fullName);
@@ -1733,13 +1802,13 @@ public class TTFFile {
* doesn't matter...
*/
private Integer[] unicodeToWinAnsi(int unicode) {
- List ret = new java.util.ArrayList();
+ List<Integer> ret = new ArrayList<Integer>();
for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
if (unicode == Glyphs.WINANSI_ENCODING[i]) {
ret.add(new Integer(i));
}
}
- return (Integer[])ret.toArray(new Integer[0]);
+ return ret.toArray(new Integer[0]);
}
/**
@@ -1783,7 +1852,7 @@ public class TTFFile {
* @throws IOException if glyphIndex not found
*/
private Integer glyphToUnicode(int glyphIndex) throws IOException {
- return (Integer) glyphToUnicodeMap.get(new Integer(glyphIndex));
+ return glyphToUnicodeMap.get(new Integer(glyphIndex));
}
/**
@@ -1795,7 +1864,7 @@ public class TTFFile {
*/
private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
final Integer result
- = (Integer) unicodeToGlyphMap.get(new Integer(unicodeIndex));
+ = unicodeToGlyphMap.get(new Integer(unicodeIndex));
if (result == null) {
throw new IOException(
"Glyph index not found for unicode value " + unicodeIndex);
@@ -1830,4 +1899,4 @@ public class TTFFile {
ioe.printStackTrace(System.err);
}
}
-} \ No newline at end of file
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
index dd8cb27a4..7a1a7e5c8 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
@@ -21,15 +21,13 @@ 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 java.util.Set;
import org.apache.commons.io.IOUtils;
-
import org.apache.fop.fonts.BFEntry;
import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.EncodingMode;
import org.apache.fop.fonts.FontLoader;
import org.apache.fop.fonts.FontResolver;
@@ -49,6 +47,7 @@ public class TTFFontLoader extends FontLoader {
private SingleByteFont singleFont;
private final String subFontName;
private EncodingMode encodingMode;
+ private EmbeddingMode embeddingMode;
/**
* Default constructor
@@ -56,7 +55,7 @@ public class TTFFontLoader extends FontLoader {
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, FontResolver resolver) {
- this(fontFileURI, null, true, EncodingMode.AUTO, true, resolver);
+ this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, resolver);
}
/**
@@ -65,23 +64,27 @@ public class TTFFontLoader extends FontLoader {
* @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal
* TrueType fonts)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param useKerning true to enable loading kerning info if available, false to disable
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, boolean useKerning,
- FontResolver resolver) {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ boolean useKerning, FontResolver resolver) {
super(fontFileURI, embedded, true, resolver);
this.subFontName = subFontName;
this.encodingMode = encodingMode;
+ this.embeddingMode = embeddingMode;
if (this.encodingMode == EncodingMode.AUTO) {
this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType
}
+ if (this.embeddingMode == EmbeddingMode.AUTO) {
+ this.embeddingMode = EmbeddingMode.SUBSET;
+ }
}
/** {@inheritDoc} */
- @Override
protected void read() throws IOException {
read(this.subFontName);
}
@@ -144,7 +147,7 @@ public class TTFFontLoader extends FontLoader {
returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle()));
returnFont.setMissingWidth(0);
returnFont.setWeight(ttf.getWeightClass());
-
+ returnFont.setEmbeddingMode(this.embeddingMode);
if (isCid) {
multiFont.setCIDType(CIDFontType.CIDTYPE2);
int[] wx = ttf.getWidths();
@@ -168,15 +171,8 @@ public class TTFFontLoader extends FontLoader {
}
private BFEntry[] getCMap(TTFFile ttf) {
- List<TTFCmapEntry> entries = ttf.getCMaps();
- BFEntry[] bfentries = new BFEntry[entries.size()];
- int pos = 0;
- for (TTFCmapEntry ce : ttf.getCMaps()) {
- bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(),
- ce.getGlyphStartIndex());
- pos++;
- }
- return bfentries;
+ BFEntry[] array = new BFEntry[ttf.getCMaps().size()];
+ return ttf.getCMaps().toArray(array);
}
private void copyWidthsSingleByte(TTFFile ttf) {
@@ -184,9 +180,8 @@ public class TTFFontLoader extends FontLoader {
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
singleFont.setWidth(i, ttf.getCharWidth(i));
}
- Iterator iter = ttf.getCMaps().listIterator();
- while (iter.hasNext()) {
- TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+
+ for (BFEntry ce : ttf.getCMaps()) {
if (ce.getUnicodeStart() < 0xFFFE) {
for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) {
int codePoint = singleFont.getEncoding().mapChar(u);
@@ -221,7 +216,6 @@ public class TTFFontLoader extends FontLoader {
}
for (Integer kpx1 : kerningSet) {
-
Map<Integer, Integer> h2;
if (isCid) {
h2 = ttf.getKerning().get(kpx1);
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
new file mode 100644
index 000000000..ee3101f9b
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * This is an interface for streaming individual glyphs from the glyf table in a True Type font.
+ */
+public interface TTFGlyphOutputStream {
+ /**
+ * Begins the streaming of glyphs.
+ * @throws IOException file write exception
+ */
+ void startGlyphStream() throws IOException;
+
+ /**
+ * Streams an individual glyph at offset from a byte array.
+ * @param byteArray byte[] the font byte array.
+ * @param offset int the starting position to stream from.
+ * @param length int the number of bytes to stream.
+ * @throws IOException file write exception.
+ */
+ void streamGlyph(byte[] byteArray, int offset, int length) throws IOException;
+
+ /**
+ * Ends the streaming of glyphs.
+ * @throws IOException file write exception.
+ */
+ void endGlyphStream() throws IOException;
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
new file mode 100644
index 000000000..8fb8a5cc7
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * This is an interface for streaming True Type font.
+ */
+public interface TTFOutputStream {
+ /**
+ * Starts writing the font to file.
+ * @throws IOException file write exception.
+ */
+ void startFontStream() throws IOException;
+
+ /**
+ * Returns an object for streaming True Type tables.
+ * @return {@link TTFTableOutputStream}
+ */
+ TTFTableOutputStream getTableOutputStream();
+
+ /**
+ * Returns an object for streaming True Type glyphs in the glyf table.
+ * @return {@link TTFGlyphOutputStream}
+ */
+ TTFGlyphOutputStream getGlyphOutputStream();
+
+ /**
+ * Ends writing the font to file.
+ * @throws IOException file write exception.
+ */
+ void endFontStream() throws IOException;
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
index cf3f55b7d..5bd93ce81 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
@@ -20,8 +20,10 @@
package org.apache.fop.fonts.truetype;
import java.io.IOException;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.SortedSet;
/**
@@ -34,10 +36,6 @@ import java.util.Map;
*/
public class TTFSubSetFile extends TTFFile {
- private static enum OperatingMode {
- PDF, POSTSCRIPT_GLYPH_DIRECTORY;
- }
-
private byte[] output = null;
private int realSize = 0;
private int currentPos = 0;
@@ -46,27 +44,25 @@ public class TTFSubSetFile extends TTFFile {
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
- private Map<String, Integer> offsets = new java.util.HashMap<String, Integer>();
- private int glyfDirOffset = 0;
- private int headDirOffset = 0;
- private int hmtxDirOffset = 0;
- private int locaDirOffset = 0;
- private int maxpDirOffset = 0;
+ private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>();
private int checkSumAdjustmentOffset = 0;
private int locaOffset = 0;
- private int determineTableCount(OperatingMode operatingMode) {
- int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
+ /** Stores the glyph offsets so that we can end strings at glyph boundaries */
+ private int[] glyphOffsets;
+
+ /** The dir tab entries in the new subset font. */
+ private Map<TTFTableName, TTFDirTabEntry> newDirTabs
+ = new HashMap<TTFTableName, TTFDirTabEntry>();
+
+ private int determineTableCount() {
+ int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp,
if (isCFF()) {
throw new UnsupportedOperationException(
"OpenType fonts with CFF glyphs are not supported");
} else {
- if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
- numTables++; //1 table: gdir
- } else {
- numTables += 2; //2 req'd tables: glyf,loca
- }
+ numTables += 5; //5 req'd tables: glyf,loca,post,name,OS/2
if (hasCvt()) {
numTables++;
}
@@ -83,8 +79,8 @@ public class TTFSubSetFile extends TTFFile {
/**
* Create the directory table
*/
- private void createDirectory(OperatingMode operatingMode) {
- int numTables = determineTableCount(operatingMode);
+ private void createDirectory() {
+ int numTables = determineTableCount();
// Create the TrueType header
writeByte((byte)0);
writeByte((byte)1);
@@ -97,7 +93,7 @@ public class TTFSubSetFile extends TTFFile {
// Create searchRange, entrySelector and rangeShift
int maxPow = maxPow2(numTables);
- int searchRange = maxPow * 16;
+ int searchRange = (int) Math.pow(2, maxPow) * 16;
writeUShort(searchRange);
realSize += 2;
@@ -106,83 +102,47 @@ public class TTFSubSetFile extends TTFFile {
writeUShort((numTables * 16) - searchRange);
realSize += 2;
+ // Create space for the table entries (these must be in ASCII alphabetical order[A-Z]then[a-z])
+ writeTableName(TTFTableName.OS2);
- // Create space for the table entries
if (hasCvt()) {
- writeString("cvt ");
- offsets.put("cvt ", currentPos);
- currentPos += 12;
- realSize += 16;
+ writeTableName(TTFTableName.CVT);
}
-
if (hasFpgm()) {
- writeString("fpgm");
- offsets.put("fpgm", currentPos);
- currentPos += 12;
- realSize += 16;
- }
-
- if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
- writeString("glyf");
- glyfDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
- }
-
- writeString("head");
- headDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("hhea");
- offsets.put("hhea", currentPos);
- currentPos += 12;
- realSize += 16;
-
- writeString("hmtx");
- hmtxDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
- writeString("loca");
- locaDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
+ writeTableName(TTFTableName.FPGM);
+ }
+ writeTableName(TTFTableName.GLYF);
+ writeTableName(TTFTableName.HEAD);
+ writeTableName(TTFTableName.HHEA);
+ writeTableName(TTFTableName.HMTX);
+ writeTableName(TTFTableName.LOCA);
+ writeTableName(TTFTableName.MAXP);
+ writeTableName(TTFTableName.NAME);
+ writeTableName(TTFTableName.POST);
+ if (hasPrep()) {
+ writeTableName(TTFTableName.PREP);
}
+ newDirTabs.put(TTFTableName.DIRECTORY_TABLE, new TTFDirTabEntry(0, currentPos));
+ }
- writeString("maxp");
- maxpDirOffset = currentPos;
+ private void writeTableName(TTFTableName tableName) {
+ writeString(tableName.getName());
+ offsets.put(tableName, currentPos);
currentPos += 12;
realSize += 16;
-
- if (hasPrep()) {
- writeString("prep");
- offsets.put("prep", currentPos);
- currentPos += 12;
- realSize += 16;
- }
-
- if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
- //"gdir" indicates to the PostScript interpreter that the GlyphDirectory approach
- //is in use.
- writeString("gdir");
- currentPos += 12;
- realSize += 16;
- }
}
private boolean hasCvt() {
- return dirTabs.containsKey("cvt ");
+ return dirTabs.containsKey(TTFTableName.CVT);
}
private boolean hasFpgm() {
- return dirTabs.containsKey("fpgm");
+ return dirTabs.containsKey(TTFTableName.FPGM);
}
private boolean hasPrep() {
- return dirTabs.containsKey("prep");
+ return dirTabs.containsKey(TTFTableName.PREP);
}
/**
@@ -191,26 +151,24 @@ public class TTFSubSetFile extends TTFFile {
private void createLoca(int size) throws IOException {
pad4();
locaOffset = currentPos;
- writeULong(locaDirOffset + 4, currentPos);
- writeULong(locaDirOffset + 8, size * 4 + 4);
+ int dirTableOffset = offsets.get(TTFTableName.LOCA);
+ writeULong(dirTableOffset + 4, currentPos);
+ writeULong(dirTableOffset + 8, size * 4 + 4);
currentPos += size * 4 + 4;
realSize += size * 4 + 4;
}
- private boolean copyTable(FontFileReader in, String tableName) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get(tableName);
+ private boolean copyTable(FontFileReader in, TTFTableName tableName) throws IOException {
+ TTFDirTabEntry entry = dirTabs.get(tableName);
if (entry != null) {
pad4();
seekTab(in, tableName, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- int offset = offsets.get(tableName);
- writeULong(offset, checksum);
- writeULong(offset + 4, currentPos);
- writeULong(offset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
+
+ updateCheckSum(currentPos, (int) entry.getLength(), tableName);
+ currentPos += (int) entry.getLength();
+ realSize += (int) entry.getLength();
return true;
} else {
return false;
@@ -221,14 +179,34 @@ public class TTFSubSetFile extends TTFFile {
* Copy the cvt table as is from original font to subset font
*/
private boolean createCvt(FontFileReader in) throws IOException {
- return copyTable(in, "cvt ");
+ return copyTable(in, TTFTableName.CVT);
}
/**
* Copy the fpgm table as is from original font to subset font
*/
private boolean createFpgm(FontFileReader in) throws IOException {
- return copyTable(in, "fpgm");
+ return copyTable(in, TTFTableName.FPGM);
+ }
+
+ /**
+ * Copy the name table as is from the original.
+ * @param in FontFileReader
+ * @return boolean
+ * @throws IOException exception
+ */
+ private boolean createName(FontFileReader in) throws IOException {
+ return copyTable(in, TTFTableName.NAME);
+ }
+
+ /**
+ * Copy the OS/2 table as is from the original.
+ * @param in
+ * @return
+ * @throws IOException
+ */
+ private boolean createOS2(FontFileReader in) throws IOException {
+ return copyTable(in, TTFTableName.OS2);
}
/**
@@ -236,18 +214,16 @@ public class TTFSubSetFile extends TTFFile {
* and set num glyphs to size
*/
private void createMaxp(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
+ TTFTableName maxp = TTFTableName.MAXP;
+ TTFDirTabEntry entry = dirTabs.get(maxp);
if (entry != null) {
pad4();
- seekTab(in, "maxp", 0);
+ seekTab(in, maxp, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
writeUShort(currentPos + 4, size);
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(maxpDirOffset, checksum);
- writeULong(maxpDirOffset + 4, currentPos);
- writeULong(maxpDirOffset + 8, (int)entry.getLength());
+ updateCheckSum(currentPos, (int)entry.getLength(), maxp);
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
@@ -255,12 +231,34 @@ public class TTFSubSetFile extends TTFFile {
}
}
+ private void createPost(FontFileReader in) throws IOException {
+ TTFTableName post = TTFTableName.POST;
+ TTFDirTabEntry entry = dirTabs.get(post);
+ if (entry != null) {
+ pad4();
+ seekTab(in, post, 0);
+ int newTableSize = 32; // This is the post table size with glyphs truncated
+ byte[] newPostTable = new byte[newTableSize];
+ // We only want the first 28 bytes (truncate the glyph names);
+ System.arraycopy(in.getBytes((int) entry.getOffset(), newTableSize),
+ 0, newPostTable, 0, newTableSize);
+ // set the post table to Format 3.0
+ newPostTable[1] = 0x03;
+ System.arraycopy(newPostTable, 0, output, currentPos, newTableSize);
+ updateCheckSum(currentPos, newTableSize, post);
+ currentPos += newTableSize;
+ realSize += newTableSize;
+ } else {
+ throw new IOException("Can't find post table");
+ }
+ }
+
/**
* Copy the prep table as is from original font to subset font
*/
private boolean createPrep(FontFileReader in) throws IOException {
- return copyTable(in, "prep");
+ return copyTable(in, TTFTableName.PREP);
}
@@ -269,8 +267,18 @@ public class TTFSubSetFile extends TTFFile {
* and fill in size of hmtx table
*/
private void createHhea(FontFileReader in, int size) throws IOException {
- boolean copied = copyTable(in, "hhea");
- if (!copied) {
+ TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA);
+ if (entry != null) {
+ pad4();
+ seekTab(in, TTFTableName.HHEA, 0);
+ System.arraycopy(in.getBytes((int) entry.getOffset(), (int) entry.getLength()), 0,
+ output, currentPos, (int) entry.getLength());
+ writeUShort((int) entry.getLength() + currentPos - 2, size);
+
+ updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA);
+ currentPos += (int) entry.getLength();
+ realSize += (int) entry.getLength();
+ } else {
throw new IOException("Can't find hhea table");
}
}
@@ -283,10 +291,11 @@ public class TTFSubSetFile extends TTFFile {
* in checkSumAdjustmentOffset
*/
private void createHead(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+ TTFTableName head = TTFTableName.HEAD;
+ TTFDirTabEntry entry = dirTabs.get(head);
if (entry != null) {
pad4();
- seekTab(in, "head", 0);
+ seekTab(in, head, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
@@ -298,11 +307,7 @@ public class TTFSubSetFile extends TTFFile {
output[currentPos + 50] = 0; // long locaformat
output[currentPos + 51] = 1; // long locaformat
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(headDirOffset, checksum);
- writeULong(headDirOffset + 4, currentPos);
- writeULong(headDirOffset + 8, (int)entry.getLength());
-
+ updateCheckSum(currentPos, (int)entry.getLength(), head);
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
@@ -315,8 +320,9 @@ public class TTFSubSetFile extends TTFFile {
* Create the glyf table and fill in loca table
*/
private void createGlyf(FontFileReader in,
- Map<Integer, Integer> glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ Map<Integer, Integer> glyphs) throws IOException {
+ TTFTableName glyf = TTFTableName.GLYF;
+ TTFDirTabEntry entry = dirTabs.get(glyf);
int size = 0;
int startPos = 0;
int endOffset = 0; // Store this as the last loca
@@ -329,6 +335,7 @@ public class TTFSubSetFile extends TTFFile {
* location offset.
*/
int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+ glyphOffsets = new int[origIndexes.length];
for (int i = 0; i < origIndexes.length; i++) {
int nextOffset = 0;
@@ -358,27 +365,30 @@ public class TTFSubSetFile extends TTFFile {
endOffset1 = (currentPos - startPos + glyphLength);
}
+ // Store the glyph boundary positions relative to the start the font
+ glyphOffsets[i] = currentPos;
currentPos += glyphLength;
realSize += glyphLength;
- endOffset = endOffset1;
+ endOffset = endOffset1;
}
+
size = currentPos - startPos;
- int checksum = getCheckSum(startPos, size);
- writeULong(glyfDirOffset, checksum);
- writeULong(glyfDirOffset + 4, startPos);
- writeULong(glyfDirOffset + 8, size);
currentPos += 12;
realSize += 12;
+ updateCheckSum(startPos, size + 12, glyf);
// Update loca checksum and last loca index
writeULong(locaOffset + glyphs.size() * 4, endOffset);
-
- checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
- writeULong(locaDirOffset, checksum);
+ int locaSize = glyphs.size() * 4 + 4;
+ int checksum = getCheckSum(output, locaOffset, locaSize);
+ writeULong(offsets.get(TTFTableName.LOCA), checksum);
+ int padSize = (locaOffset + locaSize) % 4;
+ newDirTabs.put(TTFTableName.LOCA,
+ new TTFDirTabEntry(locaOffset, locaSize + padSize));
} else {
throw new IOException("Can't find glyf table");
}
@@ -402,7 +412,8 @@ public class TTFSubSetFile extends TTFFile {
*/
private void createHmtx(FontFileReader in,
Map<Integer, Integer> glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+ TTFTableName hmtx = TTFTableName.HMTX;
+ TTFDirTabEntry entry = dirTabs.get(hmtx);
int longHorMetricSize = glyphs.size() * 2;
int leftSideBearingSize = glyphs.size() * 2;
@@ -421,10 +432,7 @@ public class TTFSubSetFile extends TTFFile {
mtxTab[origIndex.intValue()].getLsb());
}
- int checksum = getCheckSum(currentPos, hmtxSize);
- writeULong(hmtxDirOffset, checksum);
- writeULong(hmtxDirOffset + 4, currentPos);
- writeULong(hmtxDirOffset + 8, hmtxSize);
+ updateCheckSum(currentPos, hmtxSize, hmtx);
currentPos += hmtxSize;
realSize += hmtxSize;
} else {
@@ -540,16 +548,16 @@ public class TTFSubSetFile extends TTFFile {
*/
private void scanGlyphs(FontFileReader in,
Map<Integer, Integer> glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ TTFDirTabEntry entry = dirTabs.get(TTFTableName.GLYF);
Map<Integer, Integer> newComposites = null;
- Map<Integer, Integer> allComposites = new java.util.HashMap<Integer, Integer>();
+ Map<Integer, Integer> allComposites = new HashMap<Integer, Integer>();
int newIndex = glyphs.size();
if (entry != null) {
while (newComposites == null || newComposites.size() > 0) {
// Inefficient to iterate through all glyphs
- newComposites = new java.util.HashMap<Integer, Integer>();
+ newComposites = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
int origIndex = glyph.getKey();
@@ -591,46 +599,38 @@ public class TTFSubSetFile extends TTFFile {
}
}
-
-
/**
- * Returns a subset of the original font.
+ * Reads a font and creates a subset of the font.
*
* @param in FontFileReader to read from
* @param name Name to be checked for in the font file
* @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
* new index as (Integer) value)
- * @return A subset of the original font
* @throws IOException in case of an I/O problem
*/
- public byte[] readFont(FontFileReader in, String name,
+ public void readFont(FontFileReader in, String name,
Map<Integer, Integer> glyphs) throws IOException {
-
+ fontFile = in;
//Check if TrueType collection, and that the name exists in the collection
- if (!checkTTC(in, name)) {
+ if (!checkTTC(name)) {
throw new IOException("Failed to read font");
}
//Copy the Map as we're going to modify it
- Map<Integer, Integer> subsetGlyphs = new java.util.HashMap<Integer, Integer>(glyphs);
+ Map<Integer, Integer> subsetGlyphs = new HashMap<Integer, Integer>(glyphs);
output = new byte[in.getFileSize()];
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
- readIndexToLocation(in);
+ readDirTabs();
+ readFontHeader();
+ getNumGlyphs();
+ readHorizontalHeader();
+ readHorizontalMetrics();
+ readIndexToLocation();
scanGlyphs(in, subsetGlyphs);
- createDirectory(OperatingMode.PDF); // Create the TrueType header and directory
-
- createHead(in);
- createHhea(in, subsetGlyphs.size()); // Create the hhea table
- createHmtx(in, subsetGlyphs); // Create hmtx table
- createMaxp(in, subsetGlyphs.size()); // copy the maxp table
+ createDirectory(); // Create the TrueType header and directory
boolean optionalTableFound;
optionalTableFound = createCvt(in); // copy the cvt table
@@ -644,78 +644,16 @@ public class TTFSubSetFile extends TTFFile {
// fpgm is optional (used in TrueType fonts only)
log.debug("TrueType: fpgm table not present. Skipped.");
}
-
- optionalTableFound = createPrep(in); // copy prep table
- if (!optionalTableFound) {
- // prep is optional (used in TrueType fonts only)
- log.debug("TrueType: prep table not present. Skipped.");
- }
-
createLoca(subsetGlyphs.size()); // create empty loca table
- createGlyf(in, subsetGlyphs); //create glyf table and update loca table
-
- pad4();
- createCheckSumAdjustment();
-
- byte[] ret = new byte[realSize];
- System.arraycopy(output, 0, ret, 0, realSize);
-
- return ret;
- }
-
- /**
- * Returns a subset of the original font suitable for use in PostScript programs.
- *
- * @param in FontFileReader to read from
- * @param name Name to be checked for in the font file
- * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
- * new index as (Integer) value)
- * @param glyphHandler the handler to receive all glyphs of the subset
- * @return A subset of the original font
- * @throws IOException in case of an I/O problem
- */
- public byte[] toPostScriptSubset(FontFileReader in, String name,
- Map glyphs, GlyphHandler glyphHandler) throws IOException {
-
- //Check if TrueType collection, and that the name exists in the collection
- if (!checkTTC(in, name)) {
- throw new IOException("Failed to read font");
- }
-
- //Copy the Map as we're going to modify it
- Map<Integer, Integer> subsetGlyphs = new java.util.HashMap(glyphs);
-
- output = new byte[in.getFileSize()];
-
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
- readIndexToLocation(in);
-
- scanGlyphs(in, subsetGlyphs);
-
- // Create the TrueType header and directory
- createDirectory(OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY);
+ createGlyf(in, subsetGlyphs); //create glyf table and update loca table
+ createOS2(in); // copy the OS/2 table
createHead(in);
createHhea(in, subsetGlyphs.size()); // Create the hhea table
createHmtx(in, subsetGlyphs); // Create hmtx table
createMaxp(in, subsetGlyphs.size()); // copy the maxp table
-
- boolean optionalTableFound;
- optionalTableFound = createCvt(in); // copy the cvt table
- if (!optionalTableFound) {
- // cvt is optional (used in TrueType fonts only)
- log.debug("TrueType: ctv table not present. Skipped.");
- }
-
- optionalTableFound = createFpgm(in); // copy fpgm table
- if (!optionalTableFound) {
- // fpgm is optional (used in TrueType fonts only)
- log.debug("TrueType: fpgm table not present. Skipped.");
- }
+ createName(in); // copy the name table
+ createPost(in); // copy the post table
optionalTableFound = createPrep(in); // copy prep table
if (!optionalTableFound) {
@@ -723,59 +661,54 @@ public class TTFSubSetFile extends TTFFile {
log.debug("TrueType: prep table not present. Skipped.");
}
- //Send all the glyphs from the subset
- handleGlyphSubset(in, subsetGlyphs, glyphHandler);
-
pad4();
createCheckSumAdjustment();
+ }
+ /**
+ * Returns a subset of the fonts (readFont() MUST be called first in order to create the
+ * subset).
+ * @return byte array
+ */
+ public byte[] getFontSubset() {
byte[] ret = new byte[realSize];
System.arraycopy(output, 0, ret, 0, realSize);
-
return ret;
}
- private void handleGlyphSubset(FontFileReader in, Map<Integer, Integer> glyphs,
- GlyphHandler glyphHandler) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
- if (entry != null) {
-
- int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
-
- for (int i = 0; i < origIndexes.length; i++) {
- int nextOffset = 0;
- int origGlyphIndex = origIndexes[i];
- if (origGlyphIndex >= (mtxTab.length - 1)) {
- nextOffset = (int)lastLoca;
- } else {
- nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
- }
- int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset();
- int glyphLength = nextOffset - glyphOffset;
-
- byte[] glyphData = in.getBytes(
- (int)entry.getOffset() + glyphOffset,
- glyphLength);
-
- glyphHandler.addGlyph(glyphData);
+ private void handleGlyphSubset(TTFGlyphOutputStream glyphOut) throws IOException {
+ glyphOut.startGlyphStream();
+ // Stream all but the last glyph
+ for (int i = 0; i < glyphOffsets.length - 1; i++) {
+ glyphOut.streamGlyph(output, glyphOffsets[i],
+ glyphOffsets[i + 1] - glyphOffsets[i]);
+ }
+ // Stream the last glyph
+ TTFDirTabEntry glyf = newDirTabs.get(TTFTableName.GLYF);
+ long lastGlyphLength = glyf.getLength()
+ - (glyphOffsets[glyphOffsets.length - 1] - glyf.getOffset());
+ glyphOut.streamGlyph(output, glyphOffsets[glyphOffsets.length - 1],
+ (int) lastGlyphLength);
+ glyphOut.endGlyphStream();
+ }
+
+ @Override
+ public void stream(TTFOutputStream ttfOut) throws IOException {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs
+ = sortDirTabMap(newDirTabs);
+ TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+ TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+
+ ttfOut.startFontStream();
+ for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
+ if (entry.getKey().equals(TTFTableName.GLYF)) {
+ handleGlyphSubset(glyphOut);
+ } else {
+ tableOut.streamTable(output, (int) entry.getValue().getOffset(),
+ (int) entry.getValue().getLength());
}
- } else {
- throw new IOException("Can't find glyf table");
}
- }
-
- /**
- * Used as callback to handle a number of glyphs.
- */
- public static interface GlyphHandler {
-
- /**
- * Adds a glyph.
- * @param glyphData the glyph data
- * @throws IOException if an I/O error occurs
- */
- void addGlyph(byte[] glyphData) throws IOException;
-
+ ttfOut.endFontStream();
}
/**
@@ -827,20 +760,6 @@ public class TTFSubSetFile extends TTFFile {
output[pos + 1] = b2;
}
- /**
- * Appends a ULONG to the output array,
- * updates currentPos but not realSize
- */
- private void writeULong(int s) {
- byte b1 = (byte)((s >> 24) & 0xff);
- byte b2 = (byte)((s >> 16) & 0xff);
- byte b3 = (byte)((s >> 8) & 0xff);
- byte b4 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- writeByte(b3);
- writeByte(b4);
- }
/**
* Appends a ULONG to the output array,
@@ -858,40 +777,16 @@ public class TTFSubSetFile extends TTFFile {
}
/**
- * Read a signed short value at given position
- */
- private short readShort(int pos) {
- int ret = readUShort(pos);
- return (short)ret;
- }
-
- /**
- * Read a unsigned short value at given position
- */
- private int readUShort(int pos) {
- int ret = output[pos];
- if (ret < 0) {
- ret += 256;
- }
- ret = ret << 8;
- if (output[pos + 1] < 0) {
- ret |= output[pos + 1] + 256;
- } else {
- ret |= output[pos + 1];
- }
-
- return ret;
- }
-
- /**
* Create a padding in the fontfile to align
* on a 4-byte boundary
*/
private void pad4() {
- int padSize = currentPos % 4;
- for (int i = 0; i < padSize; i++) {
- output[currentPos++] = 0;
- realSize++;
+ int padSize = getPadSize(currentPos);
+ if (padSize < 4) {
+ for (int i = 0; i < padSize; i++) {
+ output[currentPos++] = 0;
+ realSize++;
+ }
}
}
@@ -900,23 +795,25 @@ public class TTFSubSetFile extends TTFFile {
*/
private int maxPow2(int max) {
int i = 0;
- while (Math.pow(2, i) < max) {
+ while (Math.pow(2, i) <= max) {
i++;
}
return (i - 1);
}
- private int log2(int num) {
- return (int)(Math.log(num) / Math.log(2));
- }
-
- private int getCheckSum(int start, int size) {
- return (int)getLongCheckSum(output, start, size);
+ private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) {
+ int checksum = getCheckSum(output, tableStart, tableSize);
+ int offset = offsets.get(tableName);
+ int padSize = getPadSize(tableStart + tableSize);
+ newDirTabs.put(tableName, new TTFDirTabEntry(tableStart, tableSize + padSize));
+ writeULong(offset, checksum);
+ writeULong(offset + 4, tableStart);
+ writeULong(offset + 8, tableSize);
}
- private static long getLongCheckSum(byte[] data, int start, int size) {
+ private static int getCheckSum(byte[] data, int start, int size) {
// All the tables here are aligned on four byte boundaries
// Add remainder to size if it's not a multiple of 4
int remainder = size % 4;
@@ -927,26 +824,19 @@ public class TTFSubSetFile extends TTFFile {
long sum = 0;
for (int i = 0; i < size; i += 4) {
- int l = (data[start + i] << 24);
- l += (data[start + i + 1] << 16);
- l += (data[start + i + 2] << 16);
- l += (data[start + i + 3] << 16);
- sum += l;
- if (sum > 0xffffffff) {
- sum = sum - 0xffffffff;
+ long l = 0;
+ for (int j = 0; j < 4; j++) {
+ l <<= 8;
+ l |= data[start + i + j] & 0xff;
}
+ sum += l;
}
-
- return sum;
+ return (int) sum;
}
private void createCheckSumAdjustment() {
- long sum = getLongCheckSum(output, 0, realSize);
+ long sum = getCheckSum(output, 0, realSize);
int checksum = (int)(0xb1b0afba - sum);
writeULong(checkSumAdjustmentOffset, checksum);
}
-
}
-
-
-
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
new file mode 100644
index 000000000..e8e66a8ba
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
@@ -0,0 +1,158 @@
+/*
+ * 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;
+
+
+/**
+ * This class holds the True Type Format table names as in the Directory Table of a TTF font file.
+ * This class must also support custom tables found in fonts (thus an enum wasn't used).
+ */
+public final class TTFTableName {
+ /** The first table in a True Type font file containing metadata about other tables. */
+ public static final TTFTableName DIRECTORY_TABLE = new TTFTableName("dirTable");
+
+ /** Embedded bitmap data */
+ public static final TTFTableName EBDT = new TTFTableName("EBDT");
+
+ /** Embedded bitmap location data */
+ public static final TTFTableName EBLC = new TTFTableName("EBLC");
+
+ /** Embedded bitmap scaling data */
+ public static final TTFTableName EBSC = new TTFTableName("EBSC");
+
+ /** A font forge specific table */
+ public static final TTFTableName FFTM = new TTFTableName("FFTM");
+
+ /** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */
+ public static final TTFTableName GDEF = new TTFTableName("GDEF");
+
+ /** Provides kerning information, mark-to-base, etc. for opentype fonts */
+ public static final TTFTableName GPOS = new TTFTableName("GPOS");
+
+ /** Provides ligature information, swash, etc. for opentype fonts */
+ public static final TTFTableName GSUB = new TTFTableName("GSUB");
+
+ /** Linear threshold table */
+ public static final TTFTableName LTSH = new TTFTableName("LTSH");
+
+ /** OS/2 and Windows specific metrics */
+ public static final TTFTableName OS2 = new TTFTableName("OS/2");
+
+ /** PCL 5 data*/
+ public static final TTFTableName PCLT = new TTFTableName("PCLT");
+
+ /** Vertical Device Metrics table */
+ public static final TTFTableName VDMX = new TTFTableName("VDMX");
+
+ /** character to glyph mapping */
+ public static final TTFTableName CMAP = new TTFTableName("cmap");
+
+ /** Control Value Table */
+ public static final TTFTableName CVT = new TTFTableName("cvt ");
+
+ /** font program */
+ public static final TTFTableName FPGM = new TTFTableName("fpgm");
+
+ /** grid-fitting and scan conversion procedure (grayscale) */
+ public static final TTFTableName GASP = new TTFTableName("gasp");
+
+ /** glyph data */
+ public static final TTFTableName GLYF = new TTFTableName("glyf");
+
+ /** horizontal device metrics */
+ public static final TTFTableName HDMX = new TTFTableName("hdmx");
+
+ /** font header */
+ public static final TTFTableName HEAD = new TTFTableName("head");
+
+ /** horizontal header */
+ public static final TTFTableName HHEA = new TTFTableName("hhea");
+
+ /** horizontal metrics */
+ public static final TTFTableName HMTX = new TTFTableName("hmtx");
+
+ /** kerning */
+ public static final TTFTableName KERN = new TTFTableName("kern");
+
+ /** index to location */
+ public static final TTFTableName LOCA = new TTFTableName("loca");
+
+ /** maximum profile */
+ public static final TTFTableName MAXP = new TTFTableName("maxp");
+
+ /** naming table */
+ public static final TTFTableName NAME = new TTFTableName("name");
+
+ /** PostScript information */
+ public static final TTFTableName POST = new TTFTableName("post");
+
+ /** CVT Program */
+ public static final TTFTableName PREP = new TTFTableName("prep");
+
+ /** Vertical Metrics header */
+ public static final TTFTableName VHEA = new TTFTableName("vhea");
+
+ /** Vertical Metrics */
+ public static final TTFTableName VMTX = new TTFTableName("vmtx");
+
+ private final String name;
+
+ private TTFTableName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of the table as it should be in the Table Directory.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the appropriate TTFTableName object when given the string representation.
+ * @param tableName table name as in the Directory Table.
+ * @return TTFTableName
+ */
+ public static TTFTableName getValue(String tableName) {
+ if (tableName != null) {
+ return new TTFTableName(tableName);
+ }
+ throw new IllegalArgumentException("A TrueType font table name must not be null");
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TTFTableName)) {
+ return false;
+ }
+ TTFTableName to = (TTFTableName) o;
+ return this.name.equals(to.getName());
+ }
+
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
new file mode 100644
index 000000000..75f0ef63d
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * An interface for streaming full True Type tables from a TTF file.
+ */
+public interface TTFTableOutputStream {
+
+ /**
+ * Streams a table defined in byteArray at offset of length bytes.
+ * @param byteArray The source of the table to stream from.
+ * @param offset The position in byteArray to begin streaming from.
+ * @param length The number of bytes to stream.
+ * @throws IOException write error.
+ */
+ void streamTable(byte[] byteArray, int offset, int length) throws IOException;
+}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index 752d14207..2f83f76d1 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -39,9 +39,6 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
-import org.apache.xmlgraphics.xmp.Metadata;
-
import org.apache.fop.fonts.CIDFont;
import org.apache.fop.fonts.CIDSubset;
import org.apache.fop.fonts.CodePointMapping;
@@ -59,6 +56,7 @@ import org.apache.fop.fonts.truetype.FontFileReader;
import org.apache.fop.fonts.truetype.TTFSubSetFile;
import org.apache.fop.fonts.type1.PFBData;
import org.apache.fop.fonts.type1.PFBParser;
+import org.apache.xmlgraphics.xmp.Metadata;
/**
* This class provides method to create and register PDF objects.
@@ -1663,8 +1661,8 @@ public class PDFFactory {
FontFileReader reader = new FontFileReader(in);
TTFSubSetFile subset = new TTFSubSetFile();
- byte[] subsetFont = subset.readFont(reader,
- mbfont.getTTCName(), mbfont.getUsedGlyphs());
+ subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs());
+ byte[] subsetFont = subset.getFontSubset();
// Only TrueType CID fonts are supported now
embeddedFont = new PDFTTFStream(subsetFont.length);
diff --git a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
index fb88b8bce..08cfd3fc4 100644
--- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
+++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
@@ -25,7 +25,6 @@ import javax.xml.transform.Source;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.EmbedFontInfo;
import org.apache.fop.fonts.EncodingMode;
@@ -88,8 +87,8 @@ public class ConfiguredFontCollection implements FontCollection {
font = new CustomFontMetricsMapper(fontMetrics, fontSource);
} else {
CustomFont fontMetrics = FontLoader.loadFont(
- fontFile, null, true, EncodingMode.AUTO,
- configFontInfo.getKerning(), fontResolver);
+ fontFile, null, true, configFontInfo.getEmbeddingMode(),
+ EncodingMode.AUTO, configFontInfo.getKerning(), fontResolver);
font = new CustomFontMetricsMapper(fontMetrics);
}
diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java
index 9df0aa030..157f4f419 100644
--- a/src/java/org/apache/fop/render/ps/PSFontUtils.java
+++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java
@@ -23,7 +23,10 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
@@ -36,13 +39,13 @@ import org.apache.xmlgraphics.ps.DSCConstants;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSResource;
import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
-import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream;
import org.apache.fop.fonts.BFEntry;
import org.apache.fop.fonts.Base14Font;
import org.apache.fop.fonts.CIDFontType;
import org.apache.fop.fonts.CIDSubset;
import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontType;
@@ -52,8 +55,11 @@ import org.apache.fop.fonts.SingleByteEncoding;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.TTFFile;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
import org.apache.fop.fonts.truetype.TTFSubSetFile;
import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStream;
import org.apache.fop.util.HexEncoder;
/**
@@ -63,7 +69,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
/** logging instance */
protected static final Log log = LogFactory.getLog(PSFontUtils.class);
-
/**
* Generates the PostScript code for the font dictionary. This method should only be
* used if no "resource optimization" is performed, i.e. when the fonts are not embedded
@@ -119,11 +124,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
* @return a Map of PSResource instances representing all defined fonts (key: font key)
* @throws IOException in case of an I/O problem
*/
- private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts,
- boolean encodeAllCharacters, PSEventProducer eventProducer) throws IOException {
+ private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
+ Map<String, Typeface> fonts, boolean encodeAllCharacters, PSEventProducer eventProducer)
+ throws IOException {
gen.commentln("%FOPBeginFontDict");
- Map fontResources = new java.util.HashMap();
+ Map fontResources = new HashMap();
for (String key : fonts.keySet()) {
Typeface tf = getTypeFace(fontInfo, fonts, key);
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
@@ -219,57 +225,52 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
PSEventProducer eventProducer) throws IOException {
- boolean embeddedFont = false;
FontType fontType = tf.getFontType();
PSFontResource fontResource = null;
- if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
- || fontType == FontType.TYPE0) {
- if (tf instanceof CustomFont) {
- CustomFont cf = (CustomFont)tf;
- if (isEmbeddable(cf)) {
- InputStream in = getInputStreamOnFont(gen, cf);
- if (in != null) {
- if (fontType == FontType.TYPE0) {
- if (gen.embedIdentityH()) {
- checkPostScriptLevel3(gen, eventProducer);
- /*
- * First CID-keyed font to be embedded; add
- * %%IncludeResource: comment for ProcSet CIDInit.
- */
- gen.includeProcsetCIDInitResource();
- }
- PSResource cidFontResource = embedType2CIDFont(gen,
- (MultiByteFont) tf, in);
- fontResource = PSFontResource.createFontResource(fontRes,
- gen.getProcsetCIDInitResource(),
- gen.getIdentityHCMapResource(),
- cidFontResource);
- }
- gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
- fontRes);
- if (fontType == FontType.TYPE1) {
- embedType1Font(gen, in);
- fontResource = PSFontResource.createFontResource(fontRes);
- } else if (fontType == FontType.TRUETYPE) {
- embedTrueTypeFont(gen, (SingleByteFont) tf, in);
- fontResource = PSFontResource.createFontResource(fontRes);
- } else {
- composeType0Font(gen, (MultiByteFont) tf, in);
- }
- gen.writeDSCComment(DSCConstants.END_RESOURCE);
- gen.getResourceTracker().registerSuppliedResource(fontRes);
- embeddedFont = true;
- } else {
- gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
- log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
- + " PostScript file but could not be embedded!");
- }
- }
- }
- }
- if (!embeddedFont) {
+ if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
+ || fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) {
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
fontResource = PSFontResource.createFontResource(fontRes);
+ return fontResource;
+ }
+ CustomFont cf = (CustomFont)tf;
+ if (isEmbeddable(cf)) {
+ InputStream in = getInputStreamOnFont(gen, cf);
+ if (in == null) {
+ gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
+ log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
+ + " PostScript file but could not be embedded!");
+ gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ return fontResource;
+ }
+ if (fontType == FontType.TYPE0) {
+ if (gen.embedIdentityH()) {
+ checkPostScriptLevel3(gen, eventProducer);
+ /*
+ * First CID-keyed font to be embedded; add
+ * %%IncludeResource: comment for ProcSet CIDInit.
+ */
+ gen.includeProcsetCIDInitResource();
+ }
+ PSResource cidFontResource = embedType2CIDFont(gen,
+ (MultiByteFont) tf, in);
+ fontResource = PSFontResource.createFontResource(fontRes,
+ gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(),
+ cidFontResource);
+ }
+ gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes);
+ if (fontType == FontType.TYPE1) {
+ embedType1Font(gen, in);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ } else if (fontType == FontType.TRUETYPE) {
+ embedTrueTypeFont(gen, (SingleByteFont) tf, in);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ } else {
+ composeType0Font(gen, (MultiByteFont) tf, in);
+ }
+ gen.writeDSCComment(DSCConstants.END_RESOURCE);
+ gen.getResourceTracker().registerSuppliedResource(fontRes);
}
return fontResource;
}
@@ -292,12 +293,28 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
/* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */
gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions
gen.writeln("11 dict begin");
- createType42DictionaryEntries(gen, font, fontStream, font.getCMap());
+ if (font.getEmbeddingMode() == EmbeddingMode.AUTO) {
+ font.setEmbeddingMode(EmbeddingMode.SUBSET);
+ }
+ FontFileReader reader = new FontFileReader(fontStream);
+ // TODO is subset-embedding working? In which case the following can be factorized
+ // with what is in composeType0Font
+// TTFFile ttfFile;
+// if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+// ttfFile = new TTFSubSetFile();
+// ttfFile.readFont(reader, font.getFullName()(), font.getUsedGlyphs());
+// } else {
+// ttfFile = new TTFFile();
+// ttfFile.readFont(reader, font.getFullName());
+// }
+ TTFFile ttfFile = new TTFFile();
+ ttfFile.readFont(reader, font.getFullName());
+ createType42DictionaryEntries(gen, font, font.getCMap(), ttfFile);
gen.writeln("FontName currentdict end definefont pop");
}
private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font,
- InputStream fontStream, BFEntry[] cmap) throws IOException {
+ BFEntry[] cmap, TTFFile ttfFile) throws IOException {
gen.write("/FontName /");
gen.write(font.getEmbedFontName());
gen.writeln(" def");
@@ -308,7 +325,8 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
gen.writeln("/Encoding 256 array");
gen.writeln("0 1 255{1 index exch/.notdef put}for");
boolean buildCharStrings;
- if (font.getFontType() == FontType.TYPE0) {
+ Set<String> glyphNames = new HashSet<String>();
+ if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) {
//"/Encoding" is required but ignored for CID fonts
//so we keep it minimal to save space
buildCharStrings = false;
@@ -323,72 +341,75 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
gen.write(Glyphs.NOTDEF);
} else {
gen.write(glyphName);
+ glyphNames.add(glyphName);
}
gen.writeln(" put");
}
}
gen.writeln("readonly def");
- gen.write("/sfnts[");
- /*
- * Store the font file in an array of hex-encoded strings. Strings are limited to
- * 65535 characters, string will start with a newline, 2 characters are needed to
- * hex-encode each byte, one newline character will be added every 40 bytes, each
- * string should start at a 4-byte boundary
- * => buffer size = floor((65535 - 1) * 40 / 81 / 4) * 4
- * TODO this is not robust: depends on how often ASCIIHexOutputStream adds a newline
- */
- // TODO does not follow Technical Note #5012's requirements:
- // "strings must begin at TrueType table boundaries, or at individual glyph
- // boundaries within the glyf table."
- // There may be compatibility issues with older PostScript interpreters
- byte[] buffer = new byte[32360];
- int readCount;
- while ((readCount = fontStream.read(buffer)) > 0) {
- ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
- gen.writeln("<");
- hexOut.write(buffer, 0, readCount);
- gen.write("> ");
- }
- gen.writeln("]def");
+ TTFOutputStream ttfOut = new PSTTFOutputStream(gen);
+ ttfFile.stream(ttfOut);
+
+ buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font);
+ }
+
+ private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings,
+ BFEntry[] cmap, Set<String> glyphNames, CustomFont font) throws IOException {
gen.write("/CharStrings ");
- if (buildCharStrings) {
+ if (!buildCharStrings) {
+ gen.write(1);
+ } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
int charCount = 1; //1 for .notdef
for (BFEntry entry : cmap) {
charCount += entry.getUnicodeEnd() - entry.getUnicodeStart() + 1;
}
gen.write(charCount);
} else {
- gen.write(1);
+ gen.write(font.getCMap().length);
}
gen.writeln(" dict dup begin");
gen.write("/");
gen.write(Glyphs.NOTDEF);
gen.writeln(" 0 def"); // .notdef always has to be at index 0
- if (buildCharStrings) {
- //Only performed in singly-byte mode, ignored for CID fonts
-
+ if (!buildCharStrings) {
+ // If we're not building the full CharStrings we can end here
+ gen.writeln("end readonly def");
+ return;
+ }
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ //Only performed in singly-byte mode, ignored for CID fonts
for (BFEntry entry : cmap) {
int glyphIndex = entry.getGlyphStartIndex();
for (int ch = entry.getUnicodeStart(); ch <= entry.getUnicodeEnd(); ch++) {
char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit
String glyphName = Glyphs.charToGlyphName(ch16);
-
if ("".equals(glyphName)) {
glyphName = "u" + Integer.toHexString(ch).toUpperCase();
}
- gen.write("/");
- gen.write(glyphName);
- gen.write(" ");
- gen.write(glyphIndex);
- gen.writeln(" def");
+ writeGlyphDefs(gen, glyphName, glyphIndex);
glyphIndex++;
}
}
+ } else {
+ for (String name : glyphNames) {
+ writeGlyphDefs(gen, name,
+ getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0),
+ font.getCMap()));
+ }
}
gen.writeln("end readonly def");
}
+ private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex)
+ throws IOException {
+ gen.write("/");
+ gen.write(glyphName);
+ gen.write(" ");
+ gen.write(glyphIndex);
+ gen.writeln(" def");
+ }
+
private static int getGlyphIndex(char c, BFEntry[] cmap) {
for (BFEntry entry : cmap) {
if (entry.getUnicodeStart() <= c && c <= entry.getUnicodeEnd()) {
@@ -408,7 +429,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
gen.writeln("] composefont pop");
}
- private static PSResource embedType2CIDFont(final PSGenerator gen,
+ private static PSResource embedType2CIDFont(PSGenerator gen,
MultiByteFont font, InputStream fontStream) throws IOException {
assert font.getCIDType() == CIDFontType.CIDTYPE2;
@@ -467,30 +488,28 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
lineCount = 1;
}
}
- String gid = HexEncoder.encode(cid, 4);
+ String gid;
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ gid = HexEncoder.encode(cid, 4);
+ } else {
+ gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4);
+ }
gen.write(gid);
}
gen.writeln(">] def");
+ FontFileReader reader = new FontFileReader(fontStream);
- //Create tables for subset
- TTFSubSetFile subset = new TTFSubSetFile();
- TTFSubSetFile.GlyphHandler glyphHandler = new TTFSubSetFile.GlyphHandler() {
+ TTFFile ttfFile;
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ ttfFile = new TTFSubSetFile();
+ ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs());
+ } else {
+ ttfFile = new TTFFile();
+ ttfFile.readFont(reader, font.getTTCName());
+ }
- public void addGlyph(byte[] glyphData) throws IOException {
- ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
- gen.writeln("<");
- hexOut.write(glyphData);
- gen.writeln(">");
- }
- };
- gen.writeln("/GlyphDirectory [");
- FontFileReader reader = new FontFileReader(fontStream);
- byte[] subsetFont = subset.toPostScriptSubset(reader,
- font.getTTCName(), font.getUsedGlyphs(), glyphHandler);
- gen.writeln("] def");
- InputStream subsetInput = new java.io.ByteArrayInputStream(subsetFont);
- createType42DictionaryEntries(gen, font, subsetInput, new BFEntry[0]);
+ createType42DictionaryEntries(gen, font, new BFEntry[0], ttfFile);
gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop");
gen.writeln("end");
gen.writeln("%%EndResource");
@@ -702,5 +721,4 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
gen.getResourceTracker().registerSuppliedResource(res);
return res;
}
-
}
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index 5ed217db1..2821f6c67 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -404,8 +404,7 @@ public class PSPainter extends AbstractIFPainter {
}
}
- private void writeText( // CSOK: ParameterNumber
- String text, int start, int len,
+ private void writeText(String text, int start, int len,
int letterSpacing, int wordSpacing, int[] dx,
Font font, Typeface tf, boolean multiByte) throws IOException {
PSGenerator generator = getGenerator();
@@ -416,16 +415,6 @@ public class PSPainter extends AbstractIFPainter {
boolean hasLetterSpacing = (letterSpacing != 0);
boolean needTJ = false;
- char strOpen;
- char strClose;
- if (multiByte) {
- strOpen = '<';
- strClose = '>';
- } else {
- strOpen = '(';
- strClose = ')';
- }
-
int lineStart = 0;
StringBuffer accText = new StringBuffer(initialSize);
StringBuffer sb = new StringBuffer(initialSize);
@@ -467,9 +456,7 @@ public class PSPainter extends AbstractIFPainter {
sb.append(PSGenerator.LF);
lineStart = sb.length();
}
- sb.append(strOpen);
- sb.append(accText);
- sb.append(strClose);
+ lineStart = writePostScriptString(sb, accText, multiByte, lineStart);
sb.append(' ');
accText.setLength(0); //reset accumulated text
}
@@ -478,9 +465,10 @@ public class PSPainter extends AbstractIFPainter {
}
if (needTJ) {
if (accText.length() > 0) {
- sb.append(strOpen);
- sb.append(accText);
- sb.append(strClose);
+ if ((sb.length() - lineStart + accText.length()) > 200) {
+ sb.append(PSGenerator.LF);
+ }
+ writePostScriptString(sb, accText, multiByte);
}
if (hasLetterSpacing) {
sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ");
@@ -488,7 +476,7 @@ public class PSPainter extends AbstractIFPainter {
sb.append("] TJ");
}
} else {
- sb.append(strOpen).append(accText).append(strClose);
+ writePostScriptString(sb, accText, multiByte);
if (hasLetterSpacing) {
StringBuffer spb = new StringBuffer();
spb.append(formatMptAsPt(generator, letterSpacing))
@@ -502,6 +490,32 @@ public class PSPainter extends AbstractIFPainter {
generator.writeln(sb.toString());
}
+ private void writePostScriptString(StringBuffer buffer, StringBuffer string,
+ boolean multiByte) {
+ writePostScriptString(buffer, string, multiByte, 0);
+ }
+
+ private int writePostScriptString(StringBuffer buffer, StringBuffer string, boolean multiByte,
+ int lineStart) {
+ buffer.append(multiByte ? '<' : '(');
+ int l = string.length();
+ int index = 0;
+ int maxCol = 200;
+ buffer.append(string.substring(index, Math.min(index + maxCol, l)));
+ index += maxCol;
+ while (index < l) {
+ if (!multiByte) {
+ buffer.append('\\');
+ }
+ buffer.append(PSGenerator.LF);
+ lineStart = buffer.length();
+ buffer.append(string.substring(index, Math.min(index + maxCol, l)));
+ index += maxCol;
+ }
+ buffer.append(multiByte ? '>' : ')');
+ return lineStart;
+ }
+
private void useFont(String key, int size) throws IOException {
PSFontResource res = this.documentHandler.getPSResourceForFontKey(key);
PSGenerator generator = getGenerator();
@@ -509,5 +523,4 @@ public class PSPainter extends AbstractIFPainter {
res.notifyResourceUsageOnPage(generator.getResourceTracker());
}
-
}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
new file mode 100644
index 000000000..f8ce37505
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
@@ -0,0 +1,104 @@
+/*
+ * 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.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream;
+
+/**
+ * This is a wrapper for {@link PSGenerator} that contains some members specific for streaming
+ * True Type fonts to a PostScript document.
+ */
+public class PSTTFGenerator {
+ private PSGenerator gen;
+ private ASCIIHexOutputStream hexOut;
+
+ /**
+ * The buffer is used to store the font file in an array of hex-encoded strings. Strings are
+ * limited to 65535 characters, string will start with a newline, 2 characters are needed to
+ * hex-encode each byte.
+ */
+ public static final int MAX_BUFFER_SIZE = 32764;
+
+ /**
+ * Constructor - initialises the PSGenerator in this wrapper class.
+ * @param gen PSGenerator
+ */
+ public PSTTFGenerator(PSGenerator gen) {
+ this.gen = gen;
+ hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+ }
+
+ /**
+ * Begins writing a string by writing '<' to the begin.
+ * @throws IOException file write exception.
+ */
+ public void startString() throws IOException {
+ // We need to reset the streamer so that it starts a new line in the PS document
+ hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+ gen.writeln("<");
+ }
+
+ /**
+ * Streams a string to a PostScript document (wraps PSGenerator.write(String)).
+ * @param cmd String
+ * @throws IOException file write exception
+ */
+ public void write(String cmd) throws IOException {
+ gen.write(cmd);
+ }
+
+ /**
+ * Streams a string followed by a new line char to a PostScript document (wraps
+ * PSGenerator.writeln(String)).
+ * @param cmd String
+ * @throws IOException file write exception
+ */
+ public void writeln(String cmd) throws IOException {
+ gen.writeln(cmd);
+ }
+
+ /**
+ * Streams the bytes.
+ * @param byteArray byte[] the byte array to stream to file.
+ * @param offset int the starting position in the byte array to stream to file.
+ * @param length the number of bytes to stream to file. This MUST be less than
+ * MAX_BUFFER_SIZE - 1 since strings are suffixed by '00' (as in spec).
+ * @throws IOException file write exception
+ */
+ public void streamBytes(byte[] byteArray, int offset, int length) throws IOException {
+ if (length > MAX_BUFFER_SIZE) {
+ throw new UnsupportedOperationException("Attempting to write a string to a PostScript"
+ + " file that is greater than the buffer size.");
+ }
+ hexOut.write(byteArray, offset, length);
+ }
+
+ /**
+ * Finishes writing a string by appending '00' and '>' to the end.
+ * @throws IOException file write exception
+ */
+ public void endString() throws IOException {
+ /* Appends a '00' to the end of the string as specified in the spec */
+ gen.write("00\n> ");
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
new file mode 100644
index 000000000..9727af764
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+
+/**
+ * This class streams glyphs from the "glyf" table in a True Type font.
+ */
+public class PSTTFGlyphOutputStream implements TTFGlyphOutputStream {
+ /** This counts the total number of bytes written that have been streamed. */
+ private int byteCounter = 0;
+
+ /** This is a place-holder for the offset of the last string boundary. */
+ private int lastStringBoundary = 0;
+ private PSTTFGenerator ttfGen;
+
+ /**
+ * Constructor
+ * @param ttfGen PSTTFGenerator
+ */
+ public PSTTFGlyphOutputStream(PSTTFGenerator ttfGen) {
+ this.ttfGen = ttfGen;
+ }
+
+ /** {@inheritDoc} */
+ public void startGlyphStream() throws IOException {
+ ttfGen.startString();
+ }
+
+ /** {@inheritDoc} */
+ public void streamGlyph(byte[] byteArray, int offset, int length) throws IOException {
+ if (length > PSTTFGenerator.MAX_BUFFER_SIZE) {
+ throw new UnsupportedOperationException("The glyph is " + length + " there may be an "
+ + "error in the font file.");
+ }
+
+ if (length + (byteCounter - lastStringBoundary) < PSTTFGenerator.MAX_BUFFER_SIZE) {
+ ttfGen.streamBytes(byteArray, offset, length);
+ } else {
+ ttfGen.endString();
+ lastStringBoundary = byteCounter;
+ ttfGen.startString();
+ ttfGen.streamBytes(byteArray, offset, length);
+ }
+ byteCounter += length;
+ }
+
+ /** {@inheritDoc} */
+ public void endGlyphStream() throws IOException {
+ ttfGen.endString();
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
new file mode 100644
index 000000000..bf3803eb4
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
@@ -0,0 +1,64 @@
+/*
+ * 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.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * Implements TTFOutputStream and streams font tables to a PostScript file.
+ */
+public class PSTTFOutputStream implements TTFOutputStream {
+ /** The wrapper class for PSGenerator */
+ private final PSTTFGenerator ttfGen;
+
+ /**
+ * Constructor - assigns a PSGenerator to stream the font.
+ * @param gen PSGenerator.
+ */
+ public PSTTFOutputStream(PSGenerator gen) {
+ this.ttfGen = new PSTTFGenerator(gen);
+ }
+
+ /** {@inheritDoc} */
+ public void startFontStream() throws IOException {
+ ttfGen.write("/sfnts[");
+ }
+
+ /** {@inheritDoc} */
+ public TTFTableOutputStream getTableOutputStream() {
+ return new PSTTFTableOutputStream(ttfGen);
+ }
+
+ /** {@inheritDoc} */
+ public TTFGlyphOutputStream getGlyphOutputStream() {
+ return new PSTTFGlyphOutputStream(ttfGen);
+ }
+
+ /** {@inheritDoc} */
+ public void endFontStream() throws IOException {
+ ttfGen.writeln("] def");
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
new file mode 100644
index 000000000..24d96878e
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
@@ -0,0 +1,58 @@
+/*
+ * 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.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * This class streams a truetype table to a PostScript file.
+ *
+ */
+public class PSTTFTableOutputStream implements TTFTableOutputStream {
+ private PSTTFGenerator ttfGen;
+ /**
+ * Constructor.
+ * @param ttfGen PSGenerator the streamer class used for streaming bytes.
+ */
+ public PSTTFTableOutputStream(PSTTFGenerator ttfGen) {
+ this.ttfGen = ttfGen;
+ }
+
+ /** {@inheritDoc} */
+ public void streamTable(byte[] byteArray, int offset, int length) throws IOException {
+ int offsetPosition = offset;
+ // Need to split the table into MAX_BUFFER_SIZE chunks
+ for (int i = 0; i < length / PSTTFGenerator.MAX_BUFFER_SIZE; i++) {
+ streamString(byteArray, offsetPosition, PSTTFGenerator.MAX_BUFFER_SIZE);
+ offsetPosition += PSTTFGenerator.MAX_BUFFER_SIZE;
+ }
+ if (length % PSTTFGenerator.MAX_BUFFER_SIZE > 0) {
+ streamString(byteArray, offsetPosition, length % PSTTFGenerator.MAX_BUFFER_SIZE);
+ }
+ }
+
+ private void streamString(byte[] byteArray, int offset, int length) throws IOException {
+ ttfGen.startString();
+ ttfGen.streamBytes(byteArray, offset, length);
+ ttfGen.endString();
+ }
+}