Merged changes from trunk up to revision 1351540


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript@1352963 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index 96c8556..73876a5 100644
--- a/build.xml
+++ b/build.xml
@@ -864,16 +864,28 @@
   <target name="junit-text-linebreak" depends="junit-compile" description="Runs FOP's JUnit unicode linebreak tests" if="junit.present">
     <junit-run title="Unicode UAX#14 support" testsuite="org.apache.fop.text.linebreak.LineBreakStatusTestCase" outfile="TEST-linebreak"/>
   </target>
+  <target name="junit-fonts" depends="junit-compile">
+    <echo message="Running tests for the fonts package"/>
+    <junit-run title="fonts" testsuite="org.apache.fop.fonts.FOPFontsTestSuite" outfile="TEST-fonts"/>
+  </target>
+  <target name="junit-render-ps" depends="junit-compile">
+    <echo message="Running tests for the render ps package"/>
+    <junit-run title="render-ps" testsuite="org.apache.fop.render.ps.RenderPSTestSuite" outfile="TEST-render-ps"/>
+  </target>
   <target name="junit-render-pdf" depends="junit-compile">
     <junit-run title="render-pdf" testsuite="org.apache.fop.render.pdf.RenderPDFTestSuite" outfile="TEST-render-pdf"/>
   </target>
   <target name="junit-complexscripts" depends="junit-compile">
     <junit-run title="complexscripts" testsuite="org.apache.fop.complexscripts.ComplexScriptsTestSuite" outfile="TEST-complexscripts"/>
   </target>
-  <target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree, junit-render-pdf, junit-complexscripts"/>
+  <target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, 
+    junit-text-linebreak, junit-fotree, junit-fonts, junit-render-pdf, junit-render-ps, 
+    junit-complexscripts"/>
   <target name="junit" depends="junit-all" description="Runs all of FOP's JUnit tests" 
     if="junit.present">
-    <fail><condition><or><isset property="fop.junit.error"/><isset property="fop.junit.failure"/><not><isset property="hyphenation.present"/></not></or></condition>
+    <fail><condition><or><isset property="fop.junit.error"/><isset 
+            property="fop.junit.failure"/><not><isset 
+              property="hyphenation.present"/></not></or></condition>
 NOTE:
 **************************************************************************
 * One or more of the Junit tests had Failures or Errors or were skipped! *
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index 9056ff5..a52209c 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -1,6 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <FindBugsFilter>
   <Match>
+    <Class name="org.apache.fop.fonts.truetype.TTFFile$1"/>
+    <Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/>
+  </Match>
+  <Match>
+    <Class name="org.apache.fop.fonts.truetype.FontFileReader"/>
+    <Method name="getAllBytes"/>
+    <Bug pattern="EI_EXPOSE_REP"/>
+  </Match>
+  <Match>
     <Class name="org.apache.fop.fo.properties.FontFamilyProperty"/>
     <Bug pattern="EQ_DOESNT_OVERRIDE_EQUALS"/>
   </Match>
diff --git a/lib/xmlgraphics-commons-1.5svn.jar b/lib/xmlgraphics-commons-1.5svn.jar
index c11f186..ea69722 100644
--- a/lib/xmlgraphics-commons-1.5svn.jar
+++ b/lib/xmlgraphics-commons-1.5svn.jar
Binary files differ
diff --git a/src/documentation/content/xdocs/trunk/fonts.xml b/src/documentation/content/xdocs/trunk/fonts.xml
index f348160..ba607e4 100644
--- a/src/documentation/content/xdocs/trunk/fonts.xml
+++ b/src/documentation/content/xdocs/trunk/fonts.xml
@@ -493,10 +493,10 @@
           Various notes related to embedded fonts:
         </p>
         <ul>
-          <li>The PostScript renderer does not yet support TrueType fonts, but can embed Type 1 fonts.</li>
-          <li>The font is simply embedded into the PDF file, it is not converted.</li>
-          <li>When FOP embeds a font, it adds a prefix to the fontname to ensure that the name will not match the fontname of an installed font.
-            This is helpful with older versions of Acrobat Reader that preferred installed fonts over embedded fonts.</li>
+          <li>The font is simply embedded into the output file, it is not converted.</li>
+          <li>When FOP embeds a font in a PDF file, it adds a prefix to the fontname to ensure that 
+            the name will not match the fontname of an installed font. This is helpful with older 
+            versions of Acrobat Reader that preferred installed fonts over embedded fonts.</li>
           <li>When embedding PostScript fonts, the entire font is always embedded.</li>
           <li>When embedding TrueType fonts (ttf) or TrueType Collections (ttc), a subset of the
           original font, containing only the glyphs used, is embedded in the output document.
@@ -576,4 +576,4 @@
       </p>
     </section>
   </body>
-</document>
\ No newline at end of file
+</document>
diff --git a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
index a9110d3..9afb893 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
@@ -31,6 +31,7 @@
 import org.apache.fop.fonts.truetype.FontFileReader;
 import org.apache.fop.fonts.truetype.TTFDirTabEntry;
 import org.apache.fop.fonts.truetype.TTFFile;
+import org.apache.fop.fonts.truetype.TTFTableName;
 
 // CSOFF: AvoidNestedBlocksCheck
 // CSOFF: NoWhitespaceAfterCheck
@@ -126,7 +127,7 @@
         return gpos;
     }
 
-    private void readLangSysTable(String tableTag, long langSysTable, String langSysTag) throws IOException {
+    private void readLangSysTable(TTFTableName tableTag, long langSysTable, String langSysTag) throws IOException {
         in.seekSet(langSysTable);
         if (log.isDebugEnabled()) {
             log.debug(tableTag + " lang sys table: " + langSysTag );
@@ -168,7 +169,7 @@
 
     private static String defaultTag = "dflt";
 
-    private void readScriptTable(String tableTag, long scriptTable, String scriptTag) throws IOException {
+    private void readScriptTable(TTFTableName tableTag, long scriptTable, String scriptTag) throws IOException {
         in.seekSet(scriptTable);
         if (log.isDebugEnabled()) {
             log.debug(tableTag + " script table: " + scriptTag );
@@ -221,7 +222,7 @@
         seLanguages = null;
     }
 
-    private void readScriptList(String tableTag, long scriptList) throws IOException {
+    private void readScriptList(TTFTableName tableTag, long scriptList) throws IOException {
         in.seekSet(scriptList);
         // read script record count
         int ns = in.readTTFUShort();
@@ -250,7 +251,7 @@
         }
     }
 
-    private void readFeatureTable(String tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
+    private void readFeatureTable(TTFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
         in.seekSet(featureTable);
         if (log.isDebugEnabled()) {
             log.debug(tableTag + " feature table: " + featureTag );
@@ -278,7 +279,7 @@
         seFeatures.put ( "f" + featureIndex, new Object[] { featureTag, lul } );
     }
 
-    private void readFeatureList(String tableTag, long featureList) throws IOException {
+    private void readFeatureList(TTFTableName tableTag, long featureList) throws IOException {
         in.seekSet(featureList);
         // read feature record count
         int nf = in.readTTFUShort();
@@ -3144,9 +3145,9 @@
         resetATSubState();
     }
 
-    private void readLookupTable(String tableTag, int lookupSequence, long lookupTable) throws IOException {
-        boolean isGSUB = tableTag.equals ( "GSUB" );
-        boolean isGPOS = tableTag.equals ( "GPOS" );
+    private void readLookupTable(TTFTableName tableTag, int lookupSequence, long lookupTable) throws IOException {
+        boolean isGSUB = tableTag.equals ( TTFTableName.GSUB );
+        boolean isGPOS = tableTag.equals ( TTFTableName.GPOS );
         in.seekSet(lookupTable);
         // read lookup type
         int lt = in.readTTFUShort();
@@ -3197,7 +3198,7 @@
         }
     }
 
-    private void readLookupList(String tableTag, long lookupList) throws IOException {
+    private void readLookupList(TTFTableName tableTag, long lookupList) throws IOException {
         in.seekSet(lookupList);
         // read lookup record count
         int nl = in.readTTFUShort();
@@ -3232,7 +3233,7 @@
      * @param lookupList offset to lookup list from beginning of font file
      * @throws IOException In case of a I/O problem
      */
-    private void readCommonLayoutTables(String tableTag, long scriptList, long featureList, long lookupList) throws IOException {
+    private void readCommonLayoutTables(TTFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException {
         if ( scriptList > 0 ) {
             readScriptList ( tableTag, scriptList );
         }
@@ -3244,7 +3245,7 @@
         }
     }
 
-    private void readGDEFClassDefTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+    private void readGDEFClassDefTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
         initATSubState();
         in.seekSet(subtableOffset);
         // subtable is a bare class definition table
@@ -3256,7 +3257,7 @@
         resetATSubState();
     }
 
-    private void readGDEFAttachmentTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+    private void readGDEFAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
         initATSubState();
         in.seekSet(subtableOffset);
         // read coverage offset
@@ -3274,7 +3275,7 @@
         resetATSubState();
     }
 
-    private void readGDEFLigatureCaretTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+    private void readGDEFLigatureCaretTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
         initATSubState();
         in.seekSet(subtableOffset);
         // read coverage offset
@@ -3304,7 +3305,7 @@
         resetATSubState();
     }
 
-    private void readGDEFMarkAttachmentTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+    private void readGDEFMarkAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
         initATSubState();
         in.seekSet(subtableOffset);
         // subtable is a bare class definition table
@@ -3316,7 +3317,7 @@
         resetATSubState();
     }
 
-    private void readGDEFMarkGlyphsTableFormat1(String tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
+    private void readGDEFMarkGlyphsTableFormat1(TTFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
         initATSubState();
         in.seekSet(subtableOffset);
         // skip over format (already known)
@@ -3350,7 +3351,7 @@
         resetATSubState();
     }
 
-    private void readGDEFMarkGlyphsTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+    private void readGDEFMarkGlyphsTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
         in.seekSet(subtableOffset);
         // read mark set subtable format
         int sf = in.readTTFUShort();
@@ -3366,11 +3367,11 @@
      * @throws IOException In case of a I/O problem
      */
     private void readGDEF() throws IOException {
-        String tableTag = "GDEF";
+        TTFTableName tableTag = TTFTableName.GDEF;
         // Initialize temporary state
         initATState();
         // Read glyph definition (GDEF) table
-        TTFDirTabEntry dirTab = ttf.getDirectoryEntry ( tableTag );
+        TTFDirTabEntry dirTab = ttf.getDirectoryEntry( tableTag );
         if ( gdef != null ) {
             if (log.isDebugEnabled()) {
                 log.debug(tableTag + ": ignoring duplicate table");
@@ -3439,7 +3440,7 @@
      * @throws IOException In case of a I/O problem
      */
     private void readGSUB() throws IOException {
-        String tableTag = "GSUB";
+        TTFTableName tableTag = TTFTableName.GSUB;
         // Initialize temporary state
         initATState();
         // Read glyph substitution (GSUB) table
@@ -3476,7 +3477,7 @@
      * @throws IOException In case of a I/O problem
      */
     private void readGPOS() throws IOException {
-        String tableTag = "GPOS";
+        TTFTableName tableTag = TTFTableName.GPOS;
         // Initialize temporary state
         initATState();
         // Read glyph positioning (GPOS) table
diff --git a/src/java/org/apache/fop/fonts/CIDFontType.java b/src/java/org/apache/fop/fonts/CIDFontType.java
index ce01fa6..20a94b9 100644
--- a/src/java/org/apache/fop/fonts/CIDFontType.java
+++ b/src/java/org/apache/fop/fonts/CIDFontType.java
@@ -34,7 +34,7 @@
     /**
      * CID Font Type 2 (based on TrueType format)
      */
-    public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1);
+    public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 2);
 
 
     /**
diff --git a/src/java/org/apache/fop/fonts/BFEntry.java b/src/java/org/apache/fop/fonts/CMapSegment.java
similarity index 65%
rename from src/java/org/apache/fop/fonts/BFEntry.java
rename to src/java/org/apache/fop/fonts/CMapSegment.java
index d3c7956..816df2c 100644
--- a/src/java/org/apache/fop/fonts/BFEntry.java
+++ b/src/java/org/apache/fop/fonts/CMapSegment.java
@@ -20,26 +20,49 @@
 package org.apache.fop.fonts;
 
 /**
- * This is just a holder class for bfentries, groups of characters of a base font (bf).
+ * A segment in a cmap table of format 4. Unicode code points between
+ * {@link #getUnicodeStart()} and {@link #getUnicodeEnd()} map to contiguous glyph indices
+ * starting from {@link #getGlyphStartIndex()}.
  */
-public class BFEntry {
+public final class CMapSegment {
 
-    private int unicodeStart;
-    private int unicodeEnd;
-    private int glyphStartIndex;
+    private final int unicodeStart;
+    private final int unicodeEnd;
+    private final int glyphStartIndex;
 
     /**
-     * Main constructor.
+     * Creates a new segment.
+     *
      * @param unicodeStart Unicode start index
      * @param unicodeEnd Unicode end index
      * @param glyphStartIndex glyph start index
      */
-    public BFEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+    public CMapSegment(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
         this.unicodeStart = unicodeStart;
         this.unicodeEnd = unicodeEnd;
         this.glyphStartIndex = glyphStartIndex;
     }
 
+    @Override
+    public int hashCode() {
+        int hc = 17;
+        hc = 31 * hc + unicodeStart;
+        hc = 31 * hc + unicodeEnd;
+        hc = 31 * hc + glyphStartIndex;
+        return hc;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof CMapSegment) {
+            CMapSegment ce = (CMapSegment) o;
+            return ce.unicodeStart == this.unicodeStart
+                && ce.unicodeEnd == this.unicodeEnd
+                && ce.glyphStartIndex == this.glyphStartIndex;
+        }
+        return false;
+    }
+
     /**
      * Returns the unicodeStart.
      * @return the Unicode start index
@@ -67,7 +90,7 @@
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("BFEntry: ");
+        StringBuilder sb = new StringBuilder("CMapSegment: ");
         sb.append ( "{ UC[" );
         sb.append ( unicodeStart );
         sb.append ( ',' );
diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java
index c6b43fe..89f5152 100644
--- a/src/java/org/apache/fop/fonts/CustomFont.java
+++ b/src/java/org/apache/fop/fonts/CustomFont.java
@@ -42,6 +42,7 @@
     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;
@@ -62,6 +63,9 @@
     private boolean useKerning = true;
     private boolean useAdvanced = true;
 
+    /** the character map, mapping Unicode ranges to glyph indices. */
+    protected CMapSegment[] cmap;
+
     /** {@inheritDoc} */
     public String getFontName() {
         return fontName;
@@ -112,6 +116,14 @@
     }
 
     /**
+     * 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
@@ -337,6 +349,13 @@
     /**
      * {@inheritDoc}
      */
+    public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+        this.embeddingMode = embeddingMode;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void setCapHeight(int capHeight) {
         this.capHeight = capHeight;
     }
@@ -473,4 +492,25 @@
         }
     }
 
+    /**
+     * Sets the character map for this font. It maps all available Unicode characters
+     * to their glyph indices inside the font.
+     * @param cmap the character map
+     */
+    public void setCMap(CMapSegment[] cmap) {
+        this.cmap = new CMapSegment[cmap.length];
+        System.arraycopy(cmap, 0, this.cmap, 0, cmap.length);
+    }
+
+    /**
+     * Returns the character map for this font. It maps all available Unicode characters
+     * to their glyph indices inside the font.
+     * @return the character map
+     */
+    public CMapSegment[] getCMap() {
+        CMapSegment[] copy = new CMapSegment[cmap.length];
+        System.arraycopy(this.cmap, 0, copy, 0, this.cmap.length);
+        return copy;
+    }
+
 }
diff --git a/src/java/org/apache/fop/fonts/CustomFontCollection.java b/src/java/org/apache/fop/fonts/CustomFontCollection.java
index 6e798a8..fc8c947 100644
--- a/src/java/org/apache/fop/fonts/CustomFontCollection.java
+++ b/src/java/org/apache/fop/fonts/CustomFontCollection.java
@@ -72,7 +72,7 @@
 
             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 8848c0a..64bd200 100644
--- a/src/java/org/apache/fop/fonts/EmbedFontInfo.java
+++ b/src/java/org/apache/fop/fonts/EmbedFontInfo.java
@@ -25,6 +25,8 @@
 
 /**
  * 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 {
 
@@ -41,6 +43,8 @@
     protected boolean advanced;
     /** 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;
@@ -149,6 +153,14 @@
     }
 
     /**
+     * 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
      */
@@ -175,6 +187,17 @@
         this.encodingMode = mode;
     }
 
+    /**
+     * Sets the embedding mode for this font, currently not supported for Type 1 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 0000000..5a3e905
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/EmbeddingMode.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.fonts;
+
+import java.util.Locale;
+
+/**
+ * This enumerates the embedding mode of fonts; full; subset; auto (auto defaults to full for
+ * Type 1 fonts and subset for TrueType fonts.
+ */
+public enum EmbeddingMode {
+    /** Default option: assumes FULL for Type 1 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 the name of this embedding mode in lower case.
+     */
+    public String getName() {
+        return this.toString().toLowerCase(Locale.ENGLISH);
+    }
+
+    /**
+     * Returns the embedding mode corresponding to the given name.
+     * @param value the name of an embedding mode (not case sensitive)
+     * @return the corresponding embedding mode
+     */
+    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 8a40d65..78ffb7a 100644
--- a/src/java/org/apache/fop/fonts/EncodingMode.java
+++ b/src/java/org/apache/fop/fonts/EncodingMode.java
@@ -52,7 +52,7 @@
      * @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 34b6ed1..0427098 100644
--- a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
+++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
@@ -254,12 +254,16 @@
 
         boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
         boolean useAdvanced = fontCfg.getAttributeAsBoolean("advanced", 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, useAdvanced, tripletList, embedUrl,
                                 subFont);
         embedFontInfo.setEncodingMode(encodingMode);
+        embedFontInfo.setEmbeddingMode(embeddingMode);
+
         boolean skipCachedFont = false;
         if (fontCache != null) {
             if (!fontCache.containsFont(embedFontInfo)) {
diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java
index 91b7639..9bdbcd3 100644
--- a/src/java/org/apache/fop/fonts/FontLoader.java
+++ b/src/java/org/apache/fop/fonts/FontLoader.java
@@ -85,15 +85,17 @@
      * @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);
     }
 
     /**
@@ -101,16 +103,17 @@
      * @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, true,
+                embedded, embeddingMode, encodingMode, true, true,
                 resolver);
     }
 
@@ -119,6 +122,7 @@
      * @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 useAdvanced indicates whether advanced typographic information shall be loaded if
@@ -128,8 +132,8 @@
      * @throws IOException In case of an I/O error
      */
     public static CustomFont loadFont(String fontFileURI, String subFontName,
-            boolean embedded, EncodingMode encodingMode, boolean useKerning,
-            boolean useAdvanced, FontResolver resolver) throws IOException {
+            boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+            boolean useKerning, boolean useAdvanced, FontResolver resolver) throws IOException {
         fontFileURI = fontFileURI.trim();
         boolean type1 = isType1(fontFileURI);
         FontLoader loader;
@@ -138,10 +142,14 @@
                 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, useAdvanced, resolver);
+                    embedded, embeddingMode, encodingMode, useKerning, useAdvanced, resolver);
         }
         return loader.getFont();
     }
diff --git a/src/java/org/apache/fop/fonts/FontReader.java b/src/java/org/apache/fop/fonts/FontReader.java
index 9d75ad7..46ea912 100644
--- a/src/java/org/apache/fop/fonts/FontReader.java
+++ b/src/java/org/apache/fop/fonts/FontReader.java
@@ -64,7 +64,7 @@
 
     private Map<Integer, Integer> currentKerning = null;
 
-    private List<BFEntry> bfranges = null;
+    private List<CMapSegment> bfranges = null;
 
     private void createFont(InputSource source) throws FOPException {
         XMLReader parser = null;
@@ -154,12 +154,14 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void startDocument() {
     }
 
     /**
      * {@inheritDoc}
      */
+    @Override
     public void setDocumentLocator(Locator locator) {
         // this.locator = locator; // not used at present
     }
@@ -167,6 +169,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void startElement(String uri, String localName, String qName,
                              Attributes attributes) throws SAXException {
         if (localName.equals("font-metrics")) {
@@ -198,9 +201,9 @@
             returnFont.putKerningEntry(new Integer(attributes.getValue("kpx1")),
                                         currentKerning);
         } else if ("bfranges".equals(localName)) {
-            bfranges = new ArrayList<BFEntry>();
+            bfranges = new ArrayList<CMapSegment>();
         } else if ("bf".equals(localName)) {
-            BFEntry entry = new BFEntry(getInt(attributes.getValue("us")),
+            CMapSegment entry = new CMapSegment(getInt(attributes.getValue("us")),
                                         getInt(attributes.getValue("ue")),
                                         getInt(attributes.getValue("gi")));
             bfranges.add(entry);
@@ -236,6 +239,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void endElement(String uri, String localName, String qName) throws SAXException {
         String content = text.toString().trim();
         if ("font-name".equals(localName)) {
@@ -303,7 +307,7 @@
             multiFont.setWidthArray(wds);
 
         } else if ("bfranges".equals(localName)) {
-            multiFont.setBFEntries(bfranges.toArray(new BFEntry[0]));
+            multiFont.setCMap(bfranges.toArray(new CMapSegment[0]));
         }
         text.setLength(0); //Reset text buffer (see characters())
     }
@@ -311,6 +315,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void characters(char[] ch, int start, int length) {
         text.append(ch, start, length);
     }
diff --git a/src/java/org/apache/fop/fonts/FontType.java b/src/java/org/apache/fop/fonts/FontType.java
index 56039b5..edd8d0c 100644
--- a/src/java/org/apache/fop/fonts/FontType.java
+++ b/src/java/org/apache/fop/fonts/FontType.java
@@ -130,4 +130,9 @@
         return value;
     }
 
+    @Override
+    public String toString() {
+        return name;
+    }
+
 }
diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java
index 7077c53..dfad4ff 100644
--- a/src/java/org/apache/fop/fonts/LazyFont.java
+++ b/src/java/org/apache/fop/fonts/LazyFont.java
@@ -36,7 +36,6 @@
 import org.apache.fop.complexscripts.fonts.Positionable;
 import org.apache.fop.complexscripts.fonts.Substitutable;
 
-
 /**
  * This class is used to defer the loading of a font until it is really used.
  */
@@ -49,7 +48,8 @@
     private boolean useKerning;
     private boolean useAdvanced;
     private EncodingMode encodingMode = EncodingMode.AUTO;
-    private boolean embedded;
+    private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
+    private boolean embedded = true;
     private String subFontName;
 
     private boolean isMetricsLoaded;
@@ -74,6 +74,7 @@
             this.useAdvanced = fontInfo.getAdvanced();
         }
         this.encodingMode = fontInfo.getEncodingMode();
+        this.embeddingMode = fontInfo.getEmbeddingMode();
         this.subFontName = fontInfo.getSubFontName();
         this.embedded = fontInfo.isEmbedded();
         this.resolver = resolver;
@@ -147,8 +148,9 @@
                     if (fontEmbedPath == null) {
                         throw new RuntimeException("Cannot load font. No font URIs available.");
                     }
-                    realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName,
-                            this.embedded, this.encodingMode, useKerning, useAdvanced, resolver);
+                    realFont = FontLoader.loadFont(fontEmbedPath, subFontName,
+                            embedded, embeddingMode, encodingMode,
+                            useKerning, useAdvanced, resolver);
                 }
                 if (realFont instanceof FontDescriptor) {
                     realFontDescriptor = (FontDescriptor) realFont;
diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java
index 73ef7c2..54b772b 100644
--- a/src/java/org/apache/fop/fonts/MultiByteFont.java
+++ b/src/java/org/apache/fop/fonts/MultiByteFont.java
@@ -51,14 +51,6 @@
 
     private CIDSubset subset = new CIDSubset();
 
-    /**
-     * A map from Unicode indices to glyph indices. No assumption
-     * about ordering is made below. If lookup is changed to a binary
-     * search (from the current linear search), then addPrivateUseMapping()
-     * needs to be changed to perform ordered inserts.
-     */
-    private BFEntry[] bfentries = null;
-
     /* advanced typographic support */
     private GlyphDefinitionTable gdef;
     private GlyphSubstitutionTable gsub;
@@ -82,26 +74,31 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public int getDefaultWidth() {
         return defaultWidth;
     }
 
     /** {@inheritDoc} */
+    @Override
     public String getRegistry() {
         return "Adobe";
     }
 
     /** {@inheritDoc} */
+    @Override
     public String getOrdering() {
         return "UCS";
     }
 
     /** {@inheritDoc} */
+    @Override
     public int getSupplement() {
         return 0;
     }
 
     /** {@inheritDoc} */
+    @Override
     public CIDFontType getCIDType() {
         return cidType;
     }
@@ -115,6 +112,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public String getEmbedFontName() {
         if (isEmbeddable()) {
             return FontUtil.stripWhiteSpace(super.getFontName());
@@ -128,17 +126,18 @@
         return !(getEmbedFileName() == null && getEmbedResourceName() == null);
     }
 
-    /** {@inheritDoc} */
     public boolean isSubsetEmbedded() {
         return true;
     }
 
     /** {@inheritDoc} */
+    @Override
     public CIDSubset getCIDSubset() {
         return this.subset;
     }
 
     /** {@inheritDoc} */
+    @Override
     public String getEncodingName() {
         return encoding;
     }
@@ -171,30 +170,30 @@
         int idx = c;
         int retIdx = SingleByteEncoding.NOT_FOUND_CODE_POINT;
 
-        for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
-            if (bfentries[i].getUnicodeStart() <= idx
-                && bfentries[i].getUnicodeEnd() >= idx) {
+        for (int i = 0; (i < cmap.length) && retIdx == 0; i++) {
+            if (cmap[i].getUnicodeStart() <= idx
+                    && cmap[i].getUnicodeEnd() >= idx) {
 
-                retIdx = bfentries[i].getGlyphStartIndex()
+                retIdx = cmap[i].getGlyphStartIndex()
                     + idx
-                    - bfentries[i].getUnicodeStart();
+                    - cmap[i].getUnicodeStart();
             }
         }
         return retIdx;
     }
 
     /**
-     * Add a private use mapping {PU,GI} to the existing BFENTRIES map.
+     * Add a private use mapping {PU,GI} to the existing character map.
      * N.B. Does not insert in order, merely appends to end of existing map.
      */
     private synchronized void addPrivateUseMapping ( int pu, int gi ) {
         assert findGlyphIndex ( pu ) == SingleByteEncoding.NOT_FOUND_CODE_POINT;
-        BFEntry[] bfeOld = bfentries;
-        int       bfeCnt = bfeOld.length;
-        BFEntry[] bfeNew = new BFEntry [ bfeCnt + 1 ];
-        System.arraycopy ( bfeOld, 0, bfeNew, 0, bfeCnt );
-        bfeNew [ bfeCnt ] = new BFEntry ( pu, pu, gi );
-        bfentries = bfeNew;
+        CMapSegment[] oldCmap = cmap;
+        int cmapLength = oldCmap.length;
+        CMapSegment[] newCmap = new CMapSegment [ cmapLength + 1 ];
+        System.arraycopy ( oldCmap, 0, newCmap, 0, cmapLength );
+        newCmap [ cmapLength ] = new CMapSegment ( pu, pu, gi );
+        cmap = newCmap;
     }
 
     /**
@@ -252,12 +251,12 @@
     // [TBD] - needs optimization, i.e., change from linear search to binary search
     private int findCharacterFromGlyphIndex ( int gi, boolean augment ) {
         int cc = 0;
-        for ( int i = 0, n = bfentries.length; i < n; i++ ) {
-            BFEntry be = bfentries [ i ];
-            int s = be.getGlyphStartIndex();
-            int e = s + ( be.getUnicodeEnd() - be.getUnicodeStart() );
+        for ( int i = 0, n = cmap.length; i < n; i++ ) {
+            CMapSegment segment = cmap [ i ];
+            int s = segment.getGlyphStartIndex();
+            int e = s + ( segment.getUnicodeEnd() - segment.getUnicodeStart() );
             if ( ( gi >= s ) && ( gi <= e ) ) {
-                cc = be.getUnicodeStart() + ( gi - s );
+                cc = segment.getUnicodeStart() + ( gi - s );
                 break;
             }
         }
@@ -273,6 +272,7 @@
 
 
     /** {@inheritDoc} */
+    @Override
     public char mapChar(char c) {
         notifyMapOperation();
         int glyphIndex = findGlyphIndex(c);
@@ -287,20 +287,12 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean hasChar(char c) {
         return (findGlyphIndex(c) != SingleByteEncoding.NOT_FOUND_CODE_POINT);
     }
 
     /**
-     * Sets the array of BFEntry instances which constitutes the Unicode to glyph index map for
-     * a font. ("BF" means "base font")
-     * @param entries the Unicode to glyph index map
-     */
-    public void setBFEntries(BFEntry[] entries) {
-        this.bfentries = entries;
-    }
-
-    /**
      * Sets the defaultWidth.
      * @param defaultWidth The defaultWidth to set
      */
diff --git a/src/java/org/apache/fop/fonts/MutableFont.java b/src/java/org/apache/fop/fonts/MutableFont.java
index 41c552a..3ebc3c4 100644
--- a/src/java/org/apache/fop/fonts/MutableFont.java
+++ b/src/java/org/apache/fop/fonts/MutableFont.java
@@ -61,6 +61,12 @@
     void setEmbedResourceName(String name);
 
     /**
+     * Sets the embedding mode.
+     * @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/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java
index d590129..1aed72d 100644
--- a/src/java/org/apache/fop/fonts/SingleByteFont.java
+++ b/src/java/org/apache/fop/fonts/SingleByteFont.java
@@ -31,6 +31,8 @@
 
 import org.apache.xmlgraphics.fonts.Glyphs;
 
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+
 /**
  * Generic SingleByte font
  */
@@ -48,6 +50,7 @@
     private List<SimpleSingleByteEncoding> additionalEncodings;
     private Map<Character, Character> alternativeCodes;
 
+    private PostScriptVersion ttPostScriptVersion;
 
     /**
      * Main constructor.
@@ -397,5 +400,26 @@
         }
     }
 
+    /**
+     * Sets the version of the PostScript table stored in the TrueType font represented by
+     * this instance.
+     *
+     * @param version version of the <q>post</q> table
+     */
+    public void setTrueTypePostScriptVersion(PostScriptVersion version) {
+        ttPostScriptVersion = version;
+    }
+
+    /**
+     * Returns the version of the PostScript table stored in the TrueType font represented by
+     * this instance.
+     *
+     * @return the version of the <q>post</q> table
+     */
+    public PostScriptVersion getTrueTypePostScriptVersion() {
+        assert getFontType() == FontType.TRUETYPE;
+        return ttPostScriptVersion;
+    }
+
 }
 
diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java
index 6acb490..224c8de 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;
 
@@ -34,9 +33,9 @@
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.Version;
+import org.apache.fop.fonts.CMapSegment;
 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;
 
@@ -288,9 +287,9 @@
             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));
@@ -386,9 +385,7 @@
 
         el = doc.createElement("bfranges");
         mel.appendChild(el);
-        Iterator iter = ttf.getCMaps().listIterator();
-        while (iter.hasNext()) {
-            TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+        for (CMapSegment ce : ttf.getCMaps()) {
             Element el2 = doc.createElement("bf");
             el.appendChild(el2);
             el2.setAttribute("us", String.valueOf(ce.getUnicodeStart()));
@@ -443,31 +440,28 @@
         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 bd0a33c..deee4b0 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -32,6 +32,7 @@
 
 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;
@@ -222,7 +223,7 @@
                 }
                 try {
                     TTFFontLoader ttfLoader = new TTFFontLoader(
-                            fontFileURL, fontName, true, EncodingMode.AUTO,
+                            fontFileURL, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO,
                             useKerning, useAdvanced, resolver);
                     customFont = ttfLoader.getFont();
                     if (this.eventListener != null) {
@@ -247,7 +248,8 @@
         } 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 b971209..5da25e4 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 @@
     }
 
     /**
-     * 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 @@
      * @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);
         }
@@ -278,14 +268,14 @@
     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");
     }
 
@@ -353,6 +343,11 @@
         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;
+    }
 }
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 897d5e2..0000000
--- a/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
+++ /dev/null
@@ -1,118 +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 {
-
-    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}
-     */
-    public int hashCode() {
-        int hc = super.hashCode();
-        hc ^= ( hc * 11 ) + unicodeStart;
-        hc ^= ( hc * 19 ) + unicodeEnd;
-        hc ^= ( hc * 23 ) + glyphStartIndex;
-        return hc;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    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 1f05ebf..c273d44 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 @@
     private long offset;
     private long length;
 
+    public TTFDirTabEntry() {
+    }
+
+    public TTFDirTabEntry(long offset, long length) {
+        this.offset = offset;
+        this.length = length;
+    }
+
     /**
      * Read Dir Tab.
      * @param in font file reader
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
index 65ab560..a1d012a 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
@@ -20,11 +20,18 @@
 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;
@@ -36,6 +43,7 @@
 import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
 import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
 import org.apache.fop.complexscripts.fonts.OTFAdvancedTypographicTableReader;
+import org.apache.fop.fonts.CMapSegment;
 import org.apache.fop.fonts.FontUtil;
 
 /**
@@ -46,10 +54,100 @@
 public class TTFFile {
 
     static final byte NTABS = 24;
-    static final int NMACGLYPHS = 258;
     static final int MAX_CHAR_CODE = 255;
     static final int ENC_BUF_SIZE = 1024;
 
+    private static final String[] MAC_GLYPH_ORDERING = {
+        /* 0x000 */
+        ".notdef",          ".null",         "nonmarkingreturn", "space",
+        "exclam",           "quotedbl",      "numbersign",       "dollar",
+        "percent",          "ampersand",     "quotesingle",      "parenleft",
+        "parenright",       "asterisk",      "plus",             "comma",
+        /* 0x010 */
+        "hyphen",           "period",        "slash",            "zero",
+        "one",              "two",           "three",            "four",
+        "five",             "six",           "seven",            "eight",
+        "nine",             "colon",         "semicolon",        "less",
+        /* 0x020 */
+        "equal",            "greater",       "question",         "at",
+        "A",                "B",             "C",                "D",
+        "E",                "F",             "G",                "H",
+        "I",                "J",             "K",                "L",
+        /* 0x030 */
+        "M",                "N",             "O",                "P",
+        "Q",                "R",             "S",                "T",
+        "U",                "V",             "W",                "X",
+        "Y",                "Z",             "bracketleft",      "backslash",
+        /* 0x040 */
+        "bracketright",     "asciicircum",   "underscore",       "grave",
+        "a",                "b",             "c",                "d",
+        "e",                "f",             "g",                "h",
+        "i",                "j",             "k",                "l",
+        /* 0x050 */
+        "m",                "n",             "o",                "p",
+        "q",                "r",             "s",                "t",
+        "u",                "v",             "w",                "x",
+        "y",                "z",             "braceleft",        "bar",
+        /* 0x060 */
+        "braceright",       "asciitilde",    "Adieresis",        "Aring",
+        "Ccedilla",         "Eacute",        "Ntilde",           "Odieresis",
+        "Udieresis",        "aacute",        "agrave",           "acircumflex",
+        "adieresis",        "atilde",        "aring",            "ccedilla",
+        /* 0x070 */
+        "eacute",           "egrave",        "ecircumflex",      "edieresis",
+        "iacute",           "igrave",        "icircumflex",      "idieresis",
+        "ntilde",           "oacute",        "ograve",           "ocircumflex",
+        "odieresis",        "otilde",        "uacute",           "ugrave",
+        /* 0x080 */
+        "ucircumflex",      "udieresis",     "dagger",           "degree",
+        "cent",             "sterling",      "section",          "bullet",
+        "paragraph",        "germandbls",    "registered",       "copyright",
+        "trademark",        "acute",         "dieresis",         "notequal",
+        /* 0x090 */
+        "AE",               "Oslash",        "infinity",         "plusminus",
+        "lessequal",        "greaterequal",  "yen",              "mu",
+        "partialdiff",      "summation",     "product",          "pi",
+        "integral",         "ordfeminine",   "ordmasculine",     "Omega",
+        /* 0x0A0 */
+        "ae",               "oslash",        "questiondown",     "exclamdown",
+        "logicalnot",       "radical",       "florin",           "approxequal",
+        "Delta",            "guillemotleft", "guillemotright",   "ellipsis",
+        "nonbreakingspace", "Agrave",        "Atilde",           "Otilde",
+        /* 0x0B0 */
+        "OE",               "oe",            "endash",           "emdash",
+        "quotedblleft",     "quotedblright", "quoteleft",        "quoteright",
+        "divide",           "lozenge",       "ydieresis",        "Ydieresis",
+        "fraction",         "currency",      "guilsinglleft",    "guilsinglright",
+        /* 0x0C0 */
+        "fi",               "fl",            "daggerdbl",        "periodcentered",
+        "quotesinglbase",   "quotedblbase",  "perthousand",      "Acircumflex",
+        "Ecircumflex",      "Aacute",        "Edieresis",        "Egrave",
+        "Iacute",           "Icircumflex",   "Idieresis",        "Igrave",
+        /* 0x0D0 */
+        "Oacute",           "Ocircumflex",   "apple",            "Ograve",
+        "Uacute",           "Ucircumflex",   "Ugrave",           "dotlessi",
+        "circumflex",       "tilde",         "macron",           "breve",
+        "dotaccent",        "ring",          "cedilla",          "hungarumlaut",
+        /* 0x0E0 */
+        "ogonek",           "caron",         "Lslash",           "lslash",
+        "Scaron",           "scaron",        "Zcaron",           "zcaron",
+        "brokenbar",        "Eth",           "eth",              "Yacute",
+        "yacute",           "Thorn",         "thorn",            "minus",
+        /* 0x0F0 */
+        "multiply",         "onesuperior",   "twosuperior",      "threesuperior",
+        "onehalf",          "onequarter",    "threequarters",    "franc",
+        "Gbreve",           "gbreve",        "Idotaccent",       "Scedilla",
+        "scedilla",         "Cacute",        "cacute",           "Ccaron",
+        /* 0x100 */
+        "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;
+
     private final String encoding = "WinAnsiEncoding";    // Default encoding
 
     private final short firstChar = 0;
@@ -61,33 +159,31 @@
     /**
      * 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 cmaps;
-    private Set unicodeMappings;
+    private List<CMapSegment> cmaps;
+    private Set<UnicodeMapping> unicodeMappings;
 
     private int upem;                                // unitsPerEm from "head" table
     private int nhmtx;                               // Number of horizontal metrics
-    private int postFormat;
+    private PostScriptVersion postScriptVersion;
     private int locaFormat;
     /**
      * Offset to last loca
      */
     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;
@@ -116,12 +212,12 @@
     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 final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer> ();
+    private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer> ();
 
     private TTFDirTabEntry currentDirTab;
 
@@ -136,6 +232,10 @@
      */
     protected Log log = LogFactory.getLog(TTFFile.class);
 
+    public TTFFile() {
+        this(true, false);
+    }
+
     /**
      * Constructor
      * @param useKerning true if kerning data should be loaded
@@ -147,9 +247,9 @@
     }
 
     /**
-     * Key-value helper class
+     * Key-value helper class.
      */
-    class UnicodeMapping implements Comparable {
+    final class UnicodeMapping implements Comparable {
 
         private final int unicodeIndex;
         private final int glyphIndex;
@@ -217,12 +317,26 @@
     }
 
     /**
+     * Version of the PostScript table (<q>post</q>) contained in this font.
+     */
+    public static enum PostScriptVersion {
+        /** PostScript table version 1.0. */
+        V1,
+        /** PostScript table version 2.0. */
+        V2,
+        /** PostScript table version 3.0. */
+        V3,
+        /** Unknown version of the PostScript table. */
+        UNKNOWN;
+    }
+
+    /**
      * Obtain directory table entry.
      * @param name (tag) of entry
      * @return a directory table entry or null if none found
      */
-    public TTFDirTabEntry getDirectoryEntry ( String name ) {
-        return (TTFDirTabEntry) dirTabs.get ( name );
+    public TTFDirTabEntry getDirectoryEntry(TTFTableName name) {
+        return dirTabs.get(name);
     }
 
     /**
@@ -234,11 +348,11 @@
      * @return true if seek succeeded
      * @throws IOException if I/O exception occurs during seek
      */
-    public boolean seekTab(FontFileReader in, String name,
+    public boolean seekTab(FontFileReader in, TTFTableName tableName,
                   long offset) throws IOException {
-        TTFDirTabEntry dt = getDirectoryEntry ( 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);
@@ -274,12 +388,12 @@
      * Set the unicodeIndex in the TTFMtxEntries and fills in the
      * cmaps vector.
      */
-    private boolean readCMAP(FontFileReader in) throws IOException {
+    private boolean readCMAP() throws IOException {
 
         unicodeMappings = new java.util.TreeSet();
 
-        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;
 
@@ -289,9 +403,9 @@
 
         //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);
@@ -306,9 +420,9 @@
         }
 
         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");
@@ -317,26 +431,26 @@
     }
 
     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);
@@ -352,26 +466,26 @@
             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);
 
@@ -413,19 +527,17 @@
                                     + (j - cmapStartCounts[i])
                                     + (i)
                                     - cmapSegCountX2 / 2) * 2;
-                            in.seekSet(glyphOffset);
-                            glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+                            fontFile.seekSet(glyphOffset);
+                            glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
                                        & 0xffff;
 
                             unicodeMappings.add(new UnicodeMapping(glyphIdx, j));
                             mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
 
                             // 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();
 
@@ -466,11 +578,9 @@
                             }
 
                             // 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();
                                 }
                             }
@@ -548,14 +658,14 @@
         // 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);
@@ -574,12 +684,12 @@
      * @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 "
@@ -590,26 +700,26 @@
             }
         }
 
-        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;
         }
@@ -617,7 +727,7 @@
         createCMaps();
 
         if ( useKerning ) {
-            readKerning(in);
+            readKerning();
         }
 
         // Read advanced typographic tables.
@@ -640,33 +750,47 @@
         return true;
     }
 
-    private void createCMaps() {
-        cmaps = new java.util.ArrayList();
-        TTFCmapEntry tce = new TTFCmapEntry();
+    /**
+     * 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);
+    }
 
-        Iterator e = unicodeMappings.iterator();
-        UnicodeMapping um = (UnicodeMapping)e.next();
+    private void createCMaps() {
+        cmaps = new ArrayList<CMapSegment>();
+        int unicodeStart;
+        int glyphStart;
+        int unicodeEnd;
+
+        Iterator<UnicodeMapping> e = unicodeMappings.iterator();
+        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 CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+                unicodeStart = um.getUnicodeIndex();
+                glyphStart = um.getGlyphIndex();
             }
             lastMapping = um;
         }
 
-        tce.setUnicodeEnd(um.getUnicodeIndex());
-        cmaps.add(tce);
+        unicodeEnd = lastMapping.getUnicodeIndex();
+        cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
     }
 
     /**
@@ -681,11 +805,15 @@
         }
     }
 
+    PostScriptVersion getPostScriptVersion() {
+        return postScriptVersion;
+    }
+
     /**
      * 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;
     }
 
@@ -730,19 +858,30 @@
     }
 
     /**
+     * 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;
     }
@@ -780,7 +919,6 @@
     }
 
     /**
-     * Returns the font bounding box.
      * @return int[] The font bbox
      */
     public int[] getFontBBox() {
@@ -886,11 +1024,10 @@
      * 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");
@@ -909,42 +1046,45 @@
             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.TABLE_DIRECTORY,
+                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
@@ -952,19 +1092,18 @@
                     + " 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();
     }
 
 
@@ -972,17 +1111,16 @@
      * 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));
@@ -996,12 +1134,11 @@
      * 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];
@@ -1013,8 +1150,8 @@
             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 (log.isTraceEnabled()) {
                 log.trace("   width[" + i + "] = "
@@ -1027,7 +1164,7 @@
             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());
             }
         }
     }
@@ -1037,35 +1174,37 @@
      * Read the "post" table
      * containing the PostScript names of the glyphs.
      */
-    private void readPostScript(FontFileReader in) throws IOException {
-        seekTab(in, "post", 0);
-        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) {
         case 0x00010000:
             log.debug("PostScript format 1");
-            for (int i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
-                mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
+            postScriptVersion = PostScriptVersion.V1;
+            for (int i = 0; i < MAC_GLYPH_ORDERING.length; i++) {
+                mtxTab[i].setName(MAC_GLYPH_ORDERING[i]);
             }
             break;
         case 0x00020000:
             log.debug("PostScript format 2");
+            postScriptVersion = PostScriptVersion.V2;
             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
@@ -1085,16 +1224,16 @@
                         + " 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
             for (int i = 0; i < l; i++) {
-                if (mtxTab[i].getIndex() < NMACGLYPHS) {
-                    mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
+                if (mtxTab[i].getIndex() < MAC_GLYPH_ORDERING.length) {
+                    mtxTab[i].setName(MAC_GLYPH_ORDERING[mtxTab[i].getIndex()]);
                 } else {
                     if (!mtxTab[i].isIndexReserved()) {
-                        int k = mtxTab[i].getIndex() - NMACGLYPHS;
+                        int k = mtxTab[i].getIndex() - MAC_GLYPH_ORDERING.length;
 
                         if (log.isTraceEnabled()) {
                             log.trace(k + " i=" + i + " mtx=" + mtxTab.length
@@ -1110,9 +1249,11 @@
         case 0x00030000:
             // PostScript format 3 contains no glyph names
             log.debug("PostScript format 3");
+            postScriptVersion = PostScriptVersion.V3;
             break;
         default:
             log.error("Unknown PostScript format: " + postFormat);
+            postScriptVersion = PostScriptVersion.UNKNOWN;
         }
     }
 
@@ -1120,60 +1261,60 @@
     /**
      * Read the "OS/2" table
      */
-    private void readOS2(FontFileReader in) throws IOException {
+    private void readOS2() throws IOException {
         // Check if font is embeddable
-        TTFDirTabEntry os2Entry = getDirectoryEntry ( "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);
@@ -1187,42 +1328,40 @@
 
     /**
      * 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 = getDirectoryEntry ( "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());
@@ -1230,17 +1369,17 @@
         }
 
 
-        long n = dirTab.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)*/
@@ -1261,34 +1400,33 @@
 
     /**
      * 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()) {
@@ -1332,21 +1470,20 @@
 
     /**
      * 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 = getDirectoryEntry ( "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) {
@@ -1476,19 +1613,18 @@
     /**
      * 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 = getDirectoryEntry ( "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;
                 }
@@ -1496,12 +1632,12 @@
                     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);
@@ -1515,9 +1651,9 @@
                             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);
@@ -1529,16 +1665,12 @@
             // 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);
 
@@ -1567,10 +1699,71 @@
     }
 
     /**
-     * Return a List with TTFCmapEntry.
-     * @return A list of TTFCmapEntry objects
+     * Streams a font.
+     * @param ttfOut The interface for streaming TrueType tables.
+     * @exception IOException file write error
      */
-    public List getCMaps() {
+    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();
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Returns this font's character to glyph mapping.
+     *
+     * @return the font's cmap
+     */
+    public List<CMapSegment> getCMaps() {
         return cmaps;
     }
 
@@ -1579,24 +1772,23 @@
      * 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 "
@@ -1610,10 +1802,10 @@
             // 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;
@@ -1631,10 +1823,10 @@
                 subFamilyName = "";
             }
 
-            in.seekSet(dirTabOffset);
+            fontFile.seekSet(dirTabOffset);
             return found;
         } else {
-            in.seekSet(0);
+            fontFile.seekSet(0);
             return true;
         }
     }
@@ -1646,8 +1838,7 @@
      * @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)) {
@@ -1667,9 +1858,9 @@
 
             for (int i = 0; (i < numDirectories); i++) {
                 in.seekSet(dirOffsets[i]);
-                readDirTabs(in);
+                readDirTabs();
 
-                readName(in);
+                readName();
 
                 log.info(fullName);
                 fontNames.add(fullName);
@@ -1695,13 +1886,13 @@
      * 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]);
     }
 
     /**
@@ -1744,7 +1935,7 @@
      * @return unicode code point
      */
     private Integer glyphToUnicode(int glyphIndex) {
-        return (Integer) glyphToUnicodeMap.get(new Integer(glyphIndex));
+        return glyphToUnicodeMap.get(new Integer(glyphIndex));
     }
 
     /**
@@ -1755,7 +1946,7 @@
      */
     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);
@@ -1763,6 +1954,10 @@
         return result;
     }
 
+    String getGlyphName(int glyphIndex) {
+        return mtxTab[glyphIndex].getName();
+    }
+
     /**
      * Determine if advanced (typographic) table is present.
      * @return true if advanced (typographic) table is present
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
index c03f0fb..1410239 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
@@ -21,17 +21,14 @@
 
 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.xmlgraphics.fonts.Glyphs;
-
-import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CMapSegment;
+import org.apache.fop.fonts.EmbeddingMode;
 import org.apache.fop.fonts.EncodingMode;
 import org.apache.fop.fonts.FontLoader;
 import org.apache.fop.fonts.FontResolver;
@@ -39,6 +36,8 @@
 import org.apache.fop.fonts.MultiByteFont;
 import org.apache.fop.fonts.NamedCharacter;
 import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.util.HexEncoder;
 
 /**
  * Loads a TrueType font into memory directly from the original font file.
@@ -49,6 +48,7 @@
     private SingleByteFont singleFont;
     private final String subFontName;
     private EncodingMode encodingMode;
+    private EmbeddingMode embeddingMode;
 
     /**
      * Default constructor
@@ -56,7 +56,7 @@
      * @param resolver the FontResolver for font URI resolution
      */
     public TTFFontLoader(String fontFileURI, FontResolver resolver) {
-        this(fontFileURI, null, true, EncodingMode.AUTO, true, true, resolver);
+        this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resolver);
     }
 
     /**
@@ -65,24 +65,28 @@
      * @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 useAdvanced true to enable loading advanced 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,
-                boolean useAdvanced, FontResolver resolver) {
+                boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+                boolean useKerning, boolean useAdvanced, FontResolver resolver) {
         super(fontFileURI, embedded, useKerning, useAdvanced, 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);
     }
@@ -145,29 +149,20 @@
         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();
             multiFont.setWidthArray(wx);
-            List entries = ttf.getCMaps();
-            BFEntry[] bfentries = new BFEntry[entries.size()];
-            int pos = 0;
-            Iterator iter = ttf.getCMaps().listIterator();
-            while (iter.hasNext()) {
-                TTFCmapEntry ce = (TTFCmapEntry)iter.next();
-                bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(),
-                        ce.getGlyphStartIndex());
-                pos++;
-            }
-            multiFont.setBFEntries(bfentries);
         } else {
             singleFont.setFontType(FontType.TRUETYPE);
             singleFont.setEncoding(ttf.getCharSetName());
             returnFont.setFirstChar(ttf.getFirstChar());
             returnFont.setLastChar(ttf.getLastChar());
+            singleFont.setTrueTypePostScriptVersion(ttf.getPostScriptVersion());
             copyWidthsSingleByte(ttf);
         }
+        returnFont.setCMap(getCMap(ttf));
 
         if (useKerning) {
             copyKerning(ttf, isCid);
@@ -186,23 +181,30 @@
         }
     }
 
+    private CMapSegment[] getCMap(TTFFile ttf) {
+        CMapSegment[] array = new CMapSegment[ttf.getCMaps().size()];
+        return ttf.getCMaps().toArray(array);
+    }
+
     private void copyWidthsSingleByte(TTFFile ttf) {
         int[] wx = ttf.getWidths();
         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();
-            if (ce.getUnicodeStart() < 0xFFFE) {
-                for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) {
+
+        for (CMapSegment segment : ttf.getCMaps()) {
+            if (segment.getUnicodeStart() < 0xFFFE) {
+                for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) {
                     int codePoint = singleFont.getEncoding().mapChar(u);
                     if (codePoint <= 0) {
-                        String unicode = Character.toString(u);
-                        String charName = Glyphs.stringToGlyph(unicode);
-                        if (charName.length() > 0) {
-                            NamedCharacter nc = new NamedCharacter(charName, unicode);
-                            int glyphIndex = ce.getGlyphStartIndex() + u - ce.getUnicodeStart();
+                        int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
+                        String glyphName = ttf.getGlyphName(glyphIndex);
+                        if (glyphName.length() == 0 && ttf.getPostScriptVersion() != PostScriptVersion.V2) {
+                            glyphName = "u" + HexEncoder.encode(u);
+                        }
+                        if (glyphName.length() > 0) {
+                            String unicode = Character.toString(u);
+                            NamedCharacter nc = new NamedCharacter(glyphName, unicode);
                             singleFont.addUnencodedCharacter(nc, wx[glyphIndex]);
                         }
                     }
@@ -225,7 +227,6 @@
         }
 
         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 0000000..313d583
--- /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;
+
+/**
+ * An interface for writing individual glyphs from the glyf table of a TrueType font to an output stream.
+ */
+public interface TTFGlyphOutputStream {
+
+    /**
+     * Begins the streaming of glyphs.
+     */
+    void startGlyphStream() throws IOException;
+
+    /**
+     * Streams an individual glyph from the given byte array.
+     *
+     * @param glyphData the source of the glyph data to stream from
+     * @param offset the position in the glyph data where the glyph starts
+     * @param size the size of the glyph data in bytes
+     */
+    void streamGlyph(byte[] glyphData, int offset, int size) throws IOException;
+
+    /**
+     * Ends the streaming of glyphs.
+     */
+    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 0000000..09b5b6f
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
@@ -0,0 +1,49 @@
+/*
+ * 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 writing a TrueType font to an output stream.
+ */
+public interface TTFOutputStream {
+
+    /**
+     * Starts writing the font.
+     */
+    void startFontStream() throws IOException;
+
+    /**
+     * Returns an object for streaming TrueType tables.
+     */
+    TTFTableOutputStream getTableOutputStream();
+
+    /**
+     * Returns an object for streaming TrueType glyphs in the glyf table.
+     */
+    TTFGlyphOutputStream getGlyphOutputStream();
+
+    /**
+     * Ends writing the font.
+     */
+    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 d400c0b..c83aaab 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.Iterator;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.SortedSet;
 
 
 /**
@@ -42,24 +44,18 @@
      * Offsets in name table to be filled out by table.
      * The offsets are to the checkSum field
      */
-    private int cvtDirOffset = 0;
-    private int fpgmDirOffset = 0;
-    private int glyfDirOffset = 0;
-    private int headDirOffset = 0;
-    private int hheaDirOffset = 0;
-    private int hmtxDirOffset = 0;
-    private int locaDirOffset = 0;
-    private int maxpDirOffset = 0;
-    private int prepDirOffset = 0;
+    private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>();
 
     private int checkSumAdjustmentOffset = 0;
     private int locaOffset = 0;
 
+    /** Stores the glyph offsets so that we can end strings at glyph boundaries */
+    private int[] glyphOffsets;
+
     /**
      * Default Constructor
      */
     public TTFSubSetFile() {
-        this(false, false);
     }
 
     /**
@@ -78,17 +74,19 @@
         output = new byte[size];
         realSize = 0;
         currentPos = 0;
-
-        // createDirectory()
     }
 
+    /** 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 {
-            numTables += 2; //1 req'd table: glyf,loca
+            numTables += 5; //5 req'd tables: glyf,loca,post,name,OS/2
             if (hasCvt()) {
                 numTables++;
             }
@@ -119,7 +117,7 @@
 
         // 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;
 
@@ -128,151 +126,122 @@
 
         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 ");
-            cvtDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
+            writeTableName(TTFTableName.CVT);
         }
-
         if (hasFpgm()) {
-            writeString("fpgm");
-            fpgmDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
+            writeTableName(TTFTableName.FPGM);
         }
-
-        writeString("glyf");
-        glyfDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("head");
-        headDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("hhea");
-        hheaDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("hmtx");
-        hmtxDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("loca");
-        locaDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("maxp");
-        maxpDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
+        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()) {
-            writeString("prep");
-            prepDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
+            writeTableName(TTFTableName.PREP);
         }
+        newDirTabs.put(TTFTableName.TABLE_DIRECTORY, new TTFDirTabEntry(0, currentPos));
     }
 
-
-    /**
-     * Copy the cvt table as is from original font to subset font
-     */
-    private boolean createCvt(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "cvt ", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(cvtDirOffset, checksum);
-            writeULong(cvtDirOffset + 4, currentPos);
-            writeULong(cvtDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-            return true;
-        } else {
-            return false;
-            //throw new IOException("Can't find cvt table");
-        }
+    private void writeTableName(TTFTableName tableName) {
+        writeString(tableName.getName());
+        offsets.put(tableName, currentPos);
+        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);
     }
 
     /**
-     * Copy the fpgm table as is from original font to subset font
-     */
-    private boolean createFpgm(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "fpgm", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(fpgmDirOffset, checksum);
-            writeULong(fpgmDirOffset + 4, currentPos);
-            writeULong(fpgmDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-
-
-    /**
      * Create an empty loca table without updating checksum
      */
     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, 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());
+
+            updateCheckSum(currentPos, (int) entry.getLength(), tableName);
+            currentPos += (int) entry.getLength();
+            realSize += (int) entry.getLength();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Copy the cvt table as is from original font to subset font
+     */
+    private boolean createCvt(FontFileReader in) throws IOException {
+        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, TTFTableName.FPGM);
+    }
+
+    /**
+     * Copy the name table as is from the original.
+     */
+    private boolean createName(FontFileReader in) throws IOException {
+        return copyTable(in, TTFTableName.NAME);
+    }
+
+    /**
+     * Copy the OS/2 table as is from the original.
+     */
+    private boolean createOS2(FontFileReader in) throws IOException {
+        return copyTable(in, TTFTableName.OS2);
+    }
 
     /**
      * Copy the maxp table as is from original font to subset font
      * 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 {
@@ -280,28 +249,34 @@
         }
     }
 
+    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 {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "prep", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(prepDirOffset, checksum);
-            writeULong(prepDirOffset + 4, currentPos);
-            writeULong(prepDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-            return true;
-        } else {
-            return false;
-        }
+        return copyTable(in, TTFTableName.PREP);
     }
 
 
@@ -310,20 +285,17 @@
      * and fill in size of hmtx table
      */
     private void createHhea(FontFileReader in, int size) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
+        TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA);
         if (entry != null) {
             pad4();
-            seekTab(in, "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);
+            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);
 
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(hheaDirOffset, checksum);
-            writeULong(hheaDirOffset + 4, currentPos);
-            writeULong(hheaDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
+            updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA);
+            currentPos += (int) entry.getLength();
+            realSize += (int) entry.getLength();
         } else {
             throw new IOException("Can't find hhea table");
         }
@@ -337,10 +309,11 @@
      * 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());
 
@@ -352,11 +325,7 @@
             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 {
@@ -369,30 +338,24 @@
      * Create the glyf table and fill in loca table
      */
     private void createGlyf(FontFileReader in,
-                            Map 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 start = 0;
+        int startPos = 0;
         int endOffset = 0;    // Store this as the last loca
         if (entry != null) {
             pad4();
-            start = currentPos;
+            startPos = currentPos;
 
             /* Loca table must be in order by glyph index, so build
              * an array first and then write the glyph info and
              * location offset.
              */
-            int[] origIndexes = new int[glyphs.size()];
-
-            Iterator e = glyphs.keySet().iterator();
-            while (e.hasNext()) {
-                Integer origIndex = (Integer)e.next();
-                Integer subsetIndex = (Integer)glyphs.get(origIndex);
-                origIndexes[subsetIndex.intValue()] = origIndex.intValue();
-            }
+            int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+            glyphOffsets = new int[origIndexes.length];
 
             for (int i = 0; i < origIndexes.length; i++) {
-                int glyphLength = 0;
                 int nextOffset = 0;
                 int origGlyphIndex = origIndexes[i];
                 if (origGlyphIndex >= (mtxTab.length - 1)) {
@@ -400,46 +363,64 @@
                 } else {
                     nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
                 }
-                glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
+                int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset();
+                int glyphLength = nextOffset - glyphOffset;
 
+                byte[] glyphData = in.getBytes(
+                        (int)entry.getOffset() + glyphOffset,
+                        glyphLength);
+                int endOffset1 = endOffset;
                 // Copy glyph
                 System.arraycopy(
-                    in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
-                        glyphLength), 0,
+                    glyphData, 0,
                     output, currentPos,
                     glyphLength);
 
 
                 // Update loca table
-                writeULong(locaOffset + i * 4, currentPos - start);
-                if ((currentPos - start + glyphLength) > endOffset) {
-                    endOffset = (currentPos - start + glyphLength);
+                writeULong(locaOffset + i * 4, currentPos - startPos);
+                if ((currentPos - startPos + glyphLength) > endOffset1) {
+                    endOffset1 = (currentPos - startPos + glyphLength);
                 }
 
+                // Store the glyph boundary positions relative to the start of the font
+                glyphOffsets[i] = currentPos;
                 currentPos += glyphLength;
                 realSize += glyphLength;
 
+
+                endOffset = endOffset1;
             }
 
-            size = currentPos - start;
 
-            int checksum = getCheckSum(start, size);
-            writeULong(glyfDirOffset, checksum);
-            writeULong(glyfDirOffset + 4, start);
-            writeULong(glyfDirOffset + 8, size);
+            size = currentPos - startPos;
+
             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");
         }
     }
 
+    private int[] buildSubsetIndexToOrigIndexMap(Map<Integer, Integer> glyphs) {
+        int[] origIndexes = new int[glyphs.size()];
+        for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
+            int origIndex = glyph.getKey();
+            int subsetIndex = glyph.getValue();
+            origIndexes[subsetIndex] = origIndex;
+        }
+        return origIndexes;
+    }
 
     /**
      * Create the hmtx table by copying metrics from original
@@ -448,8 +429,9 @@
      * metric (key) to the subset metric (value)
      */
     private void createHmtx(FontFileReader in,
-                            Map glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+                            Map<Integer, Integer> glyphs) throws IOException {
+        TTFTableName hmtx = TTFTableName.HMTX;
+        TTFDirTabEntry entry = dirTabs.get(hmtx);
 
         int longHorMetricSize = glyphs.size() * 2;
         int leftSideBearingSize = glyphs.size() * 2;
@@ -458,10 +440,9 @@
         if (entry != null) {
             pad4();
             //int offset = (int)entry.offset;
-            Iterator e = glyphs.keySet().iterator();
-            while (e.hasNext()) {
-                Integer origIndex = (Integer)e.next();
-                Integer subsetIndex = (Integer)glyphs.get(origIndex);
+            for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
+                Integer origIndex = glyph.getKey();
+                Integer subsetIndex = glyph.getValue();
 
                 writeUShort(currentPos + subsetIndex.intValue() * 4,
                             mtxTab[origIndex.intValue()].getWx());
@@ -469,10 +450,7 @@
                             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 {
@@ -481,43 +459,37 @@
     }
 
     /**
-     * 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();                // 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
@@ -531,6 +503,16 @@
             // fpgm is optional (used in TrueType fonts only)
             log.debug("TrueType: fpgm table not present. Skipped.");
         }
+        createLoca(subsetGlyphs.size());    // create empty loca table
+        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
+        createName(in);                         // copy the name table
+        createPost(in);                         // copy the post table
 
         optionalTableFound = createPrep(in);    // copy prep table
         if (!optionalTableFound) {
@@ -538,21 +520,59 @@
             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();
+    }
 
+    /**
+     * 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(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());
+            }
+        }
+        ttfOut.endFontStream();
+    }
+
     private void scanGlyphs(FontFileReader in, Map<Integer, Integer> subsetGlyphs)
             throws IOException {
-        TTFDirTabEntry glyfTableInfo = (TTFDirTabEntry) dirTabs.get("glyf");
+        TTFDirTabEntry glyfTableInfo = dirTabs.get(TTFTableName.GLYF);
         if (glyfTableInfo == null) {
             throw new IOException("Glyf table could not be found");
         }
@@ -610,20 +630,6 @@
         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,
@@ -641,40 +647,16 @@
     }
 
     /**
-     * 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++;
+            }
         }
     }
 
@@ -683,23 +665,25 @@
      */
     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 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 int getCheckSum(int start, int size) {
-        return (int)getLongCheckSum(start, size);
-    }
-
-    private long getLongCheckSum(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;
@@ -710,26 +694,19 @@
         long sum = 0;
 
         for (int i = 0; i < size; i += 4) {
-            int l = (output[start + i] << 24);
-            l += (output[start + i + 1] << 16);
-            l += (output[start + i + 2] << 16);
-            l += (output[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(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 0000000..e5ad631
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+
+/**
+ * Represents table names as found in a TrueType font's Table Directory.
+ * TrueType fonts may have custom tables so we cannot use an enum.
+ */
+public final class TTFTableName {
+
+    /** The first table in a TrueType font file containing metadata about other tables. */
+    public static final TTFTableName TABLE_DIRECTORY = new TTFTableName("tableDirectory");
+
+    /** 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 FontForge 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 Directory Table.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns an instance of this class corresponding to the given string representation.
+     * @param tableName table name as in the Table Directory
+     * @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());
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+}
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 0000000..d0d2007
--- /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 writing a TrueType table to an output stream.
+ */
+public interface TTFTableOutputStream {
+
+    /**
+     * Streams a table from the given byte array.
+     *
+     * @param ttfData the source of the table to stream from
+     * @param offset the position in the byte array where the table starts
+     * @param size the size of the table in bytes
+     */
+    void streamTable(byte[] ttfData, int offset, int size) throws IOException;
+}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index cc1d93d..beb384d 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -45,6 +45,7 @@
 
 import org.apache.xmlgraphics.java2d.color.ColorUtil;
 import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+
 import org.apache.xmlgraphics.xmp.Metadata;
 
 import org.apache.fop.fonts.CIDFont;
@@ -1674,8 +1675,8 @@
                         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 7ac350d..9c404be 100644
--- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
+++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
@@ -89,7 +89,8 @@
                     font = new CustomFontMetricsMapper(fontMetrics, fontSource);
                 } else {
                     CustomFont fontMetrics = FontLoader.loadFont(
-                            fontFile, null, true, EncodingMode.AUTO,
+                            fontFile, null, true, configFontInfo.getEmbeddingMode(),
+                            EncodingMode.AUTO,
                             configFontInfo.getKerning(),
                             configFontInfo.getAdvanced(), fontResolver);
                     font = new CustomFontMetricsMapper(fontMetrics);
diff --git a/src/java/org/apache/fop/render/ps/FontResourceCache.java b/src/java/org/apache/fop/render/ps/FontResourceCache.java
index 9d4090e..1514d20 100644
--- a/src/java/org/apache/fop/render/ps/FontResourceCache.java
+++ b/src/java/org/apache/fop/render/ps/FontResourceCache.java
@@ -42,19 +42,20 @@
     }
 
     /**
-     * Returns the PSResource for the given font key.
+     * Returns the PSFontResource for the given font key.
      * @param key the font key ("F*")
-     * @return the matching PSResource
+     * @return the matching PSFontResource instance
      */
-    public PSResource getPSResourceForFontKey(String key) {
-        PSResource res = null;
+    public PSFontResource getFontResourceForFontKey(String key) {
+        PSFontResource res = null;
         if (this.fontResources != null) {
-            res = (PSResource)this.fontResources.get(key);
+            res = (PSFontResource)this.fontResources.get(key);
         } else {
             this.fontResources = new java.util.HashMap();
         }
         if (res == null) {
-            res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
+            res = PSFontResource.createFontResource(
+                    new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key)));
             this.fontResources.put(key, res);
         }
         return res;
@@ -76,9 +77,9 @@
             throw new IllegalStateException("Font not available: " + key);
         }
         if (postFix == null) {
-            return tf.getFontName();
+            return tf.getEmbedFontName();
         } else {
-            return tf.getFontName() + postFix;
+            return tf.getEmbedFontName() + postFix;
         }
     }
 
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
index dc785ea..0fe5648 100644
--- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
@@ -50,6 +50,7 @@
 import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
 import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
 
+import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.MimeConstants;
 import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
 import org.apache.fop.render.intermediate.IFContext;
@@ -107,6 +108,8 @@
     private static final int COMMENT_PAGE_TRAILER = 2;
     private static final int PAGE_TRAILER_CODE_BEFORE = 3;
 
+    private PSEventProducer eventProducer;
+
     /**
      * Default constructor.
      */
@@ -126,7 +129,9 @@
     /** {@inheritDoc} */
     public void setContext(IFContext context) {
         super.setContext(context);
-        this.psUtil = new PSRenderingUtil(context.getUserAgent());
+        FOUserAgent userAgent = context.getUserAgent();
+        this.psUtil = new PSRenderingUtil(userAgent);
+        eventProducer = PSEventProducer.Provider.get(userAgent.getEventBroadcaster());
     }
 
     /** {@inheritDoc} */
@@ -145,7 +150,7 @@
         try {
             OutputStream out;
             if (psUtil.isOptimizeResources()) {
-                this.tempFile = File.createTempFile("fop", null);
+                this.tempFile = File.createTempFile("fop", ".ps");
                 out = new java.io.FileOutputStream(this.tempFile);
                 out = new java.io.BufferedOutputStream(out);
             } else {
@@ -203,7 +208,7 @@
         gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
         PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
         if (!psUtil.isOptimizeResources()) {
-            this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo));
+            this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo, eventProducer));
         } else {
             gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
         }
@@ -258,8 +263,8 @@
         in = new java.io.BufferedInputStream(in);
         try {
             try {
-                ResourceHandler handler = new ResourceHandler(getUserAgent(), this.fontInfo,
-                        resTracker, this.formResources);
+                ResourceHandler handler = new ResourceHandler(getUserAgent(), eventProducer,
+                        this.fontInfo, resTracker, this.formResources);
                 handler.process(in, this.outputStream,
                         this.currentPageNumber, this.documentBoundingBox);
                 this.outputStream.flush();
@@ -547,8 +552,8 @@
      * @param key the font key ("F*")
      * @return the matching PSResource
      */
-    protected PSResource getPSResourceForFontKey(String key) {
-        return this.fontResources.getPSResourceForFontKey(key);
+    protected PSFontResource getPSResourceForFontKey(String key) {
+        return this.fontResources.getFontResourceForFontKey(key);
     }
 
     /**
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.java b/src/java/org/apache/fop/render/ps/PSEventProducer.java
index 702380a..bffdf22 100644
--- a/src/java/org/apache/fop/render/ps/PSEventProducer.java
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.java
@@ -53,4 +53,11 @@
      */
     void postscriptDictionaryParseError(Object source, String content, Exception e);
 
+    /**
+     * PostScript Level 3 features are necessary.
+     *
+     * @param source the event source
+     * @event.severity FATAL
+     */
+    void postscriptLevel3Needed(Object source);
 }
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.xml b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
index bcd89ed..64b22d1 100644
--- a/src/java/org/apache/fop/render/ps/PSEventProducer.xml
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <catalogue xml:lang="en">
   <message key="postscriptDictionaryParseError">Failed to parse dictionary string. Reason: {e}, content = "{content}"</message>
+  <message key="postscriptLevel3Needed">PostScript Level 3 features are needed to handle this document.</message>
 </catalogue>
diff --git a/src/java/org/apache/fop/render/ps/PSFontResource.java b/src/java/org/apache/fop/render/ps/PSFontResource.java
new file mode 100644
index 0000000..8b7b835
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSFontResource.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
+
+/**
+ * A DSC resource corresponding to a font. This class handles the possible other resources
+ * that a font may depend on. For example, a CID-keyed font depends on a CIDFont resource, a
+ * CMap resource, and the ProcSet CIDInit resource.
+ */
+abstract class PSFontResource {
+
+    static PSFontResource createFontResource(final PSResource fontResource) {
+        return new PSFontResource() {
+
+            String getName() {
+                return fontResource.getName();
+            }
+
+            void notifyResourceUsageOnPage(ResourceTracker resourceTracker) {
+                resourceTracker.notifyResourceUsageOnPage(fontResource);
+            }
+        };
+    }
+
+    static PSFontResource createFontResource(final PSResource fontResource,
+            final PSResource procsetCIDInitResource, final PSResource cmapResource,
+            final PSResource cidFontResource) {
+        return new PSFontResource() {
+
+            String getName() {
+                return fontResource.getName();
+            }
+
+            void notifyResourceUsageOnPage(ResourceTracker resourceTracker) {
+                resourceTracker.notifyResourceUsageOnPage(fontResource);
+                resourceTracker.notifyResourceUsageOnPage(procsetCIDInitResource);
+                resourceTracker.notifyResourceUsageOnPage(cmapResource);
+                resourceTracker.notifyResourceUsageOnPage(cidFontResource);
+            }
+        };
+    }
+
+    /**
+     * Returns the name of the font resource.
+     *
+     * @return the name of the font
+     */
+    abstract String getName();
+
+    /**
+     * Notifies the given resource tracker of all the resources needed by this font.
+     *
+     * @param resourceTracker
+     */
+    abstract void notifyResourceUsageOnPage(ResourceTracker resourceTracker);
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java
index 5e95b5d..d0d7574 100644
--- a/src/java/org/apache/fop/render/ps/PSFontUtils.java
+++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java
@@ -23,7 +23,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import javax.xml.transform.Source;
 import javax.xml.transform.stream.StreamSource;
@@ -38,14 +42,26 @@
 import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
 
 import org.apache.fop.fonts.Base14Font;
+import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CIDSubset;
+import org.apache.fop.fonts.CMapSegment;
 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;
 import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
 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.TTFFile.PostScriptVersion;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFSubSetFile;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStream;
+import org.apache.fop.util.HexEncoder;
 
 /**
  * Utility code for font handling in PostScript.
@@ -54,7 +70,6 @@
 
     /** 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
@@ -66,7 +81,22 @@
      */
     public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo)
                 throws IOException {
-        return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true);
+        return writeFontDict(gen, fontInfo, null);
+    }
+
+    /**
+     * 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
+     * in a second pass.
+     * @param gen PostScript generator to use for output
+     * @param fontInfo available fonts
+     * @param eventProducer to report events
+     * @return a Map of PSResource instances representing all defined fonts (key: font key)
+     * @throws IOException in case of an I/O problem
+     */
+    public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
+            PSEventProducer eventProducer) throws IOException {
+        return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true, eventProducer);
     }
 
     /**
@@ -76,13 +106,13 @@
      * @param gen PostScript generator to use for output
      * @param fontInfo available fonts
      * @param fonts the set of fonts to work with
+     * @param eventProducer the event producer
      * @return a Map of PSResource instances representing all defined fonts (key: font key)
      * @throws IOException in case of an I/O problem
      */
-    public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
-            Map<String, Typeface> fonts)
-                throws IOException {
-        return writeFontDict(gen, fontInfo, fonts, false);
+    public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts,
+            PSEventProducer eventProducer) throws IOException {
+        return writeFontDict(gen, fontInfo, fonts, false, eventProducer);
     }
 
     /**
@@ -96,15 +126,16 @@
      * @throws IOException in case of an I/O problem
      */
     private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
-            Map<String, Typeface> fonts, boolean encodeAllCharacters) throws IOException {
+            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.getFontName());
-            fontResources.put(key, fontRes);
-            embedFont(gen, tf, fontRes);
+            PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
+            PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer);
+            fontResources.put(key, fontResource);
 
             if (tf instanceof SingleByteFont) {
                 SingleByteFont sbf = (SingleByteFont)tf;
@@ -117,9 +148,18 @@
                     SingleByteEncoding encoding = sbf.getAdditionalEncoding(i);
                     defineEncoding(gen, encoding);
                     String postFix = "_" + (i + 1);
-                    PSResource derivedFontRes = defineDerivedFont(gen, tf.getFontName(),
-                            tf.getFontName() + postFix, encoding.getName());
-                    fontResources.put(key + postFix, derivedFontRes);
+                    PSResource derivedFontRes;
+                    if (tf.getFontType() == FontType.TRUETYPE
+                            && sbf.getTrueTypePostScriptVersion() != PostScriptVersion.V2) {
+                        derivedFontRes = defineDerivedTrueTypeFont(gen, eventProducer,
+                                tf.getEmbedFontName(), tf.getEmbedFontName() + postFix, encoding,
+                                sbf.getCMap());
+                    } else {
+                        derivedFontRes = defineDerivedFont(gen, tf.getEmbedFontName(),
+                                tf.getEmbedFontName() + postFix, encoding.getName());
+                    }
+                    fontResources.put(key + postFix,
+                            PSFontResource.createFontResource(derivedFontRes));
                 }
             }
         }
@@ -156,12 +196,12 @@
             } else {
                 if (tf instanceof Base14Font) {
                     //Our Base 14 fonts don't use the default encoding
-                    redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName());
+                    redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
                 } else if (tf instanceof SingleByteFont) {
                     SingleByteFont sbf = (SingleByteFont)tf;
                     if (!sbf.isUsingNativeEncoding()) {
                         //Font has been configured to use an encoding other than the default one
-                        redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName());
+                        redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
                     }
                 }
             }
@@ -184,39 +224,299 @@
         return tf;
     }
 
-    /**
-     * Embeds a font in the PostScript file.
-     * @param gen the PostScript generator
-     * @param tf the font
-     * @param fontRes the PSResource associated with the font
-     * @throws IOException In case of an I/O error
-     */
-    public static void embedFont(PSGenerator gen, Typeface tf, PSResource fontRes)
-                throws IOException {
-        boolean embeddedFont = false;
-        if (FontType.TYPE1 == tf.getFontType()) {
-            if (tf instanceof CustomFont) {
-                CustomFont cf = (CustomFont)tf;
-                if (isEmbeddable(cf)) {
-                    InputStream in = getInputStreamOnFont(gen, cf);
-                    if (in != null) {
-                        gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
-                                fontRes);
-                        embedType1Font(gen, in);
-                        gen.writeDSCComment(DSCConstants.END_RESOURCE);
-                        gen.getResourceTracker().registerSuppliedResource(fontRes);
-                        embeddedFont = true;
-                    } else {
-                        gen.commentln("%WARNING: Could not embed font: " + cf.getFontName());
-                        log.warn("Font " + cf.getFontName() + " is marked as supplied in the"
-                                + " PostScript file but could not be embedded!");
-                    }
+    private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
+            PSEventProducer eventProducer) throws IOException {
+        FontType fontType = tf.getFontType();
+        PSFontResource fontResource = null;
+        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;
+    }
+
+    private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) {
+        if (gen.getPSLevel() < 3) {
+            if (eventProducer != null) {
+                eventProducer.postscriptLevel3Needed(gen);
+            } else {
+                throw new IllegalStateException("PostScript Level 3 is"
+                        + " required to use TrueType fonts,"
+                        + " configured level is "
+                        + gen.getPSLevel());
             }
         }
-        if (!embeddedFont) {
-            gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+    }
+
+    private static void embedTrueTypeFont(PSGenerator gen,
+            SingleByteFont font, InputStream fontStream) throws IOException {
+        /* 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");
+        if (font.getEmbeddingMode() == EmbeddingMode.AUTO) {
+            font.setEmbeddingMode(EmbeddingMode.SUBSET);
         }
+        FontFileReader reader = new FontFileReader(fontStream);
+        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,
+            CMapSegment[] cmap, TTFFile ttfFile) throws IOException {
+        gen.write("/FontName /");
+        gen.write(font.getEmbedFontName());
+        gen.writeln(" def");
+        gen.writeln("/PaintType 0 def");
+        gen.writeln("/FontMatrix [1 0 0 1 0 0] def");
+        writeFontBBox(gen, font);
+        gen.writeln("/FontType 42 def");
+        gen.writeln("/Encoding 256 array");
+        gen.writeln("0 1 255{1 index exch/.notdef put}for");
+        boolean buildCharStrings;
+        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;
+        } else {
+            buildCharStrings = true;
+            for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) {
+                gen.write("dup ");
+                gen.write(i);
+                gen.write(" /");
+                String glyphName = Glyphs.charToGlyphName(Glyphs.WINANSI_ENCODING[i]);
+                if (glyphName.equals("")) {
+                    gen.write(Glyphs.NOTDEF);
+                } else {
+                    gen.write(glyphName);
+                    glyphNames.add(glyphName);
+                }
+                gen.writeln(" put");
+            }
+        }
+        gen.writeln("readonly def");
+        TTFOutputStream ttfOut = new PSTTFOutputStream(gen);
+        ttfFile.stream(ttfOut);
+
+        buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font);
+    }
+
+    private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings,
+            CMapSegment[] cmap, Set<String> glyphNames, CustomFont font) throws IOException {
+        gen.write("/CharStrings ");
+        if (!buildCharStrings) {
+            gen.write(1);
+        } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+            int charCount = 1; //1 for .notdef
+            for (CMapSegment segment : cmap) {
+                charCount += segment.getUnicodeEnd() - segment.getUnicodeStart() + 1;
+            }
+            gen.write(charCount);
+        } else {
+            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) {
+            // 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 (CMapSegment segment : cmap) {
+                int glyphIndex = segment.getGlyphStartIndex();
+                for (int ch = segment.getUnicodeStart(); ch <= segment.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(Locale.ENGLISH);
+                    }
+                    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, CMapSegment[] cmap) {
+        for (CMapSegment segment : cmap) {
+            if (segment.getUnicodeStart() <= c && c <= segment.getUnicodeEnd()) {
+                return segment.getGlyphStartIndex() + c - segment.getUnicodeStart();
+            }
+        }
+        return 0;
+    }
+
+    private static void composeType0Font(PSGenerator gen, MultiByteFont font,
+            InputStream fontStream) throws IOException {
+        String psName = font.getEmbedFontName();
+        gen.write("/");
+        gen.write(psName);
+        gen.write(" /Identity-H [/");
+        gen.write(psName);
+        gen.writeln("] composefont pop");
+    }
+
+    private static PSResource embedType2CIDFont(PSGenerator gen,
+            MultiByteFont font, InputStream fontStream) throws IOException {
+        assert font.getCIDType() == CIDFontType.CIDTYPE2;
+
+        String psName = font.getEmbedFontName();
+        gen.write("%%BeginResource: CIDFont ");
+        gen.writeln(psName);
+
+        gen.write("%%Title: (");
+        gen.write(psName);
+        gen.writeln(" Adobe Identity 0)");
+
+        gen.writeln("%%Version: 1"); // TODO use font revision?
+        gen.writeln("/CIDInit /ProcSet findresource begin");
+        gen.writeln("20 dict begin");
+
+        gen.write("/CIDFontName /");
+        gen.write(psName);
+        gen.writeln(" def");
+
+        gen.writeln("/CIDFontVersion 1 def"); // TODO same as %%Version above
+
+        gen.write("/CIDFontType ");
+        gen.write(font.getCIDType().getValue());
+        gen.writeln(" def");
+
+        gen.writeln("/CIDSystemInfo 3 dict dup begin");
+        gen.writeln("  /Registry (Adobe) def");
+        gen.writeln("  /Ordering (Identity) def");
+        gen.writeln("  /Supplement 0 def");
+        gen.writeln("end def");
+
+        // TODO UIDBase (and UIDOffset in CMap) necessary if PostScript Level 1 & 2
+        // interpreters are to be supported
+        // (Level 1: with composite font extensions; Level 2: those that do not offer
+        // native mode support for CID-keyed fonts)
+
+        // TODO XUID (optional but strongly recommended)
+
+        // TODO /FontInfo
+
+        gen.write("/CIDCount ");
+        CIDSubset cidSubset = font.getCIDSubset();
+        int subsetSize = cidSubset.getSubsetSize();
+        gen.write(subsetSize);
+        gen.writeln(" def");
+        gen.writeln("/GDBytes 2 def"); // TODO always 2?
+        gen.writeln("/CIDMap [<");
+        int colCount = 0;
+        int lineCount = 1;
+        for (int cid = 0; cid < subsetSize; cid++) {
+            if (colCount++ == 20) {
+                gen.newLine();
+                colCount = 1;
+                if (lineCount++ == 800) {
+                    gen.writeln("> <");
+                    lineCount = 1;
+                }
+            }
+            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);
+
+        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());
+        }
+
+
+        createType42DictionaryEntries(gen, font, new CMapSegment[0], ttfFile);
+        gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop");
+        gen.writeln("end");
+        gen.writeln("%%EndResource");
+        PSResource cidFontResource = new PSResource(PSResource.TYPE_CIDFONT, psName);
+        gen.getResourceTracker().registerSuppliedResource(cidFontResource);
+        return cidFontResource;
+    }
+
+    private static void writeFontBBox(PSGenerator gen, CustomFont font) throws IOException {
+        int[] bbox = font.getFontBBox();
+        gen.write("/FontBBox[");
+        for (int i = 0; i < 4; i++) {
+            gen.write(" ");
+            gen.write(bbox[i]);
+        }
+        gen.writeln(" ] def");
     }
 
     private static boolean isEmbeddable(CustomFont font) {
@@ -273,12 +573,20 @@
         Map fontResources = new java.util.HashMap();
         for (String key : fonts.keySet()) {
             Typeface tf = getTypeFace(fontInfo, fonts, key);
-            PSResource fontRes = new PSResource("font", tf.getFontName());
+            PSResource fontRes = new PSResource("font", tf.getEmbedFontName());
             fontResources.put(key, fontRes);
-            if (FontType.TYPE1 == tf.getFontType()) {
+            FontType fontType = tf.getFontType();
+            if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
+                    || fontType == FontType.TYPE0) {
                 if (tf instanceof CustomFont) {
                     CustomFont cf = (CustomFont)tf;
                     if (isEmbeddable(cf)) {
+                        if (fontType == FontType.TYPE0) {
+                            resTracker.registerSuppliedResource(
+                                    new PSResource(PSResource.TYPE_CIDFONT, tf.getEmbedFontName()));
+                            resTracker.registerSuppliedResource(
+                                    new PSResource(PSResource.TYPE_CMAP, "Identity-H"));
+                        }
                         resTracker.registerSuppliedResource(fontRes);
                     }
                     if (tf instanceof SingleByteFont) {
@@ -289,7 +597,7 @@
                                     PSResource.TYPE_ENCODING, encoding.getName());
                             resTracker.registerSuppliedResource(encodingRes);
                             PSResource derivedFontRes = new PSResource(
-                                    PSResource.TYPE_FONT, tf.getFontName() + "_" + (i + 1));
+                                    PSResource.TYPE_FONT, tf.getEmbedFontName() + "_" + (i + 1));
                             resTracker.registerSuppliedResource(derivedFontRes);
                         }
                     }
@@ -366,4 +674,42 @@
         return res;
     }
 
+    private static PSResource defineDerivedTrueTypeFont(PSGenerator gen,
+            PSEventProducer eventProducer, String baseFontName, String fontName,
+            SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException {
+        checkPostScriptLevel3(gen, eventProducer);
+        PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
+        gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
+        gen.commentln("%XGCDependencies: font " + baseFontName);
+        gen.commentln("%XGC+ encoding " + encoding.getName());
+        gen.writeln("/" + baseFontName + " findfont");
+        gen.writeln("dup length dict begin");
+        gen.writeln("  {1 index /FID ne {def} {pop pop} ifelse} forall");
+        gen.writeln("  /Encoding " + encoding.getName() + " def");
+
+        gen.writeln("  /CharStrings 256 dict dup begin");
+        String[] charNameMap = encoding.getCharNameMap();
+        char[] unicodeCharMap = encoding.getUnicodeCharMap();
+        assert charNameMap.length == unicodeCharMap.length;
+        for (int i = 0; i < charNameMap.length; i++) {
+            String glyphName = charNameMap[i];
+            gen.write("    /");
+            gen.write(glyphName);
+            gen.write(" ");
+            if (glyphName.equals(".notdef")) {
+                gen.write(0);
+            } else {
+                gen.write(getGlyphIndex(unicodeCharMap[i], cmap));
+            }
+            gen.writeln(" def");
+        }
+        gen.writeln("  end readonly def");
+
+        gen.writeln("  currentdict");
+        gen.writeln("end");
+        gen.writeln("/" + fontName + " exch definefont pop");
+        gen.writeDSCComment(DSCConstants.END_RESOURCE);
+        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 9bed2a4..c228801 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -44,6 +44,7 @@
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontTriplet;
 import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
 import org.apache.fop.fonts.SingleByteFont;
 import org.apache.fop.fonts.Typeface;
 import org.apache.fop.render.RenderingContext;
@@ -55,6 +56,7 @@
 import org.apache.fop.traits.BorderProps;
 import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.HexEncoder;
 
 /**
  * IFPainter implementation that produces PostScript.
@@ -392,7 +394,7 @@
                     if (currentEncoding != encoding) {
                         if (i > 0) {
                             writeText(text, start, i - start,
-                                    letterSpacing, wordSpacing, dp, font, tf);
+                                    letterSpacing, wordSpacing, dp, font, tf, false);
                         }
                         if (encoding == 0) {
                             useFont(fontKey, sizeMillipoints);
@@ -404,19 +406,18 @@
                     }
                 }
             } else {
-                //Simple single-font painting
                 useFont(fontKey, sizeMillipoints);
             }
-            writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf);
+            writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf,
+                    tf instanceof MultiByteFont);
         } catch (IOException ioe) {
             throw new IFException("I/O error in drawText()", ioe);
         }
     }
 
-    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[][] dp,
-            Font font, Typeface tf) throws IOException {
+            Font font, Typeface tf, boolean multiByte) throws IOException {
         PSGenerator generator = getGenerator();
         int end = start + len;
         int initialSize = len;
@@ -451,8 +452,12 @@
             if (dx != null && i < dxl - 1) {
                 glyphAdjust -= dx[i + 1];
             }
-            char codepoint = (char)(ch % 256);
-            PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+            if (multiByte) {
+                accText.append(HexEncoder.encode(ch));
+            } else {
+                char codepoint = (char)(ch % 256);
+                PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+            }
             if (glyphAdjust != 0) {
                 needTJ = true;
                 if (sb.length() == 0) {
@@ -463,9 +468,8 @@
                         sb.append(PSGenerator.LF);
                         lineStart = sb.length();
                     }
-                    sb.append('(');
-                    sb.append(accText);
-                    sb.append(") ");
+                    lineStart = writePostScriptString(sb, accText, multiByte, lineStart);
+                    sb.append(' ');
                     accText.setLength(0); //reset accumulated text
                 }
                 sb.append(Integer.toString(glyphAdjust)).append(' ');
@@ -473,9 +477,10 @@
         }
         if (needTJ) {
             if (accText.length() > 0) {
-                sb.append('(');
-                sb.append(accText);
-                sb.append(')');
+                if ((sb.length() - lineStart + accText.length()) > 200) {
+                    sb.append(PSGenerator.LF);
+                }
+                writePostScriptString(sb, accText, multiByte);
             }
             if (hasLetterSpacing) {
                 sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ");
@@ -483,7 +488,7 @@
                 sb.append("] TJ");
             }
         } else {
-            sb.append('(').append(accText).append(")");
+            writePostScriptString(sb, accText, multiByte);
             if (hasLetterSpacing) {
                 StringBuffer spb = new StringBuffer();
                 spb.append(formatMptAsPt(generator, letterSpacing))
@@ -497,12 +502,37 @@
         generator.writeln(sb.toString());
     }
 
-    private void useFont(String key, int size) throws IOException {
-        PSResource res = this.documentHandler.getPSResourceForFontKey(key);
-        PSGenerator generator = getGenerator();
-        generator.useFont("/" + res.getName(), size / 1000f);
-        generator.getResourceTracker().notifyResourceUsageOnPage(res);
+    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();
+        generator.useFont("/" + res.getName(), size / 1000f);
+        res.notifyResourceUsageOnPage(generator.getResourceTracker());
+    }
 
 }
diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java
index acc6734..2b3afae 100644
--- a/src/java/org/apache/fop/render/ps/PSTextPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java
@@ -41,12 +41,15 @@
 
 import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
 import org.apache.xmlgraphics.ps.PSGenerator;
-import org.apache.xmlgraphics.ps.PSResource;
 
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontMetrics;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
 import org.apache.fop.svg.NativeTextPainter;
 import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.HexEncoder;
 
 /**
  * Renders the attributed character iterator of a text node.
@@ -240,9 +243,9 @@
         }
     }
 
-    private PSResource getResourceForFont(Font f, String postfix) {
+    private PSFontResource getResourceForFont(Font f, String postfix) {
         String key = (postfix != null ? f.getFontName() + '_' + postfix : f.getFontName());
-        return this.fontResources.getPSResourceForFontKey(key);
+        return this.fontResources.getFontResourceForFontKey(key);
     }
 
     private void clip(PSGraphics2D ps, Shape shape) throws IOException {
@@ -299,9 +302,9 @@
         public void selectFont(Font f, char mapped) throws IOException {
             int encoding = mapped / 256;
             String postfix = (encoding == 0 ? null : Integer.toString(encoding));
-            PSResource res = getResourceForFont(f, postfix);
+            PSFontResource res = getResourceForFont(f, postfix);
             gen.useFont("/" + res.getName(), f.getFontSize() / 1000f);
-            gen.getResourceTracker().notifyResourceUsageOnPage(res);
+            res.notifyResourceUsageOnPage(gen.getResourceTracker());
         }
 
         public Font getCurrentFont() {
@@ -427,15 +430,23 @@
             textUtil.setCurrentFont(f, mapped);
             applyColor(paint, gen);
 
+            FontMetrics metrics = f.getFontMetrics();
+            boolean multiByte = metrics instanceof MultiByteFont
+                    || metrics instanceof LazyFont
+                            && ((LazyFont) metrics).getRealFont() instanceof MultiByteFont;
             StringBuffer sb = new StringBuffer();
-            sb.append('(');
+            sb.append(multiByte ? '<' : '(');
             for (int i = 0, c = this.currentChars.length(); i < c; i++) {
                 char ch = this.currentChars.charAt(i);
                 mapped = f.mapChar(ch);
-                char codepoint = (char) (mapped % 256);
-                PSGenerator.escapeChar(codepoint, sb);
+                if (multiByte) {
+                    sb.append(HexEncoder.encode(mapped));
+                } else {
+                    char codepoint = (char) (mapped % 256);
+                    PSGenerator.escapeChar(codepoint, sb);
+                }
             }
-            sb.append(')');
+            sb.append(multiByte ? '>' : ')');
             if (x || y) {
                 sb.append("\n[");
                 int idx = 0;
@@ -513,10 +524,20 @@
                     textUtil.selectFont(f, mapped);
                     textUtil.setCurrentFont(f, mapped);
                 }
-                mapped = f.mapChar(this.currentChars.charAt(i));
                 //add glyph outlines to current path
-                char codepoint = (char)(mapped % 256);
-                gen.write("(" + codepoint + ")");
+                mapped = f.mapChar(this.currentChars.charAt(i));
+                FontMetrics metrics = f.getFontMetrics();
+                boolean multiByte = metrics instanceof MultiByteFont
+                        || metrics instanceof LazyFont
+                                && ((LazyFont) metrics).getRealFont() instanceof MultiByteFont;
+                if (multiByte) {
+                    gen.write('<');
+                    gen.write(HexEncoder.encode(mapped));
+                    gen.write('>');
+                } else {
+                    char codepoint = (char)(mapped % 256);
+                    gen.write("(" + codepoint + ")");
+                }
                 gen.writeln(" false charpath");
 
                 if (iter.hasNext()) {
diff --git a/src/java/org/apache/fop/render/ps/ResourceHandler.java b/src/java/org/apache/fop/render/ps/ResourceHandler.java
index 502242c..5594897 100644
--- a/src/java/org/apache/fop/render/ps/ResourceHandler.java
+++ b/src/java/org/apache/fop/render/ps/ResourceHandler.java
@@ -83,6 +83,8 @@
     private FOUserAgent userAgent;
     private FontInfo fontInfo;
 
+    private PSEventProducer eventProducer;
+
     private ResourceTracker resTracker;
 
     //key: URI, values PSImageFormResource
@@ -93,13 +95,15 @@
     /**
      * Main constructor.
      * @param userAgent the FO user agent
+     * @param eventProducer the event producer
      * @param fontInfo the font information
      * @param resTracker the resource tracker to use
      * @param formResources Contains all forms used by this document (maintained by PSRenderer)
      */
-    public ResourceHandler(FOUserAgent userAgent, FontInfo fontInfo,
-            ResourceTracker resTracker, Map formResources) {
+    public ResourceHandler(FOUserAgent userAgent, PSEventProducer eventProducer,
+            FontInfo fontInfo, ResourceTracker resTracker, Map formResources) {
         this.userAgent = userAgent;
+        this.eventProducer = eventProducer;
         this.fontInfo = fontInfo;
         this.resTracker = resTracker;
         determineInlineForms(formResources);
@@ -222,7 +226,7 @@
         if (fontSetupPlaceholder == null) {
             throw new DSCException("Didn't find %FOPFontSetup comment in stream");
         }
-        PSFontUtils.writeFontDict(gen, fontInfo, fontInfo.getUsedFonts());
+        PSFontUtils.writeFontDict(gen, fontInfo, fontInfo.getUsedFonts(), eventProducer);
         generateForms(globalFormResources, gen);
 
         //Skip the prolog and to the first page
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 0000000..31035dc
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
@@ -0,0 +1,101 @@
+/*
+ * 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
+ * TrueType 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;
+
+    /**
+     * Creates a new instance wrapping the given generator.
+     * @param gen the PSGenerator to wrap
+     */
+    public PSTTFGenerator(PSGenerator gen) {
+        this.gen = gen;
+        hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+    }
+
+    /**
+     * Writes the '&lt;' character that starts a string.
+     */
+    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("<");
+    }
+
+    /**
+     * Writes the given string to the output.
+     * @param cmd a string
+     */
+    public void write(String cmd) throws IOException {
+        gen.write(cmd);
+    }
+
+    /**
+     * Writes the given string to the output, followed by a newline.
+     * @param cmd a string
+     */
+    public void writeln(String cmd) throws IOException {
+        gen.writeln(cmd);
+    }
+
+    /**
+     * Writes bytes from the given byte array to the output.
+     *
+     * @param byteArray byte[] a byte array
+     * @param offset the position in the byte array where the streaming must start
+     * @param length the number of bytes to stream. This MUST be less than
+     * {@link MAX_BUFFER_SIZE} - 1 since strings are suffixed by '00' (see Section 4.2 of
+     * Adobe Technical Note #5012, <em>The Type 42 Font Format Specification</em>.).
+     */
+    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.
+     */
+    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 0000000..cc2ae3e
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+/**
+ * Streams glyphs in accordance with the constraints of the PostScript file format.
+ * Mainly, PostScript strings have a limited capacity and the font data may have to be
+ * broken down into several strings; however, this must occur at well-defined places like
+ * table or glyph boundaries. See also Adobe Technical Note #5012, <em>The Type 42 Font
+ * Format Specification</em>.
+ */
+public class PSTTFGlyphOutputStream implements TTFGlyphOutputStream {
+
+    /** Total number of bytes written so far. */
+    private int byteCounter;
+
+    private int lastStringBoundary;
+
+    private PSTTFGenerator ttfGen;
+
+    /**
+     * Constructor
+     * @param ttfGen PSTTFGenerator
+     */
+    public PSTTFGlyphOutputStream(PSTTFGenerator ttfGen) {
+        this.ttfGen = ttfGen;
+    }
+
+    public void startGlyphStream() throws IOException {
+        ttfGen.startString();
+    }
+
+    public void streamGlyph(byte[] glyphData, int offset, int size) throws IOException {
+        if (size > PSTTFGenerator.MAX_BUFFER_SIZE) {
+            throw new UnsupportedOperationException("The glyph is " + size
+                    + " bytes. There may be an error in the font file.");
+        }
+
+        if (size + (byteCounter - lastStringBoundary) < PSTTFGenerator.MAX_BUFFER_SIZE) {
+            ttfGen.streamBytes(glyphData, offset, size);
+        } else {
+            ttfGen.endString();
+            lastStringBoundary = byteCounter;
+            ttfGen.startString();
+            ttfGen.streamBytes(glyphData, offset, size);
+        }
+        byteCounter += size;
+    }
+
+    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 0000000..271d87d
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
@@ -0,0 +1,62 @@
+/*
+ * 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.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * Streams a TrueType font according to the PostScript format.
+ */
+public class PSTTFOutputStream implements TTFOutputStream {
+
+    private final PSTTFGenerator ttfGen;
+
+    /**
+     * Creates a new instance wrapping the given generator.
+     *
+     * @param gen the generator to wrap
+     */
+    public PSTTFOutputStream(PSGenerator gen) {
+        this.ttfGen = new PSTTFGenerator(gen);
+    }
+
+    public void startFontStream() throws IOException {
+        ttfGen.write("/sfnts[");
+    }
+
+    public TTFTableOutputStream getTableOutputStream() {
+        return new PSTTFTableOutputStream(ttfGen);
+    }
+
+    public TTFGlyphOutputStream getGlyphOutputStream() {
+        return new PSTTFGlyphOutputStream(ttfGen);
+    }
+
+    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 0000000..2226e11
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/**
+ * Streams a TrueType table according to the PostScript format.
+ */
+public class PSTTFTableOutputStream implements TTFTableOutputStream  {
+
+    private PSTTFGenerator ttfGen;
+
+    /**
+     * Constructor.
+     * @param ttfGen the helper object to stream TrueType data
+     */
+    public PSTTFTableOutputStream(PSTTFGenerator ttfGen) {
+        this.ttfGen = ttfGen;
+    }
+
+    public void streamTable(byte[] ttfData, int offset, int size) throws IOException {
+        int offsetPosition = offset;
+        // Need to split the table into MAX_BUFFER_SIZE chunks
+        for (int i = 0; i < size / PSTTFGenerator.MAX_BUFFER_SIZE; i++) {
+            streamString(ttfData, offsetPosition, PSTTFGenerator.MAX_BUFFER_SIZE);
+            offsetPosition += PSTTFGenerator.MAX_BUFFER_SIZE;
+        }
+        if (size % PSTTFGenerator.MAX_BUFFER_SIZE > 0) {
+            streamString(ttfData, offsetPosition, size % PSTTFGenerator.MAX_BUFFER_SIZE);
+        }
+    }
+
+    private void streamString(byte[] byteArray, int offset, int length) throws IOException {
+        ttfGen.startString();
+        ttfGen.streamBytes(byteArray, offset, length);
+        ttfGen.endString();
+    }
+
+}
diff --git a/src/java/org/apache/fop/util/HexEncoder.java b/src/java/org/apache/fop/util/HexEncoder.java
new file mode 100644
index 0000000..9ca91f2
--- /dev/null
+++ b/src/java/org/apache/fop/util/HexEncoder.java
@@ -0,0 +1,57 @@
+/*
+ * 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.util;
+
+/**
+ * A helper class to create hex-encoded representations of numbers.
+ */
+public final class HexEncoder {
+
+    private HexEncoder() { }
+
+    /**
+     * Returns an hex encoding of the given number as a string of the given length,
+     * left-padded with zeros if necessary.
+     *
+     * @param n a number
+     * @param width required length of the string
+     * @return an hex-encoded representation of the number
+     */
+    public static String encode(int n, int width) {
+        char[] digits = new char[width];
+        for (int i = width - 1; i >= 0; i--) {
+            int digit = n & 0xF;
+            digits[i] = (char) (digit < 10 ? '0' + digit : 'A' + digit - 10);
+            n >>= 4;
+        }
+        return new String(digits);
+    }
+
+    /**
+     * Returns an hex encoding of the given character as a four-character string.
+     *
+     * @param c a character
+     * @return an hex-encoded representation of the character
+     */
+    public static String encode(char c) {
+        return encode(c, 4);
+    }
+
+}
diff --git a/test/java/org/apache/fop/UtilityCodeTestSuite.java b/test/java/org/apache/fop/UtilityCodeTestSuite.java
index cf6b887..762b86b 100644
--- a/test/java/org/apache/fop/UtilityCodeTestSuite.java
+++ b/test/java/org/apache/fop/UtilityCodeTestSuite.java
@@ -28,10 +28,13 @@
 import org.apache.fop.pdf.PDFDocumentGraphics2DTestCase;
 import org.apache.fop.pdf.PDFEncryptionJCETestCase;
 import org.apache.fop.pdf.PDFFactoryTestCase;
+import org.apache.fop.pdf.PDFNumberTestCase;
+import org.apache.fop.pdf.PDFObjectTestCase;
 import org.apache.fop.traits.BorderPropsTestCase;
 import org.apache.fop.util.BitmapImageUtilTestCase;
 import org.apache.fop.util.ColorUtilTestCase;
 import org.apache.fop.util.ElementListUtilsTestCase;
+import org.apache.fop.util.HexEncoderTestCase;
 import org.apache.fop.util.XMLResourceBundleTestCase;
 
 /**
@@ -49,7 +52,10 @@
     PDFFactoryTestCase.class,
     PDFEncryptionJCETestCase.class,
     BitmapImageUtilTestCase.class,
-    PDFDocumentGraphics2DTestCase.class
+    PDFDocumentGraphics2DTestCase.class,
+    PDFNumberTestCase.class,
+    PDFObjectTestCase.class,
+    HexEncoderTestCase.class
 })
 public class UtilityCodeTestSuite {
 }
diff --git a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
index 4ac61d8..49c4475 100644
--- a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
+++ b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
@@ -43,7 +43,8 @@
     @Before
     public void setUp() throws Exception {
         File file = new File("test/resources/fonts/ttf/DejaVuLGCSerif.ttf");
-        font = FontLoader.loadFont(file, "", true, EncodingMode.AUTO, fontResolver);
+        font = FontLoader.loadFont(file, "", true, EmbeddingMode.AUTO, EncodingMode.AUTO,
+                fontResolver);
     }
 
     /**
diff --git a/test/java/org/apache/fop/fonts/EncodingModeTestCase.java b/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
index 1ec22e1..8cab9eb 100644
--- a/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
+++ b/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
@@ -19,10 +19,13 @@
 
 package org.apache.fop.fonts;
 
-import static org.junit.Assert.assertEquals;
-
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link EncodingMode}.
+ */
 public class EncodingModeTestCase {
 
     @Test
@@ -34,8 +37,13 @@
 
     @Test
     public void testGetValue() {
-        assertEquals(EncodingMode.AUTO, EncodingMode.getEncodingMode("auto"));
-        assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getEncodingMode("single-byte"));
-        assertEquals(EncodingMode.CID, EncodingMode.getEncodingMode("cid"));
+        assertEquals(EncodingMode.AUTO, EncodingMode.getValue("auto"));
+        assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getValue("single-byte"));
+        assertEquals(EncodingMode.CID, EncodingMode.getValue("cid"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void getValueMustCheckForIllegalArguments() {
+        EncodingMode.getValue("fail");
     }
 }
diff --git a/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java b/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java
new file mode 100644
index 0000000..8e1a004
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+import org.apache.fop.fonts.truetype.FontFileReaderTestCase;
+import org.apache.fop.fonts.truetype.TTFFileTestCase;
+import org.apache.fop.fonts.truetype.TTFSubSetFileTestCase;
+import org.apache.fop.fonts.truetype.TTFTableNameTestCase;
+
+/**
+ * A test suite designed for org.apache.fop.fonts.*
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+        EncodingModeTestCase.class,
+        FontFileReaderTestCase.class,
+        TTFFileTestCase.class,
+        TTFSubSetFileTestCase.class,
+        TTFTableNameTestCase.class })
+public final class FOPFontsTestSuite {
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java b/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java
new file mode 100644
index 0000000..5c1fec1
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java
@@ -0,0 +1,304 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * A test class for org.apache.fop.truetype.FontFileReader
+ */
+public class FontFileReaderTestCase {
+    private FontFileReader fontReader;
+    private final InputStream in;
+    private final byte[] byteArray;
+
+    /**
+     * Constructor - initialises an array that only needs to be created once. It creates a byte[]
+     * of form { 0x00, 0x01, 0x02, 0x03..., 0xff};
+     */
+    public FontFileReaderTestCase() {
+        byteArray = new byte[256];
+        for (int i = 0; i < 256; i++) {
+            byteArray[i] = (byte) i;
+        }
+        in = new ByteArrayInputStream(byteArray);
+    }
+
+    /**
+     * sets up the test subject object for testing.
+     */
+    @Before
+    public void setUp() {
+        try {
+            fontReader = new FontFileReader(in);
+        } catch (Exception e) {
+            fail("Error: " + e.getMessage());
+        }
+    }
+
+    /**
+     * the "destructor" method.
+     *
+     */
+    public void tearDown() {
+        fontReader = null;
+    }
+
+    /**
+     * Test readTTFByte()
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFByte() throws IOException {
+        for (int i = 0; i < 256; i++) {
+            assertEquals((byte) i, fontReader.readTTFByte());
+        }
+    }
+
+    /**
+     * Test seekSet() - check that it moves to the correct position and enforce a failure case.
+     * @throws IOException exception
+     */
+    @Test
+    public void testSeekSet() throws IOException {
+        fontReader.seekSet(10);
+        assertEquals(10, fontReader.readTTFByte());
+        try {
+            fontReader.seekSet(257);
+            fail("FileFontReaderTest Failed testSeekSet");
+        } catch (IOException e) {
+            // Passed
+        }
+    }
+
+    /**
+     * Test skip() - check that it moves to the correct position and enforce a failure case.
+     * @throws IOException exception
+     */
+    @Test
+    public void testSkip() throws IOException {
+        fontReader.skip(100);
+        assertEquals(100, fontReader.readTTFByte());
+        try {
+            // 100 (seekAdd) + 1 (read() = 1 byte) + 156 = 257
+            fontReader.skip(156);
+            fail("FileFontReaderTest Failed testSkip");
+        } catch (IOException e) {
+            // Passed
+        }
+    }
+
+    /**
+     * Test getCurrentPos() - 3 checks:
+     * 1) test with seekSet(int)
+     * 2) test with skip(int)
+     * 3) test with a readTTFByte() (this moves the position by the size of the data being read)
+     * @throws IOException exception
+     */
+    @Test
+    public void testGetCurrentPos() throws IOException {
+        fontReader.seekSet(10);
+        fontReader.skip(100);
+        assertEquals(110, fontReader.getCurrentPos());
+        fontReader.readTTFByte();
+        assertEquals(111, fontReader.getCurrentPos());
+    }
+
+    /**
+     * Test getFileSize()
+     */
+    @Test
+    public void testGetFileSize() {
+        assertEquals(256, fontReader.getFileSize());
+    }
+
+    /**
+     * Test readTTFUByte()
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFUByte() throws IOException {
+        for (int i = 0; i < 256; i++) {
+            assertEquals(i, fontReader.readTTFUByte());
+        }
+    }
+
+    /**
+     * Test readTTFShort() - Test positive and negative numbers (two's compliment).
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFShort() throws IOException {
+        // 0x0001 = 1
+        assertEquals("Should have been 1 (0x0001)", 1, fontReader.readTTFShort());
+        // 0x0203 = 515
+        assertEquals(515, fontReader.readTTFShort());
+        // now test negative numbers
+        fontReader.seekSet(250);
+        // 0xfafb
+        assertEquals(-1285, fontReader.readTTFShort());
+    }
+
+    /**
+     * Test readTTFUShort() - Test positive and potentially negative numbers (two's compliment).
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFUShort() throws IOException {
+        // 0x0001
+        assertEquals(1, fontReader.readTTFUShort());
+        // 0x0203
+        assertEquals(515, fontReader.readTTFUShort());
+        // test potential negatives
+        fontReader.seekSet(250);
+        // 0xfafb
+        assertEquals((250 << 8)  + 251, fontReader.readTTFUShort());
+    }
+
+    /**
+     * Test readTTFShort(int) - test reading ahead of current position and behind current position
+     * and in both cases ensure that our current position isn't changed.
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFShortWithArg() throws IOException {
+        // 0x6465
+        assertEquals(25701, fontReader.readTTFShort(100));
+        assertEquals(0, fontReader.getCurrentPos());
+        // read behind current position (and negative)
+        fontReader.seekSet(255);
+        // 0xfafb
+        assertEquals(-1285, fontReader.readTTFShort(250));
+        assertEquals(255, fontReader.getCurrentPos());
+    }
+
+    /**
+     * Test readTTFUShort(int arg) - test reading ahead of current position and behind current
+     * position and in both cases ensure that our current position isn't changed.
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFUShortWithArg() throws IOException {
+        // 0x6465
+        assertEquals(25701, fontReader.readTTFUShort(100));
+        assertEquals(0, fontReader.getCurrentPos());
+        // read behind current position (and potential negative)
+        fontReader.seekSet(255);
+        // 0xfafb
+        assertEquals(64251, fontReader.readTTFUShort(250));
+        assertEquals(255, fontReader.getCurrentPos());
+    }
+
+    /**
+     * Test readTTFLong()
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFLong() throws IOException {
+        // 0x00010203
+        assertEquals(66051, fontReader.readTTFLong());
+        // test negative numbers
+        fontReader.seekSet(250);
+        // 0xf0f1f2f3
+        assertEquals(-84148995, fontReader.readTTFLong());
+    }
+
+    /**
+     * Test readTTFULong()
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFULong() throws IOException {
+        // 0x00010203
+        assertEquals(66051, fontReader.readTTFULong());
+        // test negative numbers
+        fontReader.seekSet(250);
+        // 0xfafbfcfd
+        assertEquals(4210818301L, fontReader.readTTFULong());
+    }
+
+    /**
+     * Test readTTFString() - there are two paths to test here:
+     * 1) A null terminated string
+     * 2) A string not terminated with a null (we expect this to throw an EOFException)
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFString() throws IOException {
+        byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t', 0x00};
+        fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+        assertEquals("test", fontReader.readTTFString());
+        try {
+            // not NUL terminated
+            byte[] strByteNoNull = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+            fontReader = new FontFileReader(new ByteArrayInputStream(strByteNoNull));
+            assertEquals("test", fontReader.readTTFString());
+            fail("FontFileReaderTest testReadTTFString Fails.");
+        } catch (EOFException e) {
+            // Pass
+        }
+    }
+
+    /**
+     * Test readTTFString(int arg)
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadTTFStringIntArg() throws IOException {
+        byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+        fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+        assertEquals("test", fontReader.readTTFString(4));
+        try {
+            fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+            assertEquals("test", fontReader.readTTFString(5));
+            fail("FontFileReaderTest testReadTTFStringIntArg Fails.");
+        } catch (EOFException e) {
+            // Pass
+        }
+    }
+
+    /**
+     * Test readTTFString(int arg1, int arg2)
+     */
+    public void testReadTTFString2IntArgs() {
+        // currently the same as above
+    }
+
+    /**
+     * Test getBytes()
+     * @throws IOException exception
+     */
+    @Test
+    public void testGetBytes() throws IOException {
+        byte[] retrievedBytes = fontReader.getBytes(0, 256);
+        assertTrue(Arrays.equals(byteArray, retrievedBytes));
+    }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
index 67191ac..825f71a 100644
--- a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
+++ b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
@@ -19,8 +19,6 @@
 
 package org.apache.fop.fonts.truetype;
 
-import static org.junit.Assert.assertTrue;
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -31,6 +29,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.junit.Assert.assertTrue;
+
 /**
  * Tests {@link GlyfTable}.
  */
@@ -141,7 +141,8 @@
 
     private void setupSubsetReader(Map<Integer, Integer> glyphs) throws IOException {
         TTFSubSetFile fontFile = new TTFSubSetFile();
-        byte[] subsetFont = fontFile.readFont(originalFontReader, "Deja", glyphs);
+        fontFile.readFont(originalFontReader, "Deja", glyphs);
+        byte[] subsetFont = fontFile.getFontSubset();
         InputStream intputStream = new ByteArrayInputStream(subsetFont);
         subsetReader = new FontFileReader(intputStream);
     }
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java
new file mode 100644
index 0000000..d490a3d
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java
@@ -0,0 +1,427 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+
+/**
+ * Class for testing org.apache.fop.fonts.truetype.TTFFile
+ */
+public class TTFFileTestCase {
+    // We only want to initialize the FontFileReader once (for performance reasons)
+    /** The truetype font file (DejaVuLGCSerif) */
+    protected final TTFFile dejavuTTFFile;
+    /** The FontFileReader for ttfFile (DejaVuLGCSerif) */
+    protected final FontFileReader dejavuReader;
+    /** The truetype font file (DroidSansMono) */
+    protected final TTFFile droidmonoTTFFile;
+    /** The FontFileReader for ttfFile (DroidSansMono) */
+    protected final FontFileReader droidmonoReader;
+
+
+    /**
+     * Constructor initialises FileFontReader to
+     * @throws IOException exception
+     */
+    public TTFFileTestCase() throws IOException {
+        dejavuTTFFile = new TTFFile();
+        dejavuReader = new FontFileReader("test/resources/fonts/ttf/DejaVuLGCSerif.ttf");
+        dejavuTTFFile.readFont(dejavuReader);
+        droidmonoTTFFile = new TTFFile();
+        droidmonoReader = new FontFileReader("test/resources/fonts/ttf/DroidSansMono.ttf");
+        droidmonoTTFFile.readFont(droidmonoReader);
+    }
+
+    /**
+     * Test convertTTFUnit2PDFUnit() - The units per em retrieved reading the HEAD table from
+     * the font file. (DroidSansMono has the same units per em as DejaVu so no point testing it)
+     */
+    @Test
+    public void testConvertTTFUnit2PDFUnit() {
+        // DejaVu has 2048 units per em (PDF works in millipts, thus the 1000)
+        // test rational number
+        assertEquals(1000, dejavuTTFFile.convertTTFUnit2PDFUnit(2048));
+        // test smallest case, this should = 0.488 (round down to 0)
+        assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(1));
+        // this should round up, but since it's millipts...
+        assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(2));
+        // ensure behaviour is the same for negative numbers
+        assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-0));
+        assertEquals(-1000, dejavuTTFFile.convertTTFUnit2PDFUnit(-2048));
+        assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-1));
+        assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-2));
+    }
+
+    /**
+     * Test checkTTC()
+     * @throws IOException exception
+     */
+    @Test
+    public void testCheckTTC() throws IOException {
+        // DejaVu is not a TTC, thus this returns true
+        assertTrue(dejavuTTFFile.checkTTC(""));
+        assertTrue(droidmonoTTFFile.checkTTC(""));
+        /*
+         * Cannot reasonably test the rest of this method without an actual truetype collection
+         * because all methods in FontFileReader are "final" and thus mocking isn't possible.
+         */
+    }
+
+    /**
+     * Test getAnsiKerning() - Tests values retrieved from the kern table in the font file.
+     */
+    @Test
+    public void testGetAnsiKerning() {
+        Map<Integer, Map<Integer, Integer>> ansiKerning = dejavuTTFFile.getKerning();
+        if (ansiKerning.isEmpty()) {
+            fail();
+        }
+        Integer k1 = ansiKerning.get(Integer.valueOf('A')).get(
+                Integer.valueOf('T'));
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue());
+        Integer k2 = ansiKerning.get(Integer.valueOf('Y')).get(Integer.valueOf('u'));
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-178), k2.intValue());
+
+        // DroidSansMono doens't have kerning (it's mono-spaced)
+        ansiKerning = droidmonoTTFFile.getAnsiKerning();
+        if (!ansiKerning.isEmpty()) {
+            fail("DroidSansMono shouldn't have any kerning data.");
+        }
+    }
+
+    /**
+     * Test getCapHeight - there are several paths to test:
+     * 1) The PCLT table (if present)
+     * 2) The yMax (3rd) value, for the bounding box, for 'H' in the glyf table.
+     * if not the above:
+     * 3) The caps height in the OS/2 table
+     * Tests values retrieved from analysing the font file.
+     */
+    @Test
+    public void testGetCapHeight() {
+        // DejaVu doesn't have the PCLT table and so these have to be guessed
+        // The height is approximated to be the height of the "H" which for
+        // Deja = 1493 TTFunits
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1493), dejavuTTFFile.getCapHeight());
+        // DroidSansMono doesn't have a PCLT table either
+        // height of "H" = 1462
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1462),
+                droidmonoTTFFile.getCapHeight());
+    }
+
+    /**
+     * Test getCharSetName() - check that it returns "WinAnsiEncoding".
+     */
+    @Test
+    public void testGetCharSetName() {
+        assertTrue("WinAnsiEncoding".equals(dejavuTTFFile.getCharSetName()));
+        assertTrue("WinAnsiEncoding".equals(droidmonoTTFFile.getCharSetName()));
+    }
+
+    /**
+     * Test getCharWidth() - Test values retrieved from the metrics in the glyf table in
+     * the font file.
+     */
+    @Test
+    public void testGetCharWidth() {
+        // Arbitrarily test a few values:
+        // The width of "H" (Unicode index 0x0048) is 1786
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1786), dejavuTTFFile.getCharWidth(0x48));
+        // The width of "i" (unicode index 0x0069) is 655 TTFunits
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(655), dejavuTTFFile.getCharWidth(0x69));
+        // final check, "!" (unicode index 0x0021) is 823 TTFunits
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(823), dejavuTTFFile.getCharWidth(0x21));
+
+        // All the glyphs should be the same width in DroidSansMono (mono-spaced)
+        int charWidth = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229);
+        for (int i = 0; i < 255; i++) {
+            assertEquals(charWidth, droidmonoTTFFile.getCharWidth(i));
+        }
+    }
+
+    /**
+     * TODO: add implementation to this test
+     */
+    public void testGetCMaps() {
+    }
+
+    /**
+     * Test getFamilyNames() - Test value retrieved from the name table in the font file.
+     */
+    @Test
+    public void testGetFamilyNames() {
+        assertEquals(1, dejavuTTFFile.getFamilyNames().size());
+        for (String name : dejavuTTFFile.getFamilyNames()) {
+            assertEquals("DejaVu LGC Serif", name);
+        }
+        assertEquals(1, droidmonoTTFFile.getFamilyNames().size());
+        for (String name : droidmonoTTFFile.getFamilyNames()) {
+            assertEquals("Droid Sans Mono", name);
+        }
+    }
+
+    /**
+     * Test getFirstChar() - TODO: implement a more intelligent test here.
+     */
+    @Test
+    public void testGetFirstChar() {
+        // Not really sure how to test this intelligently
+        assertEquals(0, dejavuTTFFile.getFirstChar());
+        assertEquals(0, droidmonoTTFFile.getFirstChar());
+    }
+
+    /**
+     * Test getFlags() - Test values retrieved from the POST table in the font file.
+     */
+    @Test
+    public void testGetFlags() {
+        /* DejaVu flags are:
+         * italic angle = 0
+         * fixed pitch = 0
+         * has serifs = true (default value; this font doesn't have a PCLT table)
+         */
+        int flags = dejavuTTFFile.getFlags();
+        assertEquals(0, flags  & 64);       // Italics angle = 0
+        assertEquals(32, flags & 32);       // Adobe standard charset
+        assertEquals(0, flags & 2);         // fixed pitch = 0
+        assertEquals(1, flags & 1);         // has serifs = 1 (true)
+        /*
+         * Droid flags are:
+         * italic angle = 0
+         * fixed pitch = 1
+         * has serifs = true (default value; this font doesn't have a PCLT table)
+         */
+        flags = droidmonoTTFFile.getFlags();
+        assertEquals(0, flags & 64);
+        assertEquals(32, flags & 32);
+        assertEquals(2, flags & 2);
+        assertEquals(1, flags & 1);
+    }
+
+    /**
+     * Test getFontBBox() - Test values retrieved from values in the HEAD table in the font file.
+     */
+    @Test
+    public void testGetFontBBox() {
+        int[] bBox = dejavuTTFFile.getFontBBox();
+        /*
+         * The head table has the following values(DejaVu):
+         * xmin = -1576, ymin = -710, xmax = 3439, ymax = 2544
+         */
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), bBox[0]);
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-710), bBox[1]);
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(3439), bBox[2]);
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(2544), bBox[3]);
+        /*
+         * The head table has the following values (DroidSansMono):
+         * xmin = -312, ymin= -555, xmax = 1315, ymax = 2163
+         */
+        bBox = droidmonoTTFFile.getFontBBox();
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-312), bBox[0]);
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-555), bBox[1]);
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1315), bBox[2]);
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(2163), bBox[3]);
+    }
+
+    /**
+     * Test getFullName() - Test value retrieved from the name table in the font file.
+     */
+    @Test
+    public void testGetFullName() {
+        assertEquals("DejaVu LGC Serif", dejavuTTFFile.getFullName());
+        assertEquals("Droid Sans Mono", droidmonoTTFFile.getFullName());
+    }
+
+    /**
+     * Test getGlyphName - Test value retrieved from the POST table in the font file.
+     */
+    @Test
+    public void testGetGlyphName() {
+        assertEquals("H", dejavuTTFFile.getGlyphName(43));
+        assertEquals("H", droidmonoTTFFile.getGlyphName(43));
+    }
+
+    /**
+     * Test getItalicAngle() - Test value retrieved from the POST table in the font file.
+     */
+    @Test
+    public void testGetItalicAngle() {
+        assertEquals("0", dejavuTTFFile.getItalicAngle());
+        assertEquals("0", droidmonoTTFFile.getItalicAngle());
+    }
+
+    /**
+     * Test getKerning() - Test values retrieved from the kern table in the font file.
+     */
+    @Test
+    public void testGetKerning() {
+        Map<Integer, Map<Integer, Integer>> kerning = dejavuTTFFile.getKerning();
+        if (kerning.isEmpty()) {
+            fail();
+        }
+        Integer k1 = kerning.get(Integer.valueOf('A')).get(Integer.valueOf('T'));
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue());
+        Integer k2 = kerning.get(Integer.valueOf('K')).get(Integer.valueOf('u'));
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-45), k2.intValue());
+
+        // DroidSansMono has no kerning data (mono-spaced)
+        kerning = droidmonoTTFFile.getKerning();
+        if (!kerning.isEmpty()) {
+            fail("DroidSansMono shouldn't have any kerning data");
+        }
+    }
+
+    /**
+     * Test lastChar() - TODO: implement a more intelligent test
+     */
+    @Test
+    public void testLastChar() {
+        assertEquals(0xff, dejavuTTFFile.getLastChar());
+        assertEquals(0xff, droidmonoTTFFile.getLastChar());
+    }
+
+    /**
+     * Test getLowerCaseAscent() - There are several paths to test:
+     * 1) The values in the HHEA table (see code)
+     * 2) Fall back to values from the OS/2 table
+     * Test values retrieved from the font file.
+     */
+    @Test
+    public void testGetLowerCaseAscent() {
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1556),
+                dejavuTTFFile.getLowerCaseAscent());
+        // Curiously the same value
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1556),
+                droidmonoTTFFile.getLowerCaseAscent());
+    }
+
+    /**
+     * Test getPostScriptName() - Test values retrieved from the post table in the font file.
+     */
+    @Test
+    public void testGetPostScriptName() {
+        assertEquals(PostScriptVersion.V2, dejavuTTFFile.getPostScriptVersion());
+        assertEquals(PostScriptVersion.V2, droidmonoTTFFile.getPostScriptVersion());
+    }
+
+    /**
+     * Test getStemV() - Undefined.
+     */
+    @Test
+    public void testGetStemV() {
+        // Undefined
+        assertEquals("0", dejavuTTFFile.getStemV());
+        assertEquals("0", droidmonoTTFFile.getStemV());
+    }
+
+    /**
+     * Test getSubFamilyName() - Test values retrieved from the name table in the font file.
+     */
+    @Test
+    public void testGetSubFamilyName() {
+        assertEquals("Book", dejavuTTFFile.getSubFamilyName());
+        assertEquals("Regular", droidmonoTTFFile.getSubFamilyName());
+    }
+
+    /**
+     * Test getTTCnames() - TODO: add implementation with TTC font.
+     */
+    public void testGetTTCnames() {
+        // Can't test with with DejaVu since it's not a TrueType Collection
+    }
+
+    /**
+     * Test getWeightClass() - Test value retrieved from the OS/2 table in the font file.
+     */
+    @Test
+    public void testGetWeightClass() {
+        // Retrieved from OS/2 table
+        assertEquals(400, dejavuTTFFile.getWeightClass());
+        assertEquals(400, droidmonoTTFFile.getWeightClass());
+    }
+
+    /**
+     * Test getWidths() - Test values retrieved from the hmtx table in the font file.
+     */
+    @Test
+    public void testGetWidths() {
+        int[] widths = dejavuTTFFile.getWidths();
+        // using the width of 'A' index = 36
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1479), widths[36]);
+        // using the width of '|' index = 95
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(690), widths[95]);
+        widths = droidmonoTTFFile.getWidths();
+        // DroidSansMono should have all widths the same size (mono-spaced)
+        int width = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229);
+        for (int i = 0; i < 255; i++) {
+            assertEquals(width, widths[i]);
+        }
+    }
+
+    /**
+     * Test getXHeight() - There are several paths to test:
+     * 1) The PCLT table (if available)
+     * 2) The yMax for the bounding box for 'x' in the glyf table.
+     * Fall back:
+     * 3) The xheight in the OS/2 table.
+     */
+    @Test
+    public void testGetXHeight() {
+        // Since there's no PCLT table, the height of 'x' is used for both DejaVu and DroidSansMono
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1064), dejavuTTFFile.getXHeight());
+        assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1098), droidmonoTTFFile.getXHeight());
+    }
+
+    /**
+     * Test isCFF() - TODO: add test for a CFF font.
+     */
+    @Test
+    public void testIsCFF() {
+        // Neither DejaVu nor DroidSansMono are a compact format font
+        assertEquals(false, dejavuTTFFile.isCFF());
+        assertEquals(false, droidmonoTTFFile.isCFF());
+    }
+
+    /**
+     * Test isEmbeddable() - Test value retrieved from the OS/2 table in the font file.
+     */
+    @Test
+    public void testIsEmbeddable() {
+        // Dejavu and DroidSansMono are both embeddable
+        assertEquals(true, dejavuTTFFile.isEmbeddable());
+        assertEquals(true, droidmonoTTFFile.isEmbeddable());
+    }
+
+    /**
+     * Test readFont() - Add implementation if necessary.
+     */
+    public void testReadFont() {
+        // I'm pretty sure we've tested this with all the other tests
+    }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
index 35c865c..d6555c3 100644
--- a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
+++ b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
@@ -19,18 +19,19 @@
 
 package org.apache.fop.fonts.truetype;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.IOException;
 
 import org.junit.Test;
 
+import org.apache.fop.fonts.EmbeddingMode;
 import org.apache.fop.fonts.EncodingMode;
 import org.apache.fop.fonts.FontManager;
 import org.apache.fop.fonts.FontResolver;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 /**
  * Test case for {@link TTFFontLoader}.
  */
@@ -47,12 +48,12 @@
         boolean useKerning = true;
 
         TTFFontLoader fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded,
-                EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
+                EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
         assertTrue(fontLoader.getFont().hasKerningInfo());
         useKerning = false;
 
-        fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, EncodingMode.AUTO,
-                useKerning, useComplexScriptFeatures, resolver);
+        fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO,
+                EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
         assertFalse(fontLoader.getFont().hasKerningInfo());
     }
 }
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java
new file mode 100644
index 0000000..16bedad
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java
@@ -0,0 +1,76 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This class tests TTFSubSetFile
+ * TODO: Test with more than just a single font
+ */
+public class TTFSubSetFileTestCase extends TTFFileTestCase {
+    private TTFSubSetFile ttfSubset;
+    private byte[] subset;
+    /**
+     * Constructor
+     * @throws IOException exception
+     */
+    public TTFSubSetFileTestCase() throws IOException {
+        super();
+    }
+
+    /**
+     * setUp()
+     * @exception IOException file read error
+     */
+    @Before
+    public void setUp() throws IOException {
+        ttfSubset = new TTFSubSetFile();
+        Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>();
+        for (int i = 0; i < 255; i++) {
+            glyphs.put(i, i);
+        }
+        ttfSubset.readFont(dejavuReader, "DejaVu", glyphs);
+        subset = ttfSubset.getFontSubset();
+    }
+    /**
+     * Test readFont(FontFileReader, String, Map) - Reads the font and tests the output by injecting
+     * it into a TTFFile object to check the validity of the file as a font. This currently doesn't
+     * create a cmap table, and so the font doesn't contain ALL of the mandatory tables.
+     * @throws IOException exception
+     */
+    @Test
+    public void testReadFont3Args() throws IOException {
+
+        ByteArrayInputStream byteArray = new ByteArrayInputStream(subset);
+        dejavuTTFFile.readFont(new FontFileReader(byteArray));
+        // Test a couple arbitrary values
+        assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), dejavuTTFFile.getFontBBox()[0]);
+        assertEquals(dejavuTTFFile.getFullName(), "DejaVu LGC Serif");
+    }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java
new file mode 100644
index 0000000..b9066dc
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java
@@ -0,0 +1,153 @@
+/*
+ * 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 org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This class tests the enum org.apache.fop.fonts.truetype.TTFTableName
+ *
+ */
+public class TTFTableNameTestCase {
+    /**
+     * Test getName() - tests that the getName() method returns the expected String as expected in
+     * the Directory Table.
+     * @exception IllegalAccessException error
+     */
+    @Test
+    public void testGetName() throws IllegalAccessException {
+        assertEquals("tableDirectory", TTFTableName.TABLE_DIRECTORY.getName());
+        assertEquals("EBDT", TTFTableName.EBDT.getName());
+        assertEquals("EBLC", TTFTableName.EBLC.getName());
+        assertEquals("EBSC", TTFTableName.EBSC.getName());
+        assertEquals("FFTM", TTFTableName.FFTM.getName());
+        assertEquals("GDEF", TTFTableName.GDEF.getName());
+        assertEquals("GPOS", TTFTableName.GPOS.getName());
+        assertEquals("GSUB", TTFTableName.GSUB.getName());
+        assertEquals("LTSH", TTFTableName.LTSH.getName());
+        assertEquals("OS/2", TTFTableName.OS2.getName());
+        assertEquals("PCLT", TTFTableName.PCLT.getName());
+        assertEquals("VDMX", TTFTableName.VDMX.getName());
+        assertEquals("cmap", TTFTableName.CMAP.getName());
+        assertEquals("cvt ", TTFTableName.CVT.getName());
+        assertEquals("fpgm", TTFTableName.FPGM.getName());
+        assertEquals("gasp", TTFTableName.GASP.getName());
+        assertEquals("glyf", TTFTableName.GLYF.getName());
+        assertEquals("hdmx", TTFTableName.HDMX.getName());
+        assertEquals("head", TTFTableName.HEAD.getName());
+        assertEquals("hhea", TTFTableName.HHEA.getName());
+        assertEquals("hmtx", TTFTableName.HMTX.getName());
+        assertEquals("kern", TTFTableName.KERN.getName());
+        assertEquals("loca", TTFTableName.LOCA.getName());
+        assertEquals("maxp", TTFTableName.MAXP.getName());
+        assertEquals("name", TTFTableName.NAME.getName());
+        assertEquals("post", TTFTableName.POST.getName());
+        assertEquals("prep", TTFTableName.PREP.getName());
+        assertEquals("vhea", TTFTableName.VHEA.getName());
+        assertEquals("vmtx", TTFTableName.VMTX.getName());
+        // make sure it works with other table names
+        TTFTableName test = TTFTableName.getValue("test");
+        assertEquals("test", test.getName());
+    }
+
+    /**
+     * Test getValue(String) - tests that the getValue(String) method returns the expected
+     * TTFTableNames value when it is given a String (name of a table).
+     * @exception IllegalAccessException error
+     */
+    @Test
+    public void testGetValue() throws IllegalAccessException {
+        assertEquals(TTFTableName.EBDT, TTFTableName.getValue("EBDT"));
+        assertEquals(TTFTableName.EBLC, TTFTableName.getValue("EBLC"));
+        assertEquals(TTFTableName.EBSC, TTFTableName.getValue("EBSC"));
+        assertEquals(TTFTableName.FFTM, TTFTableName.getValue("FFTM"));
+        assertEquals(TTFTableName.LTSH, TTFTableName.getValue("LTSH"));
+        assertEquals(TTFTableName.OS2, TTFTableName.getValue("OS/2"));
+        assertEquals(TTFTableName.PCLT, TTFTableName.getValue("PCLT"));
+        assertEquals(TTFTableName.VDMX, TTFTableName.getValue("VDMX"));
+        assertEquals(TTFTableName.CMAP, TTFTableName.getValue("cmap"));
+        assertEquals(TTFTableName.CVT, TTFTableName.getValue("cvt "));
+        assertEquals(TTFTableName.FPGM, TTFTableName.getValue("fpgm"));
+        assertEquals(TTFTableName.GASP, TTFTableName.getValue("gasp"));
+        assertEquals(TTFTableName.GLYF, TTFTableName.getValue("glyf"));
+        assertEquals(TTFTableName.HDMX, TTFTableName.getValue("hdmx"));
+        assertEquals(TTFTableName.HEAD, TTFTableName.getValue("head"));
+        assertEquals(TTFTableName.HHEA, TTFTableName.getValue("hhea"));
+        assertEquals(TTFTableName.HMTX, TTFTableName.getValue("hmtx"));
+        assertEquals(TTFTableName.KERN, TTFTableName.getValue("kern"));
+        assertEquals(TTFTableName.LOCA, TTFTableName.getValue("loca"));
+        assertEquals(TTFTableName.MAXP, TTFTableName.getValue("maxp"));
+        assertEquals(TTFTableName.NAME, TTFTableName.getValue("name"));
+        assertEquals(TTFTableName.POST, TTFTableName.getValue("post"));
+        assertEquals(TTFTableName.PREP, TTFTableName.getValue("prep"));
+        assertEquals(TTFTableName.VHEA, TTFTableName.getValue("vhea"));
+        assertEquals(TTFTableName.VMTX, TTFTableName.getValue("vmtx"));
+        // Test that we can store a random table name and it will not fail or throw an error.
+        TTFTableName test = TTFTableName.getValue("random");
+        assertTrue(test instanceof TTFTableName);
+    }
+
+    /**
+     * This class overrides hashCode() - we need to ensure it works properly by instantiating two
+     * objects and comparing their hash-codes.
+     * @exception IllegalAccessException error
+     */
+    @Test
+    public void testHashCode() throws IllegalAccessException {
+        TTFTableName a = TTFTableName.getValue("testObject");
+        TTFTableName b = TTFTableName.getValue("testObject");
+        assertTrue(a.hashCode() == b.hashCode());
+        TTFTableName c = TTFTableName.getValue("fail");
+        assertFalse(a.hashCode() == c.hashCode());
+    }
+
+    /**
+     * This class overrides equals(object) - we need to test:
+     * 1) Reflexivity
+     * 2) Symmetry
+     * 3) Transitivity
+     * 4) Consistency
+     * 5) check it fails if you put in a null value
+     * @throws IllegalAccessException error
+     */
+    @Test
+    public void testEquals() throws IllegalAccessException {
+        // Reflexivity
+        TTFTableName a = TTFTableName.getValue("test");
+        assertTrue(a.equals(a));
+        // Symmetry
+        TTFTableName b = TTFTableName.getValue("test");
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        // Transitivity (tested with symmetry)
+        // Consistency (test that a == b is true and that a == c fails)
+        TTFTableName c = TTFTableName.getValue("fail");
+        for (int i = 0; i < 100; i++) {
+            assertTrue(a.equals(b));
+            assertFalse(a.equals(c));
+        }
+        // check with null value
+        assertFalse(a.equals(null));
+    }
+}
diff --git a/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java b/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java
new file mode 100644
index 0000000..2e15bf9
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+import org.apache.fop.render.ps.fonts.PSTTFGeneratorTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFGlyphOutputStreamTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStreamTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFTableOutputStreamTestCase;
+
+
+/**
+ * A test Suite for org.apache.fop.render.ps.*
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+        PSTTFGeneratorTestCase.class,
+        PSTTFOutputStreamTestCase.class,
+        PSTTFGlyphOutputStreamTestCase.class,
+        PSTTFTableOutputStreamTestCase.class
+})
+public final class RenderPSTestSuite {
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java
new file mode 100644
index 0000000..f7f311f
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java
@@ -0,0 +1,120 @@
+/*
+ * 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.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * The test class for org.apache.fop.render.ps.fonts.PSGenerator
+ */
+public class PSTTFGeneratorTestCase {
+    private PSTTFGenerator ttfGen;
+    private ByteArrayOutputStream out = new ByteArrayOutputStream();
+    private PSGenerator gen = new PSGenerator(out);
+    private byte[] byteArray;
+
+    /**
+     * Constructor
+     */
+    public PSTTFGeneratorTestCase() {
+        byteArray = new byte[65536];
+        for (int i = 0; i < 65536; i++) {
+            byteArray[i] = (byte) i;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        ttfGen = new PSTTFGenerator(gen);
+    }
+
+    /**
+     * Tests startString() - starts the string in an appropriate way for a PostScript file.
+     * @exception IOException write error
+     */
+    @Test
+    public void testStartString() throws IOException {
+        ttfGen.startString();
+        assertEquals("<\n", out.toString());
+    }
+
+    /**
+     * Test streamBytes() - tests that strings are written to file in the proper format.
+     * @throws IOException write error.
+     */
+    @Test
+    public void testStreamBytes() throws IOException {
+        ttfGen.streamBytes(byteArray, 0, 16);
+        assertEquals("000102030405060708090A0B0C0D0E0F", out.toString());
+        /*
+         * 65520 is the closes multiple of 80 to 65535 (max string size in PS document) and since
+         * one byte takes up two characters, 65520 / 2 - 16 (16 bytes already written)= 32744.
+         */
+        ttfGen.streamBytes(byteArray, 0, 32744);
+        // Using a regex to ensure that the format is correct
+        assertTrue(out.toString().matches("([0-9A-F]{80}\n){819}"));
+        try {
+            ttfGen.streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+            fail("Shouldn't be able to write more than MAX_BUFFER_SIZE to a PS document");
+        } catch (UnsupportedOperationException e) {
+            // PASS
+        }
+    }
+
+    /**
+     * Test reset() - reset should reset the line counter such that when reset() is invoked the
+     * following string streamed to the PS document should be 80 chars long.
+     * @throws IOException file write error.
+     */
+    @Test
+    public void testReset() throws IOException {
+        ttfGen.streamBytes(byteArray, 0, 40);
+        assertTrue(out.toString().matches("([0-9A-F]{80}\n)"));
+        ttfGen.streamBytes(byteArray, 0, 40);
+        assertTrue(out.toString().matches("([0-9A-F]{80}\n){2}"));
+
+    }
+
+    /**
+     * Test endString() - ensures strings are ended in the PostScript document in the correct
+     * format, a "00" needs to be appended to the end of a string.
+     * @throws IOException file write error
+     */
+    @Test
+    public void testEndString() throws IOException {
+        ttfGen.endString();
+        assertEquals("00\n> ", out.toString());
+        out.reset();
+        // we need to check that this doesn't write more than 80 chars per line
+        ttfGen.streamBytes(byteArray, 0, 40);
+        ttfGen.endString();
+        assertTrue(out.toString().matches("([0-9A-F]{80}\n)00\n> "));
+    }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java
new file mode 100644
index 0000000..82b4364
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java
@@ -0,0 +1,109 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test class for PSTTFGlyphOutputStream
+ */
+public class PSTTFGlyphOutputStreamTestCase {
+    private PSTTFGenerator mockGen;
+    private PSTTFGlyphOutputStream glyphOut;
+
+    @Before
+    public void setUp() {
+        mockGen = mock(PSTTFGenerator.class);
+        glyphOut = new PSTTFGlyphOutputStream(mockGen);
+    }
+
+    /**
+     * Test startGlyphStream() - test that startGlyphStream() invokes reset() and startString() in
+     * PSTTFGenerator.
+     * @exception IOException file write error
+     */
+    @Test
+    public void testStartGlyphStream() throws IOException {
+        glyphOut.startGlyphStream();
+        verify(mockGen).startString();
+    }
+
+    /**
+     * Test streamGlyph(byte[],int,int) - tests several paths:
+     * 1) strings are properly appended
+     * 2) when total strings size > PSTTFGenerator.MAX_BUFFER_SIZE, the strings is closed and a new
+     * strings is started.
+     * 3) if a glyph of size > PSTTFGenerator.MAX_BUFFER_SIZE is attempted, an exception is thrown.
+     * @throws IOException file write error.
+     */
+    @Test
+    public void testStreamGlyph() throws IOException {
+        int byteArraySize = 10;
+        byte[] byteArray = new byte[byteArraySize];
+        int runs = 100;
+        for (int i = 0; i < runs; i++) {
+            glyphOut.streamGlyph(byteArray, 0, byteArraySize);
+        }
+        verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize);
+
+        /*
+         * We want to run this for MAX_BUFFER_SIZE / byteArraySize so that go over the string
+         * boundary and enforce the ending and starting of a new string. Using mockito to ensure
+         * that this behaviour is performed in order (since this is an integral behavioural aspect)
+         */
+        int stringLimit = PSTTFGenerator.MAX_BUFFER_SIZE / byteArraySize;
+        for (int i = 0; i < stringLimit; i++) {
+            glyphOut.streamGlyph(byteArray, 0, byteArraySize);
+        }
+        InOrder inOrder = inOrder(mockGen);
+        inOrder.verify(mockGen, times(stringLimit)).streamBytes(byteArray, 0, byteArraySize);
+        inOrder.verify(mockGen).endString();
+        inOrder.verify(mockGen).startString();
+        inOrder.verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize);
+
+        try {
+            glyphOut.streamGlyph(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+            fail("Shouldn't allow a length > PSTTFGenerator.MAX_BUFFER_SIZE");
+        } catch (UnsupportedOperationException e) {
+            // PASS
+        }
+    }
+
+    /**
+     * Test endGlyphStream() - tests that PSTTFGenerator.endString() is invoked when this method
+     * is called.
+     * @throws IOException file write exception
+     */
+    @Test
+    public void testEndGlyphStream() throws IOException {
+        glyphOut.endGlyphStream();
+        verify(mockGen).endString();
+    }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java
new file mode 100644
index 0000000..744f17f
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java
@@ -0,0 +1,90 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * Tests PSTTFOuputStream
+ */
+public class PSTTFOutputStreamTestCase {
+    private PSGenerator gen;
+    private PSTTFOutputStream out;
+
+    /**
+     * Assigns an OutputStream to the PSGenerator.
+     */
+    @Before
+    public void setUp() {
+        gen = mock(PSGenerator.class);
+        out = new PSTTFOutputStream(gen);
+    }
+
+    /**
+     * Test startFontStream() - Just tests that the font is properly initiated in the PostScript
+     * document (in this case with "/sfnts[")
+     * @throws IOException write exception.
+     */
+    @Test
+    public void testStartFontStream() throws IOException {
+        out.startFontStream();
+        verify(gen).write("/sfnts[");
+    }
+
+    /**
+     * Test getTableOutputStream() - we need to test that the inheritance model is properly obeyed.
+     */
+    @Test
+    public void testGetTableOutputStream() {
+        TTFTableOutputStream tableOut = out.getTableOutputStream();
+        assertTrue(tableOut instanceof PSTTFTableOutputStream);
+    }
+
+    /**
+     * Test getGlyphOutputStream() - we need to test that the inheritance model is properly obeyed.
+     */
+    @Test
+    public void testGetGlyphOutputStream() {
+        TTFGlyphOutputStream glyphOut = out.getGlyphOutputStream();
+        assertTrue(glyphOut instanceof PSTTFGlyphOutputStream);
+    }
+
+    /**
+     * Test endFontStream()
+     * @exception IOException write error.
+     */
+    @Test
+    public void testEndFontStream() throws IOException {
+        out.endFontStream();
+        verify(gen).writeln("] def");
+    }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java
new file mode 100644
index 0000000..c20c3d8
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test class for unit testing PSTTFTableOutputStream
+ */
+public class PSTTFTableOutputStreamTestCase {
+    private PSTTFGenerator mockGen;
+    private PSTTFTableOutputStream tableOut;
+
+    @Before
+    public void setUp() {
+        mockGen = mock(PSTTFGenerator.class);
+        tableOut = new PSTTFTableOutputStream(mockGen);
+    }
+
+    /**
+     * Test streamTable() - several paths to test (2. and 3. test corner cases):
+     * 1) that a table of length < PSTTFGenerator.MAX_BUFFER_SIZE invokes the correct methods in
+     * PSTTFGenerator.
+     * 2) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE and
+     * length == n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator
+     * are invoked.
+     * 3) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE but
+     * length != n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator
+     * are invoked.
+     * @throws IOException file write error.
+     */
+    @Test
+    public void testStreamTable() throws IOException {
+        byte[] byteArray = new byte[PSTTFGenerator.MAX_BUFFER_SIZE * 3];
+        tableOut.streamTable(byteArray, 0, 10);
+        InOrder inOrder = inOrder(mockGen);
+        inOrder.verify(mockGen).startString();
+        inOrder.verify(mockGen).streamBytes(byteArray, 0, 10);
+        inOrder.verify(mockGen).endString();
+
+        setUp(); // reset all all the method calls
+        /* We're going to run this 3 times to ensure the proper method calls are invoked and all
+         * the bytes are streamed */
+        tableOut.streamTable(byteArray, 0, byteArray.length);
+        inOrder = inOrder(mockGen);
+        for (int i = 0; i < 3; i++) {
+            int offset = PSTTFGenerator.MAX_BUFFER_SIZE * i;
+            inOrder.verify(mockGen).startString();
+            inOrder.verify(mockGen).streamBytes(byteArray, offset, PSTTFGenerator.MAX_BUFFER_SIZE);
+            inOrder.verify(mockGen).endString();
+        }
+
+        setUp(); // reset all the method calls
+        tableOut.streamTable(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+        inOrder = inOrder(mockGen);
+        inOrder.verify(mockGen).startString();
+        inOrder.verify(mockGen).streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE);
+        inOrder.verify(mockGen).endString();
+        inOrder.verify(mockGen).startString();
+        inOrder.verify(mockGen).streamBytes(byteArray, PSTTFGenerator.MAX_BUFFER_SIZE, 1);
+        inOrder.verify(mockGen).endString();
+    }
+}
diff --git a/test/java/org/apache/fop/util/HexEncoderTestCase.java b/test/java/org/apache/fop/util/HexEncoderTestCase.java
new file mode 100644
index 0000000..cb366ab
--- /dev/null
+++ b/test/java/org/apache/fop/util/HexEncoderTestCase.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test case for the conversion of characters into hex-encoded strings.
+ */
+public class HexEncoderTestCase {
+
+    /**
+     * Tests that characters are properly encoded into hex strings.
+     */
+    @Test
+    public void testEncodeChar() {
+        char[] digits = new char[] {'0', '0', '0', '0'};
+        for (int c = 0; c <= 0xFFFF; c++) {
+            assertEquals(new String(digits), HexEncoder.encode((char) c));
+            increment(digits);
+        }
+    }
+
+    private static void increment(char[] digits) {
+        int d = 4;
+        do {
+            d--;
+            digits[d] = successor(digits[d]);
+        } while (digits[d] == '0' && d > 0);
+    }
+
+    private static char successor(char d) {
+        if (d == '9') {
+            return 'A';
+        } else if (d == 'F') {
+            return '0';
+        } else {
+            return (char) (d + 1);
+        }
+    }
+
+}
diff --git a/test/resources/fonts/ttf/DroidSansMono.LICENSE b/test/resources/fonts/ttf/DroidSansMono.LICENSE
new file mode 100644
index 0000000..1a96dfd
--- /dev/null
+++ b/test/resources/fonts/ttf/DroidSansMono.LICENSE
@@ -0,0 +1,18 @@
+Copyright (C) 2008 The Android Open Source Project
+
+Licensed 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.
+
+##########
+
+This directory contains the fonts for the platform. They are licensed
+under the Apache 2 license.
diff --git a/test/resources/fonts/ttf/DroidSansMono.ttf b/test/resources/fonts/ttf/DroidSansMono.ttf
new file mode 100644
index 0000000..4546611
--- /dev/null
+++ b/test/resources/fonts/ttf/DroidSansMono.ttf
Binary files differ