Index: test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTest.java
===================================================================
--- test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTest.java	(revision 0)
+++ test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTest.java	(revision 0)
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+/**
+ * This class tests TTFSubSetFile
+ * TODO: Test with more than just a single font
+ */
+public class TTFSubSetFileTest extends TTFFileTest {
+    private TTFSubSetFile ttfSubset;
+    private byte[] subset;
+    /**
+     * Constructor
+     * @throws IOException exception
+     */
+    public TTFSubSetFileTest() throws IOException {
+        super();
+    }
+
+    /**
+     * setUp()
+     * @exception IOException file read error
+     */
+    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
+     */
+    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");
+    }
+}
Index: test/java/org/apache/fop/fonts/truetype/TTFTableNameTest.java
===================================================================
--- test/java/org/apache/fop/fonts/truetype/TTFTableNameTest.java	(revision 0)
+++ test/java/org/apache/fop/fonts/truetype/TTFTableNameTest.java	(revision 0)
@@ -0,0 +1,145 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * This class tests the enum org.apache.fop.fonts.truetype.TTFTableName
+ *
+ */
+public class TTFTableNameTest extends TestCase {
+    /**
+     * Test getName() - tests that the getName() method returns the expected String as expected in
+     * the Directory Table.
+     * @exception IllegalAccessException error
+     */
+    public void testGetName() throws IllegalAccessException {
+        assertEquals("dirTable", TTFTableName.DIRECTORY_TABLE.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
+     */
+    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
+     */
+    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
+     */
+    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));
+    }
+}
Index: test/java/org/apache/fop/fonts/truetype/FontFileReaderTest.java
===================================================================
--- test/java/org/apache/fop/fonts/truetype/FontFileReaderTest.java	(revision 0)
+++ test/java/org/apache/fop/fonts/truetype/FontFileReaderTest.java	(revision 0)
@@ -0,0 +1,283 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * A test class for org.apache.fop.truetype.FontFileReader
+ */
+public class FontFileReaderTest extends TestCase {
+    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 FontFileReaderTest() {
+        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.
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    public void testGetCurrentPos() throws IOException {
+        fontReader.seekSet(10);
+        fontReader.skip(100);
+        assertEquals(110, fontReader.getCurrentPos());
+        fontReader.readTTFByte();
+        assertEquals(111, fontReader.getCurrentPos());
+    }
+
+    /**
+     * Test getFileSize()
+     */
+    public void testGetFileSize() {
+        assertEquals(256, fontReader.getFileSize());
+    }
+
+    /**
+     * Test readTTFUByte()
+     * @throws IOException exception
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    public void testGetBytes() throws IOException {
+        byte[] retrievedBytes = fontReader.getBytes(0, 256);
+        assertTrue(Arrays.equals(byteArray, retrievedBytes));
+    }
+}
Index: test/java/org/apache/fop/fonts/truetype/TTFFileTest.java
===================================================================
--- test/java/org/apache/fop/fonts/truetype/TTFFileTest.java	(revision 0)
+++ test/java/org/apache/fop/fonts/truetype/TTFFileTest.java	(revision 0)
@@ -0,0 +1,399 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+
+/**
+ * Class for testing org.apache.fop.fonts.truetype.TTFFile
+ */
+public class TTFFileTest extends TestCase {
+    // 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 TTFFileTest() throws IOException {
+        dejavuTTFFile = new TTFFile();
+        dejavuReader = new FontFileReader("test/resources/fonts/DejaVuLGCSerif.ttf");
+        dejavuTTFFile.readFont(dejavuReader);
+        droidmonoTTFFile = new TTFFile();
+        droidmonoReader = new FontFileReader("test/resources/fonts/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)
+     */
+    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
+     */
+    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.
+     */
+    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.
+     */
+    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".
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    public void testGetItalicAngle() {
+        assertEquals("0", dejavuTTFFile.getItalicAngle());
+        assertEquals("0", droidmonoTTFFile.getItalicAngle());
+    }
+
+    /**
+     * Test getKerning() - Test values retrieved from the kern table in the font file.
+     */
+    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
+     */
+    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.
+     */
+    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.
+     */
+    public void testGetPostScriptName() {
+        assertEquals(PostScriptVersion.V2, dejavuTTFFile.getPostScriptVersion());
+        assertEquals(PostScriptVersion.V2, droidmonoTTFFile.getPostScriptVersion());
+    }
+
+    /**
+     * Test getStemV() - Undefined.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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
+    }
+}
Index: test/java/org/apache/fop/fonts/FOPFontsTestSuite.java
===================================================================
--- test/java/org/apache/fop/fonts/FOPFontsTestSuite.java	(revision 0)
+++ test/java/org/apache/fop/fonts/FOPFontsTestSuite.java	(revision 0)
@@ -0,0 +1,54 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.fop.fonts.truetype.FontFileReaderTest;
+import org.apache.fop.fonts.truetype.TTFFileTest;
+import org.apache.fop.fonts.truetype.TTFSubSetFileTest;
+import org.apache.fop.fonts.truetype.TTFTableNameTest;
+
+/**
+ * A test suite designed for org.apache.fop.fonts.*
+ */
+public final class FOPFontsTestSuite {
+    /**
+     * Constructor
+     */
+    private FOPFontsTestSuite() {
+    }
+    /**
+     * Testing org.apache.fop.fonts.*
+     * @return test
+     */
+    public static Test suite() {
+        TestSuite testSuite = new TestSuite("Test suite for FOPs fonts classes");
+        //$JUnit-BEGIN$
+        testSuite.addTest(new TestSuite(EncodingModeTest.class));
+        testSuite.addTest(new TestSuite(FontFileReaderTest.class));
+        testSuite.addTest(new TestSuite(TTFFileTest.class));
+        testSuite.addTest(new TestSuite(TTFSubSetFileTest.class));
+        testSuite.addTest(new TestSuite(TTFTableNameTest.class));
+        //$JUnit-END$
+        return testSuite;
+    }
+}
Index: test/java/org/apache/fop/fonts/EncodingModeTest.java
===================================================================
--- test/java/org/apache/fop/fonts/EncodingModeTest.java	(revision 1042681)
+++ test/java/org/apache/fop/fonts/EncodingModeTest.java	(working copy)
@@ -21,16 +21,33 @@
 
 import junit.framework.TestCase;
 
+/**
+ * Tests the enum org.apache.fop.fonts.EncodingMode.
+ */
 public class EncodingModeTest extends TestCase {
+    /**
+     * Test getName() - tests the getName() method returns the expected String.
+     */
     public void testGetName() {
         assertEquals("auto", EncodingMode.AUTO.getName());
         assertEquals("single-byte", EncodingMode.SINGLE_BYTE.getName());
         assertEquals("cid", EncodingMode.CID.getName());
     }
 
+    /**
+     * Test getValue() - test that getValue() method returns the expected enum value when given
+     * an appropriate String.
+     */
     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"));
+        try {
+            // We expect this to fail
+            assertEquals(EncodingMode.AUTO, EncodingMode.getValue("fail"));
+            fail("Encoding mode fails to throw an appropriate exception");
+        } catch (IllegalArgumentException e) {
+            // PASS
+        }
     }
 }
Index: test/java/org/apache/fop/fonts/DejaVuLGCSerifTest.java
===================================================================
--- test/java/org/apache/fop/fonts/DejaVuLGCSerifTest.java	(revision 1042681)
+++ test/java/org/apache/fop/fonts/DejaVuLGCSerifTest.java	(working copy)
@@ -33,13 +33,13 @@
 
     /**
      * sets up the testcase by loading the DejaVu Font.
-     * 
+     *
      * @throws Exception
      *             if the test fails.
      */
     public void setUp() throws Exception {
         File file = new File("test/resources/fonts/DejaVuLGCSerif.ttf");
-        font = FontLoader.loadFont(file, "", true, EncodingMode.AUTO,
+        font = FontLoader.loadFont(file, "", true, EmbeddingMode.AUTO, EncodingMode.AUTO,
                 fontResolver);
     }
 
Index: test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTest.java
===================================================================
--- test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTest.java	(revision 0)
+++ test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTest.java	(revision 0)
@@ -0,0 +1,105 @@
+/*
+ * 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 static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.mockito.InOrder;
+
+/**
+ * Test class for PSTTFGlyphOutputStream
+ */
+public class PSTTFGlyphOutputStreamTest extends TestCase {
+    private PSTTFGenerator mockGen;
+    private PSTTFGlyphOutputStream glyphOut;
+
+    @Override
+    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
+     */
+    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.
+     */
+    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
+     */
+    public void testEndGlyphStream() throws IOException {
+        glyphOut.endGlyphStream();
+        verify(mockGen).endString();
+    }
+}
Index: test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTest.java
===================================================================
--- test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTest.java	(revision 0)
+++ test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTest.java	(revision 0)
@@ -0,0 +1,86 @@
+/*
+ * 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 static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.mockito.InOrder;
+
+/**
+ * Test class for unit testing PSTTFTableOutputStream
+ */
+public class PSTTFTableOutputStreamTest extends TestCase {
+    private PSTTFGenerator mockGen;
+    private PSTTFTableOutputStream tableOut;
+
+    @Override
+    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.
+     */
+    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();
+    }
+}
Index: test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTest.java
===================================================================
--- test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTest.java	(revision 0)
+++ test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTest.java	(revision 0)
@@ -0,0 +1,111 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * The test class for org.apache.fop.render.ps.fonts.PSGenerator
+ */
+public class PSTTFGeneratorTest extends TestCase {
+    private PSTTFGenerator ttfGen;
+    private ByteArrayOutputStream out = new ByteArrayOutputStream();
+    private PSGenerator gen = new PSGenerator(out);
+    private byte[] byteArray;
+
+    /**
+     * Constructor
+     */
+    public PSTTFGeneratorTest() {
+        byteArray = new byte[65536];
+        for (int i = 0; i < 65536; i++) {
+            byteArray[i] = (byte) i;
+        }
+    }
+
+    @Override
+    public void setUp() {
+        ttfGen = new PSTTFGenerator(gen);
+    }
+
+    /**
+     * Tests startString() - starts the string in an appropriate way for a PostScript file.
+     * @exception IOException write error
+     */
+    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.
+     */
+    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.
+     */
+    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
+     */
+    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> "));
+    }
+}
Index: test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTest.java
===================================================================
--- test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTest.java	(revision 0)
+++ test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTest.java	(revision 0)
@@ -0,0 +1,82 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * Tests PSTTFOuputStream
+ */
+public class PSTTFOutputStreamTest extends TestCase {
+    private PSGenerator gen;
+    private PSTTFOutputStream out;
+
+    /**
+     * Assigns an OutputStream to the PSGenerator.
+     */
+    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.
+     */
+    public void testStartFontStream() throws IOException {
+        out.startFontStream();
+        verify(gen).write("/sfnts[");
+    }
+
+    /**
+     * Test getTableOutputStream() - we need to test that the inheritance model is properly obeyed.
+     */
+    public void testGetTableOutputStream() {
+        TTFTableOutputStream tableOut = out.getTableOutputStream();
+        assertTrue(tableOut instanceof PSTTFTableOutputStream);
+    }
+
+    /**
+     * Test getGlyphOutputStream() - we need to test that the inheritance model is properly obeyed.
+     */
+    public void testGetGlyphOutputStream() {
+        TTFGlyphOutputStream glyphOut = out.getGlyphOutputStream();
+        assertTrue(glyphOut instanceof PSTTFGlyphOutputStream);
+    }
+
+    /**
+     * Test endFontStream()
+     * @exception IOException write error.
+     */
+    public void testEndFontStream() throws IOException {
+        out.endFontStream();
+        verify(gen).writeln("] def");
+    }
+}
Index: test/java/org/apache/fop/render/ps/RenderPSTestSuite.java
===================================================================
--- test/java/org/apache/fop/render/ps/RenderPSTestSuite.java	(revision 0)
+++ test/java/org/apache/fop/render/ps/RenderPSTestSuite.java	(revision 0)
@@ -0,0 +1,55 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.fop.render.ps.fonts.PSTTFGeneratorTest;
+import org.apache.fop.render.ps.fonts.PSTTFGlyphOutputStreamTest;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStreamTest;
+import org.apache.fop.render.ps.fonts.PSTTFTableOutputStreamTest;
+
+
+/**
+ * A test Suite for org.apache.fop.render.ps.*
+ */
+public final class RenderPSTestSuite {
+    /**
+     * Constructor.
+     */
+    private RenderPSTestSuite() {
+    }
+
+    /**
+     * Testing org.apache.fop.render.ps.*
+     * @return test
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        //$JUnit-BEGIN$
+        suite.addTest(new TestSuite(PSTTFGeneratorTest.class));
+        suite.addTest(new TestSuite(PSTTFOutputStreamTest.class));
+        suite.addTest(new TestSuite(PSTTFGlyphOutputStreamTest.class));
+        suite.addTest(new TestSuite(PSTTFTableOutputStreamTest.class));
+        //$JUnit-END$
+        return suite;
+    }
+}
Index: test/resources/fonts/DroidSansMono.LICENSE
===================================================================
--- test/resources/fonts/DroidSansMono.LICENSE	(revision 0)
+++ test/resources/fonts/DroidSansMono.LICENSE	(revision 0)
@@ -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.
Index: test/resources/fonts/DroidSansMono.ttf
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: test/resources/fonts/DroidSansMono.ttf
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
===================================================================
--- src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+
+/**
+ * This class streams glyphs from the "glyf" table in a True Type font.
+ */
+public class PSTTFGlyphOutputStream implements TTFGlyphOutputStream {
+    /** This counts the total number of bytes written that have been streamed. */
+    private int byteCounter = 0;
+
+    /** This is a place-holder for the offset of the last string boundary. */
+    private int lastStringBoundary = 0;
+    private PSTTFGenerator ttfGen;
+
+    /**
+     * Constructor
+     * @param ttfGen PSTTFGenerator
+     */
+    public PSTTFGlyphOutputStream(PSTTFGenerator ttfGen) {
+        this.ttfGen = ttfGen;
+    }
+
+    /** {@inheritDoc} */
+    public void startGlyphStream()  throws IOException {
+        ttfGen.startString();
+    }
+
+    /** {@inheritDoc} */
+    public void streamGlyph(byte[] byteArray, int offset, int length) throws IOException {
+        if (length > PSTTFGenerator.MAX_BUFFER_SIZE) {
+            throw new UnsupportedOperationException("The glyph is " + length + " there may be an "
+                    + "error in the font file.");
+        }
+
+        if (length + (byteCounter - lastStringBoundary) < PSTTFGenerator.MAX_BUFFER_SIZE) {
+            ttfGen.streamBytes(byteArray, offset, length);
+        } else {
+            ttfGen.endString();
+            lastStringBoundary = byteCounter;
+            ttfGen.startString();
+            ttfGen.streamBytes(byteArray, offset, length);
+        }
+        byteCounter += length;
+    }
+
+    /** {@inheritDoc} */
+    public void endGlyphStream() throws IOException {
+        ttfGen.endString();
+    }
+}
Index: src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
===================================================================
--- src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java	(revision 0)
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * This class streams a truetype table to a PostScript file.
+ *
+ */
+public class PSTTFTableOutputStream implements TTFTableOutputStream  {
+    private PSTTFGenerator ttfGen;
+    /**
+     * Constructor.
+     * @param ttfGen PSGenerator the streamer class used for streaming bytes.
+     */
+    public PSTTFTableOutputStream(PSTTFGenerator ttfGen) {
+        this.ttfGen = ttfGen;
+    }
+
+    /** {@inheritDoc} */
+    public void streamTable(byte[] byteArray, int offset, int length) throws IOException {
+        int offsetPosition = offset;
+        // Need to split the table into MAX_BUFFER_SIZE chunks
+        for (int i = 0; i < length / PSTTFGenerator.MAX_BUFFER_SIZE; i++) {
+            streamString(byteArray, offsetPosition, PSTTFGenerator.MAX_BUFFER_SIZE);
+            offsetPosition += PSTTFGenerator.MAX_BUFFER_SIZE;
+        }
+        if (length % PSTTFGenerator.MAX_BUFFER_SIZE > 0) {
+            streamString(byteArray, offsetPosition, length % PSTTFGenerator.MAX_BUFFER_SIZE);
+        }
+    }
+
+    private void streamString(byte[] byteArray, int offset, int length) throws IOException {
+        ttfGen.startString();
+        ttfGen.streamBytes(byteArray, offset, length);
+        ttfGen.endString();
+    }
+}
Index: src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
===================================================================
--- src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java	(revision 0)
+++ src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java	(revision 0)
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream;
+
+/**
+ * This is a wrapper for {@link PSGenerator} that contains some members specific for streaming
+ * True Type fonts to a PostScript document.
+ */
+public class PSTTFGenerator {
+    private PSGenerator gen;
+    private ASCIIHexOutputStream hexOut;
+
+    /**
+     * The buffer is used to store the font file in an array of hex-encoded strings. Strings are
+     * limited to 65535 characters, string will start with a newline, 2 characters are needed to
+     * hex-encode each byte.
+     */
+    public static final int MAX_BUFFER_SIZE = 32764;
+
+    /**
+     * Constructor - initialises the PSGenerator in this wrapper class.
+     * @param gen PSGenerator
+     */
+    public PSTTFGenerator(PSGenerator gen) {
+        this.gen = gen;
+        hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+    }
+
+    /**
+     * Begins writing a string by writing '<' to the begin.
+     * @throws IOException file write exception.
+     */
+    public void startString() throws IOException {
+        // We need to reset the streamer so that it starts a new line in the PS document
+        hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+        gen.writeln("<");
+    }
+
+    /**
+     * Streams a string to a PostScript document (wraps PSGenerator.write(String)).
+     * @param cmd String
+     * @throws IOException file write exception
+     */
+    public void write(String cmd) throws IOException {
+        gen.write(cmd);
+    }
+
+    /**
+     * Streams a string followed by a new line char to a PostScript document (wraps
+     * PSGenerator.writeln(String)).
+     * @param cmd String
+     * @throws IOException file write exception
+     */
+    public void writeln(String cmd) throws IOException {
+        gen.writeln(cmd);
+    }
+
+    /**
+     * Streams the bytes.
+     * @param byteArray byte[] the byte array to stream to file.
+     * @param offset int the starting position in the byte array to stream to file.
+     * @param length the number of bytes to stream to file. This MUST be less than
+     * MAX_BUFFER_SIZE - 1 since strings are suffixed by '00' (as in spec).
+     * @throws IOException file write exception
+     */
+    public void streamBytes(byte[] byteArray, int offset, int length) throws IOException {
+        if (length > MAX_BUFFER_SIZE) {
+            throw new UnsupportedOperationException("Attempting to write a string to a PostScript"
+                    + " file that is greater than the buffer size.");
+        }
+        hexOut.write(byteArray, offset, length);
+    }
+
+    /**
+     * Finishes writing a string by appending '00' and '>' to the end.
+     * @throws IOException file write exception
+     */
+    public void endString() throws IOException {
+        /* Appends a '00' to the end of the string as specified in the spec */
+        gen.write("00\n> ");
+    }
+}
Index: src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
===================================================================
--- src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java	(revision 0)
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * Implements TTFOutputStream and streams font tables to a PostScript file.
+ */
+public class PSTTFOutputStream implements TTFOutputStream {
+    /** The wrapper class for PSGenerator */
+    private final PSTTFGenerator ttfGen;
+
+    /**
+     * Constructor - assigns a PSGenerator to stream the font.
+     * @param gen PSGenerator.
+     */
+    public PSTTFOutputStream(PSGenerator gen) {
+        this.ttfGen = new PSTTFGenerator(gen);
+    }
+
+    /** {@inheritDoc} */
+    public void startFontStream() throws IOException {
+        ttfGen.write("/sfnts[");
+    }
+
+    /** {@inheritDoc} */
+    public TTFTableOutputStream getTableOutputStream() {
+        return new PSTTFTableOutputStream(ttfGen);
+    }
+
+    /** {@inheritDoc} */
+    public TTFGlyphOutputStream getGlyphOutputStream() {
+        return new PSTTFGlyphOutputStream(ttfGen);
+    }
+
+    /** {@inheritDoc} */
+    public void endFontStream() throws IOException {
+        ttfGen.writeln("] def");
+    }
+
+}
Index: src/java/org/apache/fop/render/ps/PSPainter.java
===================================================================
--- src/java/org/apache/fop/render/ps/PSPainter.java	(revision 1042681)
+++ src/java/org/apache/fop/render/ps/PSPainter.java	(working copy)
@@ -28,18 +28,8 @@
 import java.io.IOException;
 import java.util.Map;
 
-import org.w3c.dom.Document;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
-import org.apache.xmlgraphics.image.loader.ImageException;
-import org.apache.xmlgraphics.image.loader.ImageInfo;
-import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
-import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-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.FontTriplet;
@@ -56,6 +46,13 @@
 import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.CharUtilities;
 import org.apache.fop.util.HexEncoder;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.w3c.dom.Document;
 
 /**
  * IFPainter implementation that produces PostScript.
@@ -380,8 +377,9 @@
                     int encoding = mapped / 256;
                     if (currentEncoding != encoding) {
                         if (i > 0) {
-                            writeText(text, start, i - start,
-                                    letterSpacing, wordSpacing, dx, font, tf, false);
+                            TextData txtData = new TextData(text, dx, start, i - start,
+                                    letterSpacing, wordSpacing);
+                            writeText(txtData, font, tf);
                         }
                         if (encoding == 0) {
                             useFont(fontKey, sizeMillipoints);
@@ -392,114 +390,118 @@
                         start = i;
                     }
                 }
-                writeText(text, start, textLen - start,
-                        letterSpacing, wordSpacing, dx, font, tf, false);
+                TextData txtData = new TextData(text, dx, start, textLen - start, letterSpacing,
+                        wordSpacing);
+                writeText(txtData, font, tf);
             } else {
                 useFont(fontKey, sizeMillipoints);
-                writeText(text, 0, textLen,
-                        letterSpacing, wordSpacing, dx, font, tf, tf instanceof MultiByteFont);
+                TextData txtData = new TextData(text, dx, 0, textLen, letterSpacing, wordSpacing,
+                        tf instanceof MultiByteFont);
+                writeText(txtData, font, tf);
             }
         } catch (IOException ioe) {
             throw new IFException("I/O error in drawText()", ioe);
         }
     }
 
-    private void writeText(                                      // CSOK: ParameterNumber
-            String text, int start, int len,
-            int letterSpacing, int wordSpacing, int[] dx,
-            Font font, Typeface tf, boolean multiByte) throws IOException {
-        PSGenerator generator = getGenerator();
-        int end = start + len;
-        int initialSize = len;
-        initialSize += initialSize / 2;
+    private void writeText(TextData txtData, Font font, Typeface tf)
+            throws IOException {
+        PSGenerator gen = getGenerator();
+        boolean hasLetterSpacing = (txtData.letterSpacing != 0);
 
-        boolean hasLetterSpacing = (letterSpacing != 0);
-        boolean needTJ = false;
+        if (txtData.needTJ) {
+            gen.write("[");
+        }
+        gen.write(txtData.openString);
+        int charsWritten = txtData.openString.length() + 1;
 
-        char strOpen;
-        char strClose;
-        if (multiByte) {
-            strOpen = '<';
-            strClose = '>';
+        if (hasLetterSpacing) {
+            gen.write(formatMptAsPt(gen, txtData.letterSpacing) + " 0 ");
+        }
+
+        charsWritten = writeChars(txtData, charsWritten, font);
+
+        if (txtData.needTJ) {
+            if (charsWritten > 0) {
+                gen.write(txtData.closeString);
+            }
+            if (hasLetterSpacing) {
+                gen.writeln("] " + formatMptAsPt(gen, txtData.letterSpacing) + " ATJ");
+            } else {
+                gen.writeln("] TJ");
+            }
         } else {
-            strOpen = '(';
-            strClose = ')';
+            gen.write(txtData.closeString);
+            if (hasLetterSpacing) {
+                gen.writeln(" ashow");
+            } else {
+                gen.writeln(" show");
+            }
         }
+    }
 
-        int lineStart = 0;
-        StringBuffer accText = new StringBuffer(initialSize);
-        StringBuffer sb = new StringBuffer(initialSize);
-        int dxl = (dx != null ? dx.length : 0);
-        for (int i = start; i < end; i++) {
-            char orgChar = text.charAt(i);
+    private int writeChars(TextData txtData, int charsWritten, Font font) throws IOException {
+        int dxl = (txtData.dx != null ? txtData.dx.length : 0);
+        for (int i = txtData.start; i < txtData.start + txtData.length; i++) {
+            char orgChar = txtData.text.charAt(i);
             char ch;
             int cw;
             int glyphAdjust = 0;
             if (CharUtilities.isFixedWidthSpace(orgChar)) {
-                //Fixed width space are rendered as spaces so copy/paste works in a reader
+                // Fixed width space are rendered as spaces so copy/paste works
+                // in a reader
                 ch = font.mapChar(CharUtilities.SPACE);
                 cw = font.getCharWidth(orgChar);
                 glyphAdjust = font.getCharWidth(ch) - cw;
             } else {
-                if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
-                    glyphAdjust -= wordSpacing;
+                if ((txtData.wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+                    glyphAdjust -= txtData.wordSpacing;
                 }
                 ch = font.mapChar(orgChar);
                 cw = font.getCharWidth(orgChar);
             }
 
-            if (dx != null && i < dxl - 1) {
-                glyphAdjust -= dx[i + 1];
+            if (txtData.dx != null && i < dxl - 1) {
+                glyphAdjust -= txtData.dx[i + 1];
             }
-            if (multiByte) {
-                accText.append(HexEncoder.encode(ch));
+            if (txtData.multiByte) {
+                String encodedChar = HexEncoder.encode(ch);
+                writeToPSGenerator(encodedChar, false, charsWritten);
+                charsWritten += encodedChar.length();
             } else {
-                char codepoint = (char)(ch % 256);
-                PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+                char codepoint = (char) (ch % 256);
+                StringBuffer sb = new StringBuffer();
+                PSGenerator.escapeChar(codepoint, sb);
+                writeToPSGenerator(sb.toString(), true, charsWritten);
+                charsWritten += sb.length();
             }
             if (glyphAdjust != 0) {
-                needTJ = true;
-                if (sb.length() == 0) {
-                    sb.append('['); //Need to start TJ
+                if (charsWritten > 0) {
+                    getGenerator().write(txtData.closeString);
+                    getGenerator().write(" ");
+                    charsWritten += txtData.closeString.length() + 1;
                 }
-                if (accText.length() > 0) {
-                    if ((sb.length() - lineStart + accText.length()) > 200) {
-                        sb.append(PSGenerator.LF);
-                        lineStart = sb.length();
-                    }
-                    sb.append(strOpen);
-                    sb.append(accText);
-                    sb.append(strClose);
-                    sb.append(' ');
-                    accText.setLength(0); //reset accumulated text
-                }
-                sb.append(Integer.toString(glyphAdjust)).append(' ');
+                String strGlyphAdjust = Integer.toString(glyphAdjust) + " ";
+                writeToPSGenerator(strGlyphAdjust, true, charsWritten);
+                getGenerator().write(txtData.openString);
+                charsWritten += strGlyphAdjust.length() + txtData.openString.length();
             }
         }
-        if (needTJ) {
-            if (accText.length() > 0) {
-                sb.append(strOpen);
-                sb.append(accText);
-                sb.append(strClose);
+        return charsWritten;
+    }
+
+    private void writeToPSGenerator(String text, boolean useSlash, int charsWritten)
+            throws IOException {
+        PSGenerator gen = getGenerator();
+        int charCounter = 0;
+        for (Character c : text.toCharArray()) {
+            if ((charsWritten > 0) && ((charCounter + charsWritten) % 80 == 0)) {
+                gen.writeln(useSlash ? "\\" : "");
             }
-            if (hasLetterSpacing) {
-                sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ");
-            } else {
-                sb.append("] TJ");
-            }
-        } else {
-            sb.append(strOpen).append(accText).append(strClose);
-            if (hasLetterSpacing) {
-                StringBuffer spb = new StringBuffer();
-                spb.append(formatMptAsPt(generator, letterSpacing))
-                    .append(" 0 ");
-                sb.insert(0, spb.toString());
-                sb.append(" ashow");
-            } else {
-                sb.append(" show");
-            }
+
+            gen.write(c.toString());
+            charCounter++;
         }
-        generator.writeln(sb.toString());
     }
 
     private void useFont(String key, int size) throws IOException {
@@ -509,5 +511,57 @@
         res.notifyResourceUsageOnPage(generator.getResourceTracker());
     }
 
+    /**
+     * This is just a data class for holding information about the text written to a PS document.
+     */
+    private static final class TextData {
+        private final String text;
+        private final int[] dx;
+        private final int start;
+        private final int length;
+        private final int letterSpacing;
+        private final int wordSpacing;
+        private final boolean multiByte;
+        private final boolean needTJ;
+        private final String openString;
+        private final String closeString;
 
+        public TextData(String text, int[] dx, int start, int len, int letterSpacing,
+                int wordSpacing) {
+            this(text, dx, start, len, letterSpacing, wordSpacing, false);
+        }
+
+        public TextData(String text, int[] dx, int start, int len, int letterSpacing,
+                int wordSpacing, boolean multiByte) {
+            this.text = text;
+            this.dx = dx;
+            this.start = start;
+            this.length = len;
+            this.letterSpacing = letterSpacing;
+            this.wordSpacing = wordSpacing;
+            this.multiByte = multiByte;
+            needTJ = needsTJ(dx);
+            /* These need to be strings otherwise PSGenerator prints them as ints. */
+            if (this.multiByte) {
+                openString = "<";
+                closeString = ">";
+            } else {
+                openString = "(";
+                closeString = ")";
+            }
+        }
+
+        private boolean needsTJ(int[] dx) {
+            if (dx == null) {
+                return false;
+            }
+            for (int i : dx) {
+                if (i != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
 }
Index: src/java/org/apache/fop/render/ps/PSFontUtils.java
===================================================================
--- src/java/org/apache/fop/render/ps/PSFontUtils.java	(revision 1042681)
+++ src/java/org/apache/fop/render/ps/PSFontUtils.java	(working copy)
@@ -23,26 +23,22 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import javax.xml.transform.Source;
 import javax.xml.transform.stream.StreamSource;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
-import org.apache.xmlgraphics.fonts.Glyphs;
-import org.apache.xmlgraphics.ps.DSCConstants;
-import org.apache.xmlgraphics.ps.PSGenerator;
-import org.apache.xmlgraphics.ps.PSResource;
-import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
-import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream;
-
 import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.Base14Font;
 import org.apache.fop.fonts.CIDFontType;
 import org.apache.fop.fonts.CIDSubset;
 import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.EmbeddingMode;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontType;
@@ -52,9 +48,17 @@
 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.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStream;
 import org.apache.fop.util.HexEncoder;
+import org.apache.xmlgraphics.fonts.Glyphs;
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
 
 /**
  * Utility code for font handling in PostScript.
@@ -63,7 +67,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
@@ -119,11 +122,12 @@
      * @return a Map of PSResource instances representing all defined fonts (key: font key)
      * @throws IOException in case of an I/O problem
      */
-    private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts,
-            boolean encodeAllCharacters, PSEventProducer eventProducer) throws IOException {
+    private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
+            Map<String, Typeface> fonts, boolean encodeAllCharacters, PSEventProducer eventProducer)
+            throws IOException {
         gen.commentln("%FOPBeginFontDict");
 
-        Map fontResources = new java.util.HashMap();
+        Map fontResources = new HashMap();
         for (String key : fonts.keySet()) {
             Typeface tf = getTypeFace(fontInfo, fonts, key);
             PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
@@ -219,58 +223,53 @@
 
     private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
             PSEventProducer eventProducer) throws IOException {
-        boolean embeddedFont = false;
         FontType fontType = tf.getFontType();
         PSFontResource fontResource = null;
-        if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
-                || fontType == FontType.TYPE0) {
-            if (tf instanceof CustomFont) {
-                CustomFont cf = (CustomFont)tf;
-                if (isEmbeddable(cf)) {
-                    InputStream in = getInputStreamOnFont(gen, cf);
-                    if (in != null) {
-                        if (fontType == FontType.TYPE0) {
-                            if (gen.embedIdentityH()) {
-                                checkPostScriptLevel3(gen, eventProducer);
-                                /*
-                                 * First CID-keyed font to be embedded; add
-                                 * %%IncludeResource: comment for ProcSet CIDInit.
-                                 */
-                                gen.includeProcsetCIDInitResource();
-                            }
-                            PSResource cidFontResource = embedType2CIDFont(gen,
-                                    (MultiByteFont) tf, in);
-                            fontResource = PSFontResource.createFontResource(fontRes,
-                                    gen.getProcsetCIDInitResource(),
-                                    gen.getIdentityHCMapResource(),
-                                    cidFontResource);
-                        }
-                        gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
-                                fontRes);
-                        if (fontType == FontType.TYPE1) {
-                            embedType1Font(gen, in);
-                            fontResource = PSFontResource.createFontResource(fontRes);
-                        } else if (fontType == FontType.TRUETYPE) {
-                            embedTrueTypeFont(gen, (SingleByteFont) tf, in);
-                            fontResource = PSFontResource.createFontResource(fontRes);
-                        } else {
-                            composeType0Font(gen, (MultiByteFont) tf, in);
-                        }
-                        gen.writeDSCComment(DSCConstants.END_RESOURCE);
-                        gen.getResourceTracker().registerSuppliedResource(fontRes);
-                        embeddedFont = true;
-                    } else {
-                        gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
-                        log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
-                                + " PostScript file but could not be embedded!");
-                    }
+        if (!(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);
+                fontResource = PSFontResource.createFontResource(fontRes);
+            } else {
+                composeType0Font(gen, (MultiByteFont) tf, in);
+            }
+            gen.writeDSCComment(DSCConstants.END_RESOURCE);
+            gen.getResourceTracker().registerSuppliedResource(fontRes);
         }
-        if (!embeddedFont) {
-            gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
-            fontResource = PSFontResource.createFontResource(fontRes);
-        }
         return fontResource;
     }
 
@@ -288,16 +287,24 @@
     }
 
     private static void embedTrueTypeFont(PSGenerator gen,
-            SingleByteFont font, InputStream fontStream) throws IOException {
+            SingleByteFont font) 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");
-        createType42DictionaryEntries(gen, font, fontStream, font.getCMap());
+        if (font.getEmbeddingMode() == EmbeddingMode.AUTO) {
+            font.setEmbeddingMode(EmbeddingMode.SUBSET);
+        }
+        createType42DictionaryEntries(gen, font, font.getCMap());
         gen.writeln("FontName currentdict end definefont pop");
     }
 
     private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font,
-            InputStream fontStream, BFEntry[] cmap) throws IOException {
+            BFEntry[] cmap) throws IOException {
+        createType42DictionaryEntries(gen, font, cmap, null);
+    }
+
+    private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font,
+            BFEntry[] cmap, TTFFile ttfFile) throws IOException {
         gen.write("/FontName /");
         gen.write(font.getEmbedFontName());
         gen.writeln(" def");
@@ -308,7 +315,8 @@
         gen.writeln("/Encoding 256 array");
         gen.writeln("0 1 255{1 index exch/.notdef put}for");
         boolean buildCharStrings;
-        if (font.getFontType() == FontType.TYPE0) {
+        Set<String> glyphNames = new HashSet<String>();
+        if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) {
             //"/Encoding" is required but ignored for CID fonts
             //so we keep it minimal to save space
             buildCharStrings = false;
@@ -323,72 +331,75 @@
                     gen.write(Glyphs.NOTDEF);
                 } else {
                     gen.write(glyphName);
+                    glyphNames.add(glyphName);
                 }
                 gen.writeln(" put");
             }
         }
         gen.writeln("readonly def");
-        gen.write("/sfnts[");
-        /*
-         * Store the font file in an array of hex-encoded strings. Strings are limited to
-         * 65535 characters, string will start with a newline, 2 characters are needed to
-         * hex-encode each byte, one newline character will be added every 40 bytes, each
-         * string should start at a 4-byte boundary
-         * => buffer size = floor((65535 - 1) * 40 / 81 / 4) * 4
-         * TODO this is not robust: depends on how often ASCIIHexOutputStream adds a newline
-         */
-        // TODO does not follow Technical Note #5012's requirements:
-        // "strings must begin at TrueType table boundaries, or at individual glyph
-        // boundaries within the glyf table."
-        // There may be compatibility issues with older PostScript interpreters
-        byte[] buffer = new byte[32360];
-        int readCount;
-        while ((readCount = fontStream.read(buffer)) > 0) {
-            ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
-            gen.writeln("<");
-            hexOut.write(buffer, 0, readCount);
-            gen.write("> ");
-        }
-        gen.writeln("]def");
+        TTFOutputStream ttfOut = new PSTTFOutputStream(gen);
+        ttfFile.stream(ttfOut);
+
+        buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font);
+    }
+
+    private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings,
+            BFEntry[] cmap, Set<String> glyphNames, CustomFont font) throws IOException {
         gen.write("/CharStrings ");
-        if (buildCharStrings) {
+        if (!buildCharStrings) {
+            gen.write(1);
+        } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
             int charCount = 1; //1 for .notdef
             for (BFEntry entry : cmap) {
                 charCount += entry.getUnicodeEnd() - entry.getUnicodeStart() + 1;
             }
             gen.write(charCount);
         } else {
-            gen.write(1);
+            gen.write(font.getCMap().length);
         }
         gen.writeln(" dict dup begin");
         gen.write("/");
         gen.write(Glyphs.NOTDEF);
         gen.writeln(" 0 def"); // .notdef always has to be at index 0
-        if (buildCharStrings) {
-            //Only performed in singly-byte mode, ignored for CID fonts
-
+        if (!buildCharStrings) {
+            // If we're not building the full CharStrings we can end here
+            gen.writeln("end readonly def");
+            return;
+        }
+        if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+          //Only performed in singly-byte mode, ignored for CID fonts
             for (BFEntry entry : cmap) {
                 int glyphIndex = entry.getGlyphStartIndex();
                 for (int ch = entry.getUnicodeStart(); ch <= entry.getUnicodeEnd(); ch++) {
                     char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit
                     String glyphName = Glyphs.charToGlyphName(ch16);
-
                     if ("".equals(glyphName)) {
                         glyphName = "u" + Integer.toHexString(ch).toUpperCase();
                     }
-                    gen.write("/");
-                    gen.write(glyphName);
-                    gen.write(" ");
-                    gen.write(glyphIndex);
-                    gen.writeln(" def");
+                    writeGlyphDefs(gen, glyphName, glyphIndex);
 
                     glyphIndex++;
                 }
             }
+        } else {
+            for (String name : glyphNames) {
+                writeGlyphDefs(gen, name,
+                        getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0),
+                                font.getCMap()));
+            }
         }
         gen.writeln("end readonly def");
     }
 
+    private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex)
+                throws IOException {
+        gen.write("/");
+        gen.write(glyphName);
+        gen.write(" ");
+        gen.write(glyphIndex);
+        gen.writeln(" def");
+    }
+
     private static int getGlyphIndex(char c, BFEntry[] cmap) {
         for (BFEntry entry : cmap) {
             if (entry.getUnicodeStart() <= c && c <= entry.getUnicodeEnd()) {
@@ -408,7 +419,7 @@
         gen.writeln("] composefont pop");
     }
 
-    private static PSResource embedType2CIDFont(final PSGenerator gen,
+    private static PSResource embedType2CIDFont(PSGenerator gen,
             MultiByteFont font, InputStream fontStream) throws IOException {
         assert font.getCIDType() == CIDFontType.CIDTYPE2;
 
@@ -467,30 +478,28 @@
                     lineCount = 1;
                 }
             }
-            String gid = HexEncoder.encode(cid, 4);
+            String gid;
+            if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+                gid = HexEncoder.encode(cid, 4);
+            } else {
+                gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4);
+            }
             gen.write(gid);
         }
         gen.writeln(">] def");
+        FontFileReader reader = new FontFileReader(fontStream);
 
-        //Create tables for subset
-        TTFSubSetFile subset = new TTFSubSetFile();
-        TTFSubSetFile.GlyphHandler glyphHandler = new TTFSubSetFile.GlyphHandler() {
+        TTFFile ttfFile;
+        if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+            ttfFile = new TTFSubSetFile();
+            ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs());
+        } else {
+            ttfFile = new TTFFile();
+            ttfFile.readFont(reader, font.getTTCName());
+        }
 
-            public void addGlyph(byte[] glyphData) throws IOException {
-                ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
-                gen.writeln("<");
-                hexOut.write(glyphData);
-                gen.writeln(">");
-            }
-        };
-        gen.writeln("/GlyphDirectory [");
-        FontFileReader reader = new FontFileReader(fontStream);
-        byte[] subsetFont = subset.toPostScriptSubset(reader,
-                             font.getTTCName(), font.getUsedGlyphs(), glyphHandler);
-        gen.writeln("] def");
 
-        InputStream subsetInput = new java.io.ByteArrayInputStream(subsetFont);
-        createType42DictionaryEntries(gen, font, subsetInput, new BFEntry[0]);
+        createType42DictionaryEntries(gen, font, new BFEntry[0], ttfFile);
         gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop");
         gen.writeln("end");
         gen.writeln("%%EndResource");
@@ -702,5 +711,4 @@
         gen.getResourceTracker().registerSuppliedResource(res);
         return res;
     }
-
 }
Index: src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
===================================================================
--- src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java	(revision 1042681)
+++ src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java	(working copy)
@@ -25,7 +25,6 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
 import org.apache.fop.fonts.CustomFont;
 import org.apache.fop.fonts.EmbedFontInfo;
 import org.apache.fop.fonts.EncodingMode;
@@ -88,8 +87,8 @@
                     font = new CustomFontMetricsMapper(fontMetrics, fontSource);
                 } else {
                     CustomFont fontMetrics = FontLoader.loadFont(
-                            fontFile, null, true, EncodingMode.AUTO,
-                            configFontInfo.getKerning(), fontResolver);
+                            fontFile, null, true, configFontInfo.getEmbeddingMode(),
+                            EncodingMode.AUTO, configFontInfo.getKerning(), fontResolver);
                     font = new CustomFontMetricsMapper(fontMetrics);
                 }
 
Index: src/java/org/apache/fop/fonts/CustomFont.java
===================================================================
--- src/java/org/apache/fop/fonts/CustomFont.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/CustomFont.java	(working copy)
@@ -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;
@@ -114,6 +115,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
@@ -262,7 +271,7 @@
         return lastChar;
     }
 
-    /**
+    /**MutableFont
      * Used to determine if kerning is enabled.
      * @return True if kerning is enabled.
      */
@@ -330,6 +339,13 @@
     /**
      * {@inheritDoc}
      */
+    public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+        this.embeddingMode = embeddingMode;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void setCapHeight(int capHeight) {
         this.capHeight = capHeight;
     }
Index: src/java/org/apache/fop/fonts/MutableFont.java
===================================================================
--- src/java/org/apache/fop/fonts/MutableFont.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/MutableFont.java	(working copy)
@@ -61,6 +61,12 @@
     void setEmbedResourceName(String name);
 
     /**
+     * Set the embedding mode for this font.
+     * @param embeddingMode the embedding mode.
+     */
+    void setEmbeddingMode(EmbeddingMode embeddingMode);
+
+    /**
      * Sets the capital height value.
      * @param capHeight capital height
      */
Index: src/java/org/apache/fop/fonts/apps/TTFReader.java
===================================================================
--- src/java/org/apache/fop/fonts/apps/TTFReader.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/apps/TTFReader.java	(working copy)
@@ -20,7 +20,6 @@
 package org.apache.fop.fonts.apps;
 
 import java.io.IOException;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
@@ -28,9 +27,9 @@
 
 import org.apache.commons.logging.LogFactory;
 import org.apache.fop.Version;
+import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.FontUtil;
 import org.apache.fop.fonts.truetype.FontFileReader;
-import org.apache.fop.fonts.truetype.TTFCmapEntry;
 import org.apache.fop.fonts.truetype.TTFFile;
 import org.apache.fop.util.CommandLineLogger;
 import org.w3c.dom.Document;
@@ -272,9 +271,9 @@
             root.appendChild(el);
             el.appendChild(doc.createTextNode(ttf.getFullName()));
         }
-        Set familyNames = ttf.getFamilyNames();
+        Set<String> familyNames = ttf.getFamilyNames();
         if (familyNames.size() > 0) {
-            String familyName = (String)familyNames.iterator().next();
+            String familyName = familyNames.iterator().next();
             el = doc.createElement("family-name");
             root.appendChild(el);
             el.appendChild(doc.createTextNode(familyName));
@@ -370,9 +369,7 @@
 
         el = doc.createElement("bfranges");
         mel.appendChild(el);
-        Iterator iter = ttf.getCMaps().listIterator();
-        while (iter.hasNext()) {
-            TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+        for (BFEntry ce : ttf.getCMaps()) {
             Element el2 = doc.createElement("bf");
             el.appendChild(el2);
             el2.setAttribute("us", String.valueOf(ce.getUnicodeStart()));
@@ -427,31 +424,28 @@
         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());
Index: src/java/org/apache/fop/fonts/EncodingMode.java
===================================================================
--- src/java/org/apache/fop/fonts/EncodingMode.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/EncodingMode.java	(working copy)
@@ -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;
Index: src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java	(working copy)
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.fonts.truetype;
-
-/**
- * The CMap entry contains information of a Unicode range and the
- * the glyph indexes related to the range
- */
-public class TTFCmapEntry {
-
-    //TODO this class is redundant: BFEntry does the same but doesn't have an intuitive name
-
-    private int unicodeStart;
-    private int unicodeEnd;
-    private int glyphStartIndex;
-
-    TTFCmapEntry() {
-        unicodeStart = 0;
-        unicodeEnd = 0;
-        glyphStartIndex = 0;
-    }
-
-    TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
-        this.unicodeStart = unicodeStart;
-        this.unicodeEnd = unicodeEnd;
-        this.glyphStartIndex = glyphStartIndex;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        int hc = super.hashCode();
-        hc ^= ( hc * 11 ) + unicodeStart;
-        hc ^= ( hc * 19 ) + unicodeEnd;
-        hc ^= ( hc * 23 ) + glyphStartIndex;
-        return hc;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof TTFCmapEntry) {
-            TTFCmapEntry ce = (TTFCmapEntry)o;
-            if (ce.unicodeStart == this.unicodeStart
-                    && ce.unicodeEnd == this.unicodeEnd
-                    && ce.glyphStartIndex == this.glyphStartIndex) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns the glyphStartIndex.
-     * @return int
-     */
-    public int getGlyphStartIndex() {
-        return glyphStartIndex;
-    }
-
-    /**
-     * Returns the unicodeEnd.
-     * @return int
-     */
-    public int getUnicodeEnd() {
-        return unicodeEnd;
-    }
-
-    /**
-     * Returns the unicodeStart.
-     * @return int
-     */
-    public int getUnicodeStart() {
-        return unicodeStart;
-    }
-
-    /**
-     * Sets the glyphStartIndex.
-     * @param glyphStartIndex The glyphStartIndex to set
-     */
-    public void setGlyphStartIndex(int glyphStartIndex) {
-        this.glyphStartIndex = glyphStartIndex;
-    }
-
-    /**
-     * Sets the unicodeEnd.
-     * @param unicodeEnd The unicodeEnd to set
-     */
-    public void setUnicodeEnd(int unicodeEnd) {
-        this.unicodeEnd = unicodeEnd;
-    }
-
-    /**
-     * Sets the unicodeStart.
-     * @param unicodeStart The unicodeStart to set
-     */
-    public void setUnicodeStart(int unicodeStart) {
-        this.unicodeStart = unicodeStart;
-    }
-
-}
Index: src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java	(revision 0)
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+
+/**
+ * An interface for streaming full True Type tables from a TTF file.
+ */
+public interface TTFTableOutputStream {
+
+    /**
+     * Streams a table defined in byteArray at offset of length bytes.
+     * @param byteArray The source of the table to stream from.
+     * @param offset The position in byteArray to begin streaming from.
+     * @param length The number of bytes to stream.
+     * @throws IOException write error.
+     */
+    void streamTable(byte[] byteArray, int offset, int length) throws IOException;
+}
Index: src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java	(working copy)
@@ -20,8 +20,10 @@
 package org.apache.fop.fonts.truetype;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedSet;
 
 
 /**
@@ -34,10 +36,6 @@
  */
 public class TTFSubSetFile extends TTFFile {
 
-    private static enum OperatingMode {
-        PDF, POSTSCRIPT_GLYPH_DIRECTORY;
-    }
-
     private byte[] output = null;
     private int realSize = 0;
     private int currentPos = 0;
@@ -46,27 +44,25 @@
      * Offsets in name table to be filled out by table.
      * The offsets are to the checkSum field
      */
-    private Map<String, Integer> offsets = new java.util.HashMap<String, Integer>();
-    private int glyfDirOffset = 0;
-    private int headDirOffset = 0;
-    private int hmtxDirOffset = 0;
-    private int locaDirOffset = 0;
-    private int maxpDirOffset = 0;
+    private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>();
 
     private int checkSumAdjustmentOffset = 0;
     private int locaOffset = 0;
 
-    private int determineTableCount(OperatingMode operatingMode) {
-        int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
+    /** Stores the glyph offsets so that we can end strings at glyph boundaries */
+    private int[] glyphOffsets;
+
+    /** The dir tab entries in the new subset font. */
+    private Map<TTFTableName, TTFDirTabEntry> newDirTabs
+                        = new HashMap<TTFTableName, TTFDirTabEntry>();
+
+    private int determineTableCount() {
+        int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp,
         if (isCFF()) {
             throw new UnsupportedOperationException(
                     "OpenType fonts with CFF glyphs are not supported");
         } else {
-            if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
-                numTables++; //1 table: gdir
-            } else {
-                numTables += 2; //2 req'd tables: glyf,loca
-            }
+            numTables += 4; //4 req'd tables: glyf,loca,post,name
             if (hasCvt()) {
                 numTables++;
             }
@@ -83,8 +79,8 @@
     /**
      * Create the directory table
      */
-    private void createDirectory(OperatingMode operatingMode) {
-        int numTables = determineTableCount(operatingMode);
+    private void createDirectory() {
+        int numTables = determineTableCount();
         // Create the TrueType header
         writeByte((byte)0);
         writeByte((byte)1);
@@ -97,7 +93,7 @@
 
         // Create searchRange, entrySelector and rangeShift
         int maxPow = maxPow2(numTables);
-        int searchRange = maxPow * 16;
+        int searchRange = (int) Math.pow(2, maxPow) * 16;
         writeUShort(searchRange);
         realSize += 2;
 
@@ -106,83 +102,47 @@
 
         writeUShort((numTables * 16) - searchRange);
         realSize += 2;
+     // Create space for the table entries (these must be in ASCII alphabetical order[A-Z]then[a-z])
+        writeTableName(TTFTableName.OS2);
 
-        // Create space for the table entries
         if (hasCvt()) {
-            writeString("cvt ");
-            offsets.put("cvt ", currentPos);
-            currentPos += 12;
-            realSize += 16;
+            writeTableName(TTFTableName.CVT);
         }
-
         if (hasFpgm()) {
-            writeString("fpgm");
-            offsets.put("fpgm", currentPos);
-            currentPos += 12;
-            realSize += 16;
+            writeTableName(TTFTableName.FPGM);
         }
-
-        if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
-            writeString("glyf");
-            glyfDirOffset = 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()) {
+            writeTableName(TTFTableName.PREP);
         }
+        newDirTabs.put(TTFTableName.DIRECTORY_TABLE, new TTFDirTabEntry(0, currentPos));
+    }
 
-        writeString("head");
-        headDirOffset = currentPos;
+    private void writeTableName(TTFTableName tableName) {
+        writeString(tableName.getName());
+        offsets.put(tableName, currentPos);
         currentPos += 12;
         realSize += 16;
-
-        writeString("hhea");
-        offsets.put("hhea", currentPos);
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("hmtx");
-        hmtxDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
-            writeString("loca");
-            locaDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
-        }
-
-        writeString("maxp");
-        maxpDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        if (hasPrep()) {
-            writeString("prep");
-            offsets.put("prep", currentPos);
-            currentPos += 12;
-            realSize += 16;
-        }
-
-        if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) {
-            //"gdir" indicates to the PostScript interpreter that the GlyphDirectory approach
-            //is in use.
-            writeString("gdir");
-            currentPos += 12;
-            realSize += 16;
-        }
     }
 
 
     private boolean hasCvt() {
-        return dirTabs.containsKey("cvt ");
+        return dirTabs.containsKey(TTFTableName.CVT);
     }
 
     private boolean hasFpgm() {
-        return dirTabs.containsKey("fpgm");
+        return dirTabs.containsKey(TTFTableName.FPGM);
     }
 
     private boolean hasPrep() {
-        return dirTabs.containsKey("prep");
+        return dirTabs.containsKey(TTFTableName.PREP);
     }
 
     /**
@@ -191,24 +151,22 @@
     private void createLoca(int size) throws IOException {
         pad4();
         locaOffset = currentPos;
-        writeULong(locaDirOffset + 4, currentPos);
-        writeULong(locaDirOffset + 8, size * 4 + 4);
+        int dirTableOffset = offsets.get(TTFTableName.LOCA);
+        writeULong(dirTableOffset + 4, currentPos);
+        writeULong(dirTableOffset + 8, size * 4 + 4);
         currentPos += size * 4 + 4;
         realSize += size * 4 + 4;
     }
 
-    private boolean copyTable(FontFileReader in, String tableName) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get(tableName);
+    private boolean copyTable(FontFileReader in, TTFTableName tableName) throws IOException {
+        TTFDirTabEntry entry = dirTabs.get(tableName);
         if (entry != null) {
             pad4();
             seekTab(in, tableName, 0);
             System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                              0, output, currentPos, (int)entry.getLength());
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            int offset = offsets.get(tableName);
-            writeULong(offset, checksum);
-            writeULong(offset + 4, currentPos);
-            writeULong(offset + 8, (int)entry.getLength());
+
+            updateCheckSum(currentPos, (int)entry.getLength(), tableName);
             currentPos += (int)entry.getLength();
             realSize += (int)entry.getLength();
             return true;
@@ -221,33 +179,51 @@
      * Copy the cvt table as is from original font to subset font
      */
     private boolean createCvt(FontFileReader in) throws IOException {
-        return copyTable(in, "cvt ");
+        return copyTable(in, TTFTableName.CVT);
     }
 
     /**
      * Copy the fpgm table as is from original font to subset font
      */
     private boolean createFpgm(FontFileReader in) throws IOException {
-        return copyTable(in, "fpgm");
+        return copyTable(in, TTFTableName.FPGM);
     }
 
     /**
+     * Copy the name table as is from the original.
+     * @param in FontFileReader
+     * @return boolean
+     * @throws IOException exception
+     */
+    private boolean createName(FontFileReader in) throws IOException {
+        return copyTable(in, TTFTableName.NAME);
+    }
+
+    /**
+     * Copy the OS/2 table as is from the original.
+     * @param in
+     * @return
+     * @throws IOException
+     */
+    private boolean createOS2(FontFileReader in) throws IOException {
+        return copyTable(in, TTFTableName.OS2);
+    }
+
+    /**
      * 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 {
@@ -255,12 +231,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 = 28;          // This is the post table size with glyphs truncated
+            byte[] newPostTable = new byte[28];
+            // We only want the first 28 bytes (truncate the glyph names);
+            System.arraycopy(in.getBytes((int) entry.getOffset(), newTableSize),
+                    0, newPostTable, 0, newTableSize);
+            // set the post table to Format 3.0
+            newPostTable[1] = 0x03;
+            System.arraycopy(newPostTable, 0, output, currentPos, newTableSize);
+            updateCheckSum(currentPos, newTableSize, post);
+            currentPos += newTableSize;
+            realSize += newTableSize;
+        } else {
+            throw new IOException("Can't find post table");
+        }
+    }
 
+
     /**
      * Copy the prep table as is from original font to subset font
      */
     private boolean createPrep(FontFileReader in) throws IOException {
-        return copyTable(in, "prep");
+        return copyTable(in, TTFTableName.PREP);
     }
 
 
@@ -269,8 +267,18 @@
      * and fill in size of hmtx table
      */
     private void createHhea(FontFileReader in, int size) throws IOException {
-        boolean copied = copyTable(in, "hhea");
-        if (!copied) {
+        TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA);
+        if (entry != null) {
+            pad4();
+            seekTab(in, TTFTableName.HHEA, 0);
+            System.arraycopy(in.getBytes((int) entry.getOffset(), (int) entry.getLength()), 0,
+                    output, currentPos, (int) entry.getLength());
+
+            writeUShort((int) entry.getLength() + currentPos - 2, size);
+            updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA);
+            currentPos += (int) entry.getLength();
+            realSize += (int) entry.getLength();
+        } else {
             throw new IOException("Can't find hhea table");
         }
     }
@@ -283,10 +291,11 @@
      * in checkSumAdjustmentOffset
      */
     private void createHead(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+        TTFTableName head = TTFTableName.HEAD;
+        TTFDirTabEntry entry = dirTabs.get(head);
         if (entry != null) {
             pad4();
-            seekTab(in, "head", 0);
+            seekTab(in, head, 0);
             System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                              0, output, currentPos, (int)entry.getLength());
 
@@ -298,11 +307,7 @@
             output[currentPos + 50] = 0;    // long locaformat
             output[currentPos + 51] = 1;    // long locaformat
 
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(headDirOffset, checksum);
-            writeULong(headDirOffset + 4, currentPos);
-            writeULong(headDirOffset + 8, (int)entry.getLength());
-
+            updateCheckSum(currentPos, (int)entry.getLength(), head);
             currentPos += (int)entry.getLength();
             realSize += (int)entry.getLength();
         } else {
@@ -315,8 +320,9 @@
      * Create the glyf table and fill in loca table
      */
     private void createGlyf(FontFileReader in,
-                            Map<Integer, Integer> glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+            Map<Integer, Integer> glyphs) throws IOException {
+        TTFTableName glyf = TTFTableName.GLYF;
+        TTFDirTabEntry entry = dirTabs.get(glyf);
         int size = 0;
         int startPos = 0;
         int endOffset = 0;    // Store this as the last loca
@@ -329,6 +335,7 @@
              * location offset.
              */
             int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+            glyphOffsets = new int[origIndexes.length];
 
             for (int i = 0; i < origIndexes.length; i++) {
                 int nextOffset = 0;
@@ -358,27 +365,30 @@
                     endOffset1 = (currentPos - startPos + glyphLength);
                 }
 
+                // Store the glyph boundary positions relative to the start the font
+                glyphOffsets[i] = currentPos;
                 currentPos += glyphLength;
                 realSize += glyphLength;
 
-                endOffset = endOffset1;
 
+                endOffset = endOffset1;
             }
 
+
             size = currentPos - startPos;
 
-            int checksum = getCheckSum(startPos, size);
-            writeULong(glyfDirOffset, checksum);
-            writeULong(glyfDirOffset + 4, startPos);
-            writeULong(glyfDirOffset + 8, size);
             currentPos += 12;
             realSize += 12;
+            updateCheckSum(startPos, size + 12, glyf);
 
             // Update loca checksum and last loca index
             writeULong(locaOffset + glyphs.size() * 4, endOffset);
-
-            checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
-            writeULong(locaDirOffset, checksum);
+            int locaSize = glyphs.size() * 4 + 4;
+            int checksum = getCheckSum(output, locaOffset, locaSize);
+            writeULong(offsets.get(TTFTableName.LOCA), checksum);
+            int padSize = (locaOffset + locaSize) % 4;
+            newDirTabs.put(TTFTableName.LOCA,
+                    new TTFDirTabEntry(locaOffset, locaSize + padSize));
         } else {
             throw new IOException("Can't find glyf table");
         }
@@ -402,7 +412,8 @@
      */
     private void createHmtx(FontFileReader in,
                             Map<Integer, Integer> glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+        TTFTableName hmtx = TTFTableName.HMTX;
+        TTFDirTabEntry entry = dirTabs.get(hmtx);
 
         int longHorMetricSize = glyphs.size() * 2;
         int leftSideBearingSize = glyphs.size() * 2;
@@ -421,10 +432,7 @@
                             mtxTab[origIndex.intValue()].getLsb());
             }
 
-            int checksum = getCheckSum(currentPos, hmtxSize);
-            writeULong(hmtxDirOffset, checksum);
-            writeULong(hmtxDirOffset + 4, currentPos);
-            writeULong(hmtxDirOffset + 8, hmtxSize);
+            updateCheckSum(currentPos, hmtxSize, hmtx);
             currentPos += hmtxSize;
             realSize += hmtxSize;
         } else {
@@ -540,16 +548,16 @@
      */
     private void scanGlyphs(FontFileReader in,
                             Map<Integer, Integer> glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+        TTFDirTabEntry entry = dirTabs.get(TTFTableName.GLYF);
         Map<Integer, Integer> newComposites = null;
-        Map<Integer, Integer> allComposites = new java.util.HashMap<Integer, Integer>();
+        Map<Integer, Integer> allComposites = new HashMap<Integer, Integer>();
 
         int newIndex = glyphs.size();
 
         if (entry != null) {
             while (newComposites == null || newComposites.size() > 0) {
                 // Inefficient to iterate through all glyphs
-                newComposites = new java.util.HashMap<Integer, Integer>();
+                newComposites = new HashMap<Integer, Integer>();
 
                 for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
                     int origIndex = glyph.getKey();
@@ -591,47 +599,39 @@
         }
     }
 
-
-
     /**
-     * Returns a subset of the original font.
+     * Reads a font and creates a subset of the font.
      *
      * @param in FontFileReader to read from
      * @param name Name to be checked for in the font file
      * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
      * new index as (Integer) value)
-     * @return A subset of the original font
      * @throws IOException in case of an I/O problem
      */
-    public byte[] readFont(FontFileReader in, String name,
+    public void readFont(FontFileReader in, String name,
                            Map<Integer, Integer> glyphs) throws IOException {
-
+        fontFile = in;
         //Check if TrueType collection, and that the name exists in the collection
-        if (!checkTTC(in, name)) {
+        if (!checkTTC(name)) {
             throw new IOException("Failed to read font");
         }
 
         //Copy the Map as we're going to modify it
-        Map<Integer, Integer> subsetGlyphs = new java.util.HashMap<Integer, Integer>(glyphs);
+        Map<Integer, Integer> subsetGlyphs = new HashMap<Integer, Integer>(glyphs);
 
         output = new byte[in.getFileSize()];
 
-        readDirTabs(in);
-        readFontHeader(in);
-        getNumGlyphs(in);
-        readHorizontalHeader(in);
-        readHorizontalMetrics(in);
-        readIndexToLocation(in);
+        readDirTabs();
+        readFontHeader();
+        getNumGlyphs();
+        readHorizontalHeader();
+        readHorizontalMetrics();
+        readIndexToLocation();
 
         scanGlyphs(in, subsetGlyphs);
 
-        createDirectory(OperatingMode.PDF);     // Create the TrueType header and directory
+        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
-
         boolean optionalTableFound;
         optionalTableFound = createCvt(in);    // copy the cvt table
         if (!optionalTableFound) {
@@ -644,141 +644,74 @@
             // fpgm is optional (used in TrueType fonts only)
             log.debug("TrueType: fpgm table not present. Skipped.");
         }
-
-        optionalTableFound = createPrep(in);    // copy prep table
-        if (!optionalTableFound) {
-            // prep is optional (used in TrueType fonts only)
-            log.debug("TrueType: prep table not present. Skipped.");
-        }
-
         createLoca(subsetGlyphs.size());    // create empty loca table
-        createGlyf(in, subsetGlyphs);       //create glyf table and update loca table
+        createGlyf(in, subsetGlyphs); //create glyf table and update loca table
 
-        pad4();
-        createCheckSumAdjustment();
-
-        byte[] ret = new byte[realSize];
-        System.arraycopy(output, 0, ret, 0, realSize);
-
-        return ret;
-    }
-
-    /**
-     * Returns a subset of the original font suitable for use in PostScript programs.
-     *
-     * @param in FontFileReader to read from
-     * @param name Name to be checked for in the font file
-     * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
-     * new index as (Integer) value)
-     * @param glyphHandler the handler to receive all glyphs of the subset
-     * @return A subset of the original font
-     * @throws IOException in case of an I/O problem
-     */
-    public byte[] toPostScriptSubset(FontFileReader in, String name,
-                           Map glyphs, GlyphHandler glyphHandler) throws IOException {
-
-        //Check if TrueType collection, and that the name exists in the collection
-        if (!checkTTC(in, name)) {
-            throw new IOException("Failed to read font");
-        }
-
-        //Copy the Map as we're going to modify it
-        Map<Integer, Integer> subsetGlyphs = new java.util.HashMap(glyphs);
-
-        output = new byte[in.getFileSize()];
-
-        readDirTabs(in);
-        readFontHeader(in);
-        getNumGlyphs(in);
-        readHorizontalHeader(in);
-        readHorizontalMetrics(in);
-        readIndexToLocation(in);
-
-        scanGlyphs(in, subsetGlyphs);
-
-        // Create the TrueType header and directory
-        createDirectory(OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY);
-
+        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
 
-        boolean optionalTableFound;
-        optionalTableFound = createCvt(in);    // copy the cvt table
-        if (!optionalTableFound) {
-            // cvt is optional (used in TrueType fonts only)
-            log.debug("TrueType: ctv table not present. Skipped.");
-        }
-
-        optionalTableFound = createFpgm(in);    // copy fpgm table
-        if (!optionalTableFound) {
-            // fpgm is optional (used in TrueType fonts only)
-            log.debug("TrueType: fpgm table not present. Skipped.");
-        }
-
         optionalTableFound = createPrep(in);    // copy prep table
         if (!optionalTableFound) {
             // prep is optional (used in TrueType fonts only)
             log.debug("TrueType: prep table not present. Skipped.");
         }
 
-        //Send all the glyphs from the subset
-        handleGlyphSubset(in, subsetGlyphs, glyphHandler);
-
         pad4();
         createCheckSumAdjustment();
+    }
 
+    /**
+     * Returns a subset of the fonts (readFont() MUST be called first in order to create the
+     * subset).
+     * @return byte array
+     */
+    public byte[] getFontSubset() {
         byte[] ret = new byte[realSize];
         System.arraycopy(output, 0, ret, 0, realSize);
-
         return ret;
     }
 
-    private void handleGlyphSubset(FontFileReader in, Map<Integer, Integer> glyphs,
-            GlyphHandler glyphHandler) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
-        if (entry != null) {
+    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();
+    }
 
-            int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+    @Override
+    public void stream(TTFOutputStream ttfOut) throws IOException {
+        SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>>  sortedDirTabs
+                = sortDirTabMap(newDirTabs);
+        TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+        TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
 
-            for (int i = 0; i < origIndexes.length; i++) {
-                int nextOffset = 0;
-                int origGlyphIndex = origIndexes[i];
-                if (origGlyphIndex >= (mtxTab.length - 1)) {
-                    nextOffset = (int)lastLoca;
-                } else {
-                    nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
-                }
-                int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset();
-                int glyphLength = nextOffset - glyphOffset;
-
-                byte[] glyphData = in.getBytes(
-                        (int)entry.getOffset() + glyphOffset,
-                        glyphLength);
-
-                glyphHandler.addGlyph(glyphData);
+        ttfOut.startFontStream();
+        for (Map.Entry<TTFTableName, TTFDirTabEntry>  entry : sortedDirTabs) {
+            if (entry.getKey() == TTFTableName.GLYF) {
+                    handleGlyphSubset(glyphOut);
+            } else {
+                tableOut.streamTable(output, (int) entry.getValue().getOffset(),
+                            (int) entry.getValue().getLength());
             }
-        } else {
-            throw new IOException("Can't find glyf table");
         }
+        ttfOut.endFontStream();
     }
 
     /**
-     * Used as callback to handle a number of glyphs.
-     */
-    public static interface GlyphHandler {
-
-        /**
-         * Adds a glyph.
-         * @param glyphData the glyph data
-         * @throws IOException if an I/O error occurs
-         */
-        void addGlyph(byte[] glyphData) throws IOException;
-
-    }
-
-    /**
      * writes a ISO-8859-1 string at the currentPosition
      * updates currentPosition but not realSize
      * @return number of bytes written
@@ -827,20 +760,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,
@@ -858,40 +777,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++;
+            }
         }
     }
 
@@ -900,23 +795,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 int getCheckSum(int start, int size) {
-        return (int)getLongCheckSum(output, start, size);
+    private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) {
+        int checksum = getCheckSum(output, tableStart, tableSize);
+        int offset = offsets.get(tableName);
+        int padSize = getPadSize(tableStart +  tableSize);
+        newDirTabs.put(tableName, new TTFDirTabEntry(tableStart, tableSize + padSize));
+        writeULong(offset, checksum);
+        writeULong(offset + 4, tableStart);
+        writeULong(offset + 8, tableSize);
     }
 
-    private static long getLongCheckSum(byte[] data, int start, int size) {
+    private static int getCheckSum(byte[] data, int start, int size) {
         // All the tables here are aligned on four byte boundaries
         // Add remainder to size if it's not a multiple of 4
         int remainder = size % 4;
@@ -927,26 +824,19 @@
         long sum = 0;
 
         for (int i = 0; i < size; i += 4) {
-            int l = (data[start + i] << 24);
-            l += (data[start + i + 1] << 16);
-            l += (data[start + i + 2] << 16);
-            l += (data[start + i + 3] << 16);
-            sum += l;
-            if (sum > 0xffffffff) {
-                sum = sum - 0xffffffff;
+            long l = 0;
+            for (int j = 0; j < 4; j++) {
+                l <<= 8;
+                l |= data[start + i + j] & 0xff;
             }
+            sum += l;
         }
-
-        return sum;
+        return (int) sum;
     }
 
     private void createCheckSumAdjustment() {
-        long sum = getLongCheckSum(output, 0, realSize);
+        long sum = getCheckSum(output, 0, realSize);
         int checksum = (int)(0xb1b0afba - sum);
         writeULong(checkSumAdjustmentOffset, checksum);
     }
-
-}
-
-
-
+}
\ No newline at end of file
Index: src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java	(working copy)
@@ -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, return tag name
      */
Index: src/java/org/apache/fop/fonts/truetype/TTFTableName.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFTableName.java	(revision 0)
+++ src/java/org/apache/fop/fonts/truetype/TTFTableName.java	(revision 0)
@@ -0,0 +1,167 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class holds the True Type Format table names as in the Directory Table of a TTF font file.
+ * This class must also support custom tables found in fonts (thus an enum wasn't used).
+ */
+public final class TTFTableName {
+    private static final Map<String, TTFTableName> TABLES = new HashMap<String, TTFTableName>();
+
+    /** The first table in a True Type font file containing metadata about other tables. */
+    public static final TTFTableName DIRECTORY_TABLE = new TTFTableName("dirTable");
+
+    /** Embedded bitmap data */
+    public static final TTFTableName EBDT = new TTFTableName("EBDT");
+
+    /** Embedded bitmap location data */
+    public static final TTFTableName EBLC = new TTFTableName("EBLC");
+
+    /** Embedded bitmap scaling data */
+    public static final TTFTableName EBSC = new TTFTableName("EBSC");
+
+    /** A font forge specific table */
+    public static final TTFTableName FFTM = new TTFTableName("FFTM");
+
+    /** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */
+    public static final TTFTableName GDEF = new TTFTableName("GDEF");
+
+    /** Provides kerning information, mark-to-base, etc. for opentype fonts */
+    public static final TTFTableName GPOS = new TTFTableName("GPOS");
+
+    /** Provides ligature information, swash, etc. for opentype fonts */
+    public static final TTFTableName GSUB = new TTFTableName("GSUB");
+
+    /** Linear threshold table */
+    public static final TTFTableName LTSH = new TTFTableName("LTSH");
+
+    /** OS/2 and Windows specific metrics */
+    public static final TTFTableName OS2 = new TTFTableName("OS/2");
+
+    /** PCL 5 data*/
+    public static final TTFTableName PCLT = new TTFTableName("PCLT");
+
+    /** Vertical Device Metrics table */
+    public static final TTFTableName VDMX = new TTFTableName("VDMX");
+
+    /** character to glyph mapping */
+    public static final TTFTableName CMAP = new TTFTableName("cmap");
+
+    /** Control Value Table */
+    public static final TTFTableName CVT = new TTFTableName("cvt ");
+
+    /** font program */
+    public static final TTFTableName FPGM = new TTFTableName("fpgm");
+
+    /** grid-fitting and scan conversion procedure (grayscale) */
+    public static final TTFTableName GASP = new TTFTableName("gasp");
+
+    /** glyph data */
+    public static final TTFTableName GLYF = new TTFTableName("glyf");
+
+    /** horizontal device metrics */
+    public static final TTFTableName HDMX = new TTFTableName("hdmx");
+
+    /** font header */
+    public static final TTFTableName HEAD = new TTFTableName("head");
+
+    /** horizontal header */
+    public static final TTFTableName HHEA = new TTFTableName("hhea");
+
+    /** horizontal metrics */
+    public static final TTFTableName HMTX = new TTFTableName("hmtx");
+
+    /** kerning */
+    public static final TTFTableName KERN = new TTFTableName("kern");
+
+    /** index to location */
+    public static final TTFTableName LOCA = new TTFTableName("loca");
+
+    /** maximum profile */
+    public static final TTFTableName MAXP = new TTFTableName("maxp");
+
+    /** naming table */
+    public static final TTFTableName NAME = new TTFTableName("name");
+
+    /** PostScript information */
+    public static final TTFTableName POST = new TTFTableName("post");
+
+    /** CVT Program */
+    public static final TTFTableName PREP = new TTFTableName("prep");
+
+    /** Vertical Metrics header */
+    public static final TTFTableName VHEA = new TTFTableName("vhea");
+
+    /** Vertical Metrics */
+    public static final TTFTableName VMTX = new TTFTableName("vmtx");
+
+    private final String name;
+
+    private TTFTableName(String name) {
+        this.name = name;
+        TABLES.put(name.toLowerCase(), this);
+    }
+
+    /**
+     * Returns the name of the table as it should be in the Table Directory.
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the appropriate TTFTableName object when given the string representation.
+     * @param tableName table name as in the Directory Table.
+     * @return TTFTableName
+     */
+    public static TTFTableName getValue(String tableName) {
+        TTFTableName table = TABLES.get(tableName.toLowerCase());
+        if (table != null) {
+            return table;
+        }
+        // This allows support for non-standard tables
+        return new TTFTableName(tableName);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o instanceof TTFTableName) {
+            TTFTableName to = (TTFTableName) o;
+            if (this.name.equals(to.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
Index: src/java/org/apache/fop/fonts/truetype/FontFileReader.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/FontFileReader.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/FontFileReader.java	(working copy)
@@ -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);
         }
@@ -277,14 +267,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");
     }
 
@@ -322,7 +312,7 @@
      * @throws IOException If EOF is reached
      */
     public final String readTTFString(int len, int encodingID) throws IOException {
-        if ((len + current) > fsize) {
+        if ((len + current) >= fsize) {
             throw new java.io.EOFException("Reached EOF, file size=" + fsize);
         }
 
@@ -352,6 +342,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;
+    }
 }
\ No newline at end of file
Index: src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java	(revision 0)
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+
+/**
+ * This is an interface for streaming True Type font.
+ */
+public interface TTFOutputStream {
+    /**
+     * Starts writing the font to file.
+     * @throws IOException file write exception.
+     */
+    void startFontStream() throws IOException;
+
+    /**
+     * Returns an object for streaming True Type tables.
+     * @return {@link TTFTableOutputStream}
+     */
+    TTFTableOutputStream getTableOutputStream();
+
+    /**
+     * Returns an object for streaming True Type glyphs in the glyf table.
+     * @return {@link TTFGlyphOutputStream}
+     */
+    TTFGlyphOutputStream getGlyphOutputStream();
+
+    /**
+     * Ends writing the font to file.
+     * @throws IOException file write exception.
+     */
+    void endFontStream() throws IOException;
+}
Index: src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java	(working copy)
@@ -21,15 +21,13 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.io.IOUtils;
-
 import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.EmbeddingMode;
 import org.apache.fop.fonts.EncodingMode;
 import org.apache.fop.fonts.FontLoader;
 import org.apache.fop.fonts.FontResolver;
@@ -49,6 +47,7 @@
     private SingleByteFont singleFont;
     private final String subFontName;
     private EncodingMode encodingMode;
+    private EmbeddingMode embeddingMode;
 
     /**
      * Default constructor
@@ -56,7 +55,7 @@
      * @param resolver the FontResolver for font URI resolution
      */
     public TTFFontLoader(String fontFileURI, FontResolver resolver) {
-        this(fontFileURI, null, true, EncodingMode.AUTO, true, resolver);
+        this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, resolver);
     }
 
     /**
@@ -65,23 +64,27 @@
      * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal
      *          TrueType fonts)
      * @param embedded indicates whether the font is embedded or referenced
+     * @param embeddingMode the embedding mode of the font
      * @param encodingMode the requested encoding mode
      * @param useKerning true to enable loading kerning info if available, false to disable
      * @param resolver the FontResolver for font URI resolution
      */
     public TTFFontLoader(String fontFileURI, String subFontName,
-                boolean embedded, EncodingMode encodingMode, boolean useKerning,
-                FontResolver resolver) {
+                boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+                boolean useKerning, FontResolver resolver) {
         super(fontFileURI, embedded, true, resolver);
         this.subFontName = subFontName;
         this.encodingMode = encodingMode;
+        this.embeddingMode = embeddingMode;
         if (this.encodingMode == EncodingMode.AUTO) {
             this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType
         }
+        if (this.embeddingMode == EmbeddingMode.AUTO) {
+            this.embeddingMode = EmbeddingMode.SUBSET;
+        }
     }
 
     /** {@inheritDoc} */
-    @Override
     protected void read() throws IOException {
         read(this.subFontName);
     }
@@ -144,7 +147,7 @@
         returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle()));
         returnFont.setMissingWidth(0);
         returnFont.setWeight(ttf.getWeightClass());
-
+        returnFont.setEmbeddingMode(this.embeddingMode);
         if (isCid) {
             multiFont.setCIDType(CIDFontType.CIDTYPE2);
             int[] wx = ttf.getWidths();
@@ -168,15 +171,8 @@
     }
 
     private BFEntry[] getCMap(TTFFile ttf) {
-        List<TTFCmapEntry> entries = ttf.getCMaps();
-        BFEntry[] bfentries = new BFEntry[entries.size()];
-        int pos = 0;
-        for (TTFCmapEntry ce : ttf.getCMaps()) {
-            bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(),
-                    ce.getGlyphStartIndex());
-            pos++;
-        }
-        return bfentries;
+        BFEntry[] array = new BFEntry[ttf.getCMaps().size()];
+        return ttf.getCMaps().toArray(array);
     }
 
     private void copyWidthsSingleByte(TTFFile ttf) {
@@ -184,9 +180,8 @@
         for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
             singleFont.setWidth(i, ttf.getCharWidth(i));
         }
-        Iterator iter = ttf.getCMaps().listIterator();
-        while (iter.hasNext()) {
-            TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+
+        for (BFEntry ce : ttf.getCMaps()) {
             if (ce.getUnicodeStart() < 0xFFFE) {
                 for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) {
                     int codePoint = singleFont.getEncoding().mapChar(u);
@@ -221,7 +216,6 @@
         }
 
         for (Integer kpx1 : kerningSet) {
-
             Map<Integer, Integer> h2;
             if (isCid) {
                 h2 = ttf.getKerning().get(kpx1);
Index: src/java/org/apache/fop/fonts/truetype/TTFFile.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFFile.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/truetype/TTFFile.java	(working copy)
@@ -20,14 +20,22 @@
 package org.apache.fop.fonts.truetype;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.FontUtil;
 import org.apache.xmlgraphics.fonts.Glyphs;
 
@@ -127,6 +135,8 @@
         "ccaron",           "dcroat"
     };
 
+    /** The FontFileReader used to read this truetype font */
+    protected FontFileReader fontFile;
     /** Set to true to get even more debug output than with level DEBUG */
     public static final boolean TRACE_ENABLED = false;
 
@@ -138,11 +148,11 @@
     /**
      * Table directory
      */
-    protected Map dirTabs;
-    private Map<Integer, Map<Integer, Integer>> kerningTab;     // for CIDs
+    protected Map<TTFTableName, TTFDirTabEntry> dirTabs;
+    private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
     private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
-    private List<TTFCmapEntry> cmaps;
-    private List unicodeMapping;
+    private List<BFEntry> cmaps;
+    private List<UnicodeMapping> unicodeMapping;
 
     private int upem;                                // unitsPerEm from "head" table
     private int nhmtx;                               // Number of horizontal metrics
@@ -153,18 +163,16 @@
      */
     protected long lastLoca = 0;
     private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
-    private int nmGlyphs;                            // Used in fixWidths - remove?
 
     /**
      * Contains glyph data
      */
     protected TTFMtxEntry[] mtxTab;                  // Contains glyph data
-    private final int[] mtxEncoded = null;
 
     private String postScriptName = "";
     private String fullName = "";
     private String notice = "";
-    private final Set familyNames = new java.util.HashSet(); //Set<String>
+    private Set<String> familyNames = new HashSet<String>();
     private String subFamilyName = "";
 
     private long italicAngle = 0;
@@ -193,12 +201,12 @@
     private short lastChar = 0;
 
     private int[] ansiWidth;
-    private Map ansiIndex;
+    private Map<Integer, List<Integer>> ansiIndex;
 
     // internal mapping of glyph indexes to unicode indexes
     // used for quick mappings in this class
-    private final Map glyphToUnicodeMap = new java.util.HashMap();
-    private final Map unicodeToGlyphMap = new java.util.HashMap();
+    private Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer> ();
+    private Map<Integer, Integer>  unicodeToGlyphMap = new HashMap<Integer, Integer> ();
 
     private TTFDirTabEntry currentDirTab;
 
@@ -210,9 +218,9 @@
     protected Log log = LogFactory.getLog(TTFFile.class);
 
     /**
-     * Key-value helper class
+     * Key-value helper class (immutable)
      */
-    class UnicodeMapping {
+    final class UnicodeMapping {
 
         private final int unicodeIndex;
         private final int glyphIndex;
@@ -244,33 +252,26 @@
     /**
      * Version of the PostScript table (<q>post</q>) contained in this font.
      */
-    public static final class PostScriptVersion {
-
+    public static enum PostScriptVersion {
         /** PostScript table version 1.0. */
-        public static final PostScriptVersion V1 = new PostScriptVersion();
-
+        V1,
         /** PostScript table version 2.0. */
-        public static final PostScriptVersion V2 = new PostScriptVersion();
-
+        V2,
         /** PostScript table version 3.0. */
-        public static final PostScriptVersion V3 = new PostScriptVersion();
-
+        V3,
         /** Unknown version of the PostScript table. */
-        public static final PostScriptVersion UNKNOWN = new PostScriptVersion();
-
-        private PostScriptVersion() { }
-
+        UNKNOWN;
     }
 
     /**
      * Position inputstream to position indicated
      * in the dirtab offset + offset
      */
-    boolean seekTab(FontFileReader in, String name,
+    boolean seekTab(FontFileReader in, TTFTableName tableName,
                   long offset) throws IOException {
-        TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
+        TTFDirTabEntry dt = dirTabs.get(tableName);
         if (dt == null) {
-            log.error("Dirtab " + name + " not found.");
+            log.error("Dirtab " + tableName.getName() + " not found.");
             return false;
         } else {
             in.seekSet(dt.getOffset() + offset);
@@ -306,12 +307,12 @@
      * Set the unicodeIndex in the TTFMtxEntries and fills in the
      * cmaps vector.
      */
-    private boolean readCMAP(FontFileReader in) throws IOException {
+    private boolean readCMAP() throws IOException {
 
-        unicodeMapping = new java.util.ArrayList();
+        unicodeMapping = new ArrayList<UnicodeMapping>();
 
-        seekTab(in, "cmap", 2);
-        int numCMap = in.readTTFUShort();    // Number of cmap subtables
+        seekTab(fontFile, TTFTableName.CMAP, 2);
+        int numCMap = fontFile.readTTFUShort();    // Number of cmap subtables
         long cmapUniOffset = 0;
         long symbolMapOffset = 0;
 
@@ -321,9 +322,9 @@
 
         //Read offset for all tables. We are only interested in the unicode table
         for (int i = 0; i < numCMap; i++) {
-            int cmapPID = in.readTTFUShort();
-            int cmapEID = in.readTTFUShort();
-            long cmapOffset = in.readTTFULong();
+            int cmapPID = fontFile.readTTFUShort();
+            int cmapEID = fontFile.readTTFUShort();
+            long cmapOffset = fontFile.readTTFLong();
 
             if (log.isDebugEnabled()) {
                 log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
@@ -338,9 +339,9 @@
         }
 
         if (cmapUniOffset > 0) {
-            return readUnicodeCmap(in, cmapUniOffset, 1);
+            return readUnicodeCmap(cmapUniOffset, 1);
         } else if (symbolMapOffset > 0) {
-            return readUnicodeCmap(in, symbolMapOffset, 0);
+            return readUnicodeCmap(symbolMapOffset, 0);
         } else {
             log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
                     + " not present. Aborting");
@@ -349,26 +350,26 @@
     }
 
     private boolean readUnicodeCmap                             // CSOK: MethodLength
-        (FontFileReader in, long cmapUniOffset, int encodingID)
+        (long cmapUniOffset, int encodingID)
             throws IOException {
         //Read CMAP table and correct mtxTab.index
         int mtxPtr = 0;
 
         // Read unicode cmap
-        seekTab(in, "cmap", cmapUniOffset);
-        int cmapFormat = in.readTTFUShort();
-        /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
+        seekTab(fontFile, TTFTableName.CMAP, cmapUniOffset);
+        int cmapFormat = fontFile.readTTFUShort();
+        /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length
 
         if (log.isDebugEnabled()) {
             log.debug("CMAP format: " + cmapFormat);
         }
 
         if (cmapFormat == 4) {
-            in.skip(2);    // Skip version number
-            int cmapSegCountX2 = in.readTTFUShort();
-            int cmapSearchRange = in.readTTFUShort();
-            int cmapEntrySelector = in.readTTFUShort();
-            int cmapRangeShift = in.readTTFUShort();
+            fontFile.skip(2);    // Skip version number
+            int cmapSegCountX2 = fontFile.readTTFUShort();
+            int cmapSearchRange = fontFile.readTTFUShort();
+            int cmapEntrySelector = fontFile.readTTFUShort();
+            int cmapRangeShift = fontFile.readTTFUShort();
 
             if (log.isDebugEnabled()) {
                 log.debug("segCountX2   : " + cmapSegCountX2);
@@ -384,26 +385,26 @@
             int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapEndCounts[i] = in.readTTFUShort();
+                cmapEndCounts[i] = fontFile.readTTFUShort();
             }
 
-            in.skip(2);    // Skip reservedPad
+            fontFile.skip(2);    // Skip reservedPad
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapStartCounts[i] = in.readTTFUShort();
+                cmapStartCounts[i] = fontFile.readTTFUShort();
             }
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapDeltas[i] = in.readTTFShort();
+                cmapDeltas[i] = fontFile.readTTFShort();
             }
 
             //int startRangeOffset = in.getCurrentPos();
 
             for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapRangeOffsets[i] = in.readTTFUShort();
+                cmapRangeOffsets[i] = fontFile.readTTFUShort();
             }
 
-            int glyphIdArrayOffset = in.getCurrentPos();
+            int glyphIdArrayOffset = fontFile.getCurrentPos();
 
             BitSet eightBitGlyphs = new BitSet(256);
 
@@ -445,8 +446,8 @@
                                     + (j - cmapStartCounts[i])
                                     + (i)
                                     - cmapSegCountX2 / 2) * 2;
-                            in.seekSet(glyphOffset);
-                            glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+                            fontFile.seekSet(glyphOffset);
+                            glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
                                        & 0xffff;
 
                             unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
@@ -465,11 +466,9 @@
                             }
 
                             // Also add winAnsiWidth
-                            List v = (List)ansiIndex.get(new Integer(j));
+                            List<Integer> v = ansiIndex.get(new Integer(j));
                             if (v != null) {
-                                Iterator e = v.listIterator();
-                                while (e.hasNext()) {
-                                    Integer aIdx = (Integer)e.next();
+                                for (Integer aIdx : v) {
                                     ansiWidth[aIdx.intValue()]
                                         = mtxTab[glyphIdx].getWx();
 
@@ -510,11 +509,9 @@
                             }
 
                             // Also add winAnsiWidth
-                            List v = (List)ansiIndex.get(new Integer(j));
+                            List<Integer> v = ansiIndex.get(new Integer(j));
                             if (v != null) {
-                                Iterator e = v.listIterator();
-                                while (e.hasNext()) {
-                                    Integer aIdx = (Integer)e.next();
+                                for (Integer aIdx : v) {
                                     ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
                                 }
                             }
@@ -592,14 +589,14 @@
         // Create an index hash to the ansiWidth
         // Can't just index the winAnsiEncoding when inserting widths
         // same char (eg bullet) is repeated more than one place
-        ansiIndex = new java.util.HashMap();
+        ansiIndex = new HashMap<Integer, List<Integer>>();
         for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
             Integer ansi = new Integer(i);
             Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]);
 
-            List v = (List)ansiIndex.get(uni);
+            List<Integer> v = ansiIndex.get(uni);
             if (v == null) {
-                v = new java.util.ArrayList();
+                v = new ArrayList<Integer>();
                 ansiIndex.put(uni, v);
             }
             v.add(ansi);
@@ -618,12 +615,12 @@
      * @throws IOException In case of an I/O problem
      */
     public boolean readFont(FontFileReader in, String name) throws IOException {
-
+        fontFile = in;
         /*
          * Check if TrueType collection, and that the name
          * exists in the collection
          */
-        if (!checkTTC(in, name)) {
+        if (!checkTTC(name)) {
             if (name == null) {
                 throw new IllegalArgumentException(
                     "For TrueType collection you must specify which font "
@@ -634,26 +631,26 @@
             }
         }
 
-        readDirTabs(in);
-        readFontHeader(in);
-        getNumGlyphs(in);
+        readDirTabs();
+        readFontHeader();
+        getNumGlyphs();
         if (log.isDebugEnabled()) {
             log.debug("Number of glyphs in font: " + numberOfGlyphs);
         }
-        readHorizontalHeader(in);
-        readHorizontalMetrics(in);
+        readHorizontalHeader();
+        readHorizontalMetrics();
         initAnsiWidths();
-        readPostScript(in);
-        readOS2(in);
+        readPostScript();
+        readOS2();
         determineAscDesc();
         if (!isCFF) {
-            readIndexToLocation(in);
-            readGlyf(in);
+            readIndexToLocation();
+            readGlyf();
         }
-        readName(in);
-        boolean pcltFound = readPCLT(in);
+        readName();
+        boolean pcltFound = readPCLT();
         // Read cmap table and fill in ansiwidths
-        boolean valid = readCMAP(in);
+        boolean valid = readCMAP();
         if (!valid) {
             return false;
         }
@@ -661,38 +658,52 @@
         createCMaps();
         // print_max_min();
 
-        readKerning(in);
+        readKerning();
         guessVerticalMetricsFromGlyphBBox();
         return true;
     }
 
+    /**
+     * Reads a font.
+     *
+     * @param in FontFileReader to read from
+     * @param name Name to be checked for in the font file
+     * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+     * new index as (Integer) value)
+     * @throws IOException in case of an I/O problem
+     */
+    public void readFont(FontFileReader in, String name,
+            Map<Integer, Integer> glyphs) throws IOException {
+        readFont(in, name);
+    }
+
     private void createCMaps() {
-        this.cmaps = new java.util.ArrayList<TTFCmapEntry>();
-        TTFCmapEntry tce = new TTFCmapEntry();
+        cmaps = new ArrayList<BFEntry>();
+        int unicodeStart;
+        int glyphStart;
+        int unicodeEnd;
 
-        Iterator e = unicodeMapping.listIterator();
-        UnicodeMapping um = (UnicodeMapping)e.next();
+        Iterator<UnicodeMapping> e = unicodeMapping.listIterator();
+        UnicodeMapping um = e.next();
         UnicodeMapping lastMapping = um;
 
-        tce.setUnicodeStart(um.getUnicodeIndex());
-        tce.setGlyphStartIndex(um.getGlyphIndex());
+        unicodeStart = um.getUnicodeIndex();
+        glyphStart = um.getGlyphIndex();
 
         while (e.hasNext()) {
-            um = (UnicodeMapping)e.next();
+            um = e.next();
             if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
                     || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
-                tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
-                cmaps.add(tce);
-
-                tce = new TTFCmapEntry();
-                tce.setUnicodeStart(um.getUnicodeIndex());
-                tce.setGlyphStartIndex(um.getGlyphIndex());
+                unicodeEnd = lastMapping.getUnicodeIndex();
+                cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
+                unicodeStart = um.getUnicodeIndex();
+                glyphStart = um.getGlyphIndex();
             }
             lastMapping = um;
         }
 
-        tce.setUnicodeEnd(um.getUnicodeIndex());
-        cmaps.add(tce);
+        unicodeEnd = lastMapping.getUnicodeIndex();
+        cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
     }
 
     /**
@@ -715,7 +726,7 @@
      * Returns the font family names of the font.
      * @return Set The family names (a Set of Strings)
      */
-    public Set getFamilyNames() {
+    public Set<String> getFamilyNames() {
         return familyNames;
     }
 
@@ -760,19 +771,30 @@
     }
 
     /**
+     * Returns the number of bytes necessary to pad the currentPosition so that a table begins
+     * on a 4-byte boundary.
+     * @param currentPosition the position to pad.
+     * @return int the number of bytes to pad.
+     */
+    protected int getPadSize(int currentPosition) {
+        int padSize = 4 - (currentPosition % 4);
+        return padSize < 4 ? padSize : 0;
+    }
+
+    /**
      * Returns the Flags attribute of the font.
      * @return int The Flags
      */
     public int getFlags() {
         int flags = 32;    // Use Adobe Standard charset
         if (italicAngle != 0) {
-            flags = flags | 64;
+            flags |=  64;
         }
         if (isFixedPitch != 0) {
-            flags = flags | 2;
+            flags |=  2;
         }
         if (hasSerifs) {
-            flags = flags | 1;
+            flags |= 1;
         }
         return flags;
     }
@@ -810,7 +832,6 @@
     }
 
     /**
-     * Returns the font bounding box.
      * @return int[] The font bbox
      */
     public int[] getFontBBox() {
@@ -916,11 +937,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");
@@ -939,42 +959,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.DIRECTORY_TABLE,
+                new TTFDirTabEntry(0L, fontFile.getCurrentPos()));
         log.debug("dir tables: " + dirTabs.keySet());
     }
 
     /**
      * Read the "head" table, this reads the bounding box and
      * sets the upem (unitsPerEM) variable
-     * @param in FontFileReader to read the header from
      * @throws IOException in case of an I/O problem
      */
-    protected void readFontHeader(FontFileReader in) throws IOException {
-        seekTab(in, "head", 2 * 4 + 2 * 4);
-        int flags = in.readTTFUShort();
+    protected void readFontHeader() throws IOException {
+        seekTab(fontFile, TTFTableName.HEAD, 2 * 4 + 2 * 4);
+        int flags = fontFile.readTTFUShort();
         if (log.isDebugEnabled()) {
             log.debug("flags: " + flags + " - " + Integer.toString(flags, 2));
         }
-        upem = in.readTTFUShort();
+        upem = fontFile.readTTFUShort();
         if (log.isDebugEnabled()) {
             log.debug("unit per em: " + upem);
         }
 
-        in.skip(16);
+        fontFile.skip(16);
 
-        fontBBox1 = in.readTTFShort();
-        fontBBox2 = in.readTTFShort();
-        fontBBox3 = in.readTTFShort();
-        fontBBox4 = in.readTTFShort();
+        fontBBox1 = fontFile.readTTFShort();
+        fontBBox2 = fontFile.readTTFShort();
+        fontBBox3 = fontFile.readTTFShort();
+        fontBBox4 = fontFile.readTTFShort();
         if (log.isDebugEnabled()) {
             log.debug("font bbox: xMin=" + fontBBox1
                     + " yMin=" + fontBBox2
@@ -982,19 +1005,18 @@
                     + " yMax=" + fontBBox4);
         }
 
-        in.skip(2 + 2 + 2);
+        fontFile.skip(2 + 2 + 2);
 
-        locaFormat = in.readTTFShort();
+        locaFormat = fontFile.readTTFShort();
     }
 
     /**
      * Read the number of glyphs from the "maxp" table
-     * @param in FontFileReader to read the number of glyphs from
      * @throws IOException in case of an I/O problem
      */
-    protected void getNumGlyphs(FontFileReader in) throws IOException {
-        seekTab(in, "maxp", 4);
-        numberOfGlyphs = in.readTTFUShort();
+    protected void getNumGlyphs() throws IOException {
+        seekTab(fontFile, TTFTableName.MAXP, 4);
+        numberOfGlyphs = fontFile.readTTFUShort();
     }
 
 
@@ -1002,17 +1024,16 @@
      * Read the "hhea" table to find the ascender and descender and
      * size of "hmtx" table, as a fixed size font might have only
      * one width.
-     * @param in FontFileReader to read the hhea table from
      * @throws IOException in case of an I/O problem
      */
-    protected void readHorizontalHeader(FontFileReader in)
+    protected void readHorizontalHeader()
             throws IOException {
-        seekTab(in, "hhea", 4);
-        hheaAscender = in.readTTFShort();
-        hheaDescender = in.readTTFShort();
+        seekTab(fontFile, TTFTableName.HHEA, 4);
+        hheaAscender = fontFile.readTTFShort();
+        hheaDescender = fontFile.readTTFShort();
 
-        in.skip(2 + 2 + 3 * 2 + 8 * 2);
-        nhmtx = in.readTTFUShort();
+        fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
+        nhmtx = fontFile.readTTFUShort();
 
         if (log.isDebugEnabled()) {
             log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
@@ -1026,12 +1047,11 @@
      * in the mtxTab array. If the number of metrics is less
      * than the number of glyphs (eg fixed size fonts), extend
      * the mtxTab array and fill in the missing widths
-     * @param in FontFileReader to read the hmtx table from
      * @throws IOException in case of an I/O problem
      */
-    protected void readHorizontalMetrics(FontFileReader in)
+    protected void readHorizontalMetrics()
             throws IOException {
-        seekTab(in, "hmtx", 0);
+        seekTab(fontFile, TTFTableName.HMTX, 0);
 
         int mtxSize = Math.max(numberOfGlyphs, nhmtx);
         mtxTab = new TTFMtxEntry[mtxSize];
@@ -1043,8 +1063,8 @@
             mtxTab[i] = new TTFMtxEntry();
         }
         for (int i = 0; i < nhmtx; i++) {
-            mtxTab[i].setWx(in.readTTFUShort());
-            mtxTab[i].setLsb(in.readTTFUShort());
+            mtxTab[i].setWx(fontFile.readTTFUShort());
+            mtxTab[i].setLsb(fontFile.readTTFUShort());
 
             if (TRACE_ENABLED) {
                 if (log.isDebugEnabled()) {
@@ -1059,7 +1079,7 @@
             int lastWidth = mtxTab[nhmtx - 1].getWx();
             for (int i = nhmtx; i < mtxSize; i++) {
                 mtxTab[i].setWx(lastWidth);
-                mtxTab[i].setLsb(in.readTTFUShort());
+                mtxTab[i].setLsb(fontFile.readTTFUShort());
             }
         }
     }
@@ -1069,16 +1089,16 @@
      * Read the "post" table
      * containing the PostScript names of the glyphs.
      */
-    private void readPostScript(FontFileReader in) throws IOException {
-        seekTab(in, "post", 0);
-        int postFormat = in.readTTFLong();
-        italicAngle = in.readTTFULong();
-        underlinePosition = in.readTTFShort();
-        underlineThickness = in.readTTFShort();
-        isFixedPitch = in.readTTFULong();
+    private void readPostScript() throws IOException {
+        seekTab(fontFile, TTFTableName.POST, 0);
+        int postFormat = fontFile.readTTFLong();
+        italicAngle = fontFile.readTTFULong();
+        underlinePosition = fontFile.readTTFShort();
+        underlineThickness = fontFile.readTTFShort();
+        isFixedPitch = fontFile.readTTFULong();
 
         //Skip memory usage values
-        in.skip(4 * 4);
+        fontFile.skip(4 * 4);
 
         log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
         switch (postFormat) {
@@ -1095,11 +1115,11 @@
             int numGlyphStrings = 0;
 
             // Read Number of Glyphs
-            int l = in.readTTFUShort();
+            int l = fontFile.readTTFUShort();
 
             // Read indexes
             for (int i = 0; i < l; i++) {
-                mtxTab[i].setIndex(in.readTTFUShort());
+                mtxTab[i].setIndex(fontFile.readTTFUShort());
 
                 if (mtxTab[i].getIndex() > 257) {
                     //Index is not in the Macintosh standard set
@@ -1119,7 +1139,7 @@
                         + " set. Total number of glyphs=" + l);
             }
             for (int i = 0; i < psGlyphsBuffer.length; i++) {
-                psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
+                psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
             }
 
             //Set glyph names
@@ -1156,60 +1176,60 @@
     /**
      * Read the "OS/2" table
      */
-    private void readOS2(FontFileReader in) throws IOException {
+    private void readOS2() throws IOException {
         // Check if font is embeddable
-        TTFDirTabEntry os2Entry = (TTFDirTabEntry)dirTabs.get("OS/2");
+        TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2);
         if (os2Entry != null) {
-            seekTab(in, "OS/2", 0);
-            int version = in.readTTFUShort();
+            seekTab(fontFile, TTFTableName.OS2, 0);
+            int version = fontFile.readTTFUShort();
             if (log.isDebugEnabled()) {
                 log.debug("OS/2 table: version=" + version
                         + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
             }
-            in.skip(2); //xAvgCharWidth
-            this.usWeightClass = in.readTTFUShort();
+            fontFile.skip(2); //xAvgCharWidth
+            this.usWeightClass = fontFile.readTTFUShort();
 
             // usWidthClass
-            in.skip(2);
+            fontFile.skip(2);
 
-            int fsType = in.readTTFUShort();
+            int fsType = fontFile.readTTFUShort();
             if (fsType == 2) {
                 isEmbeddable = false;
             } else {
                 isEmbeddable = true;
             }
-            in.skip(11 * 2);
-            in.skip(10); //panose array
-            in.skip(4 * 4); //unicode ranges
-            in.skip(4);
-            in.skip(3 * 2);
+            fontFile.skip(11 * 2);
+            fontFile.skip(10); //panose array
+            fontFile.skip(4 * 4); //unicode ranges
+            fontFile.skip(4);
+            fontFile.skip(3 * 2);
             int v;
-            os2Ascender = in.readTTFShort(); //sTypoAscender
-            os2Descender = in.readTTFShort(); //sTypoDescender
+            os2Ascender = fontFile.readTTFShort(); //sTypoAscender
+            os2Descender = fontFile.readTTFShort(); //sTypoDescender
             if (log.isDebugEnabled()) {
                 log.debug("sTypoAscender: " + os2Ascender
                         + " -> internal " + convertTTFUnit2PDFUnit(os2Ascender));
                 log.debug("sTypoDescender: " + os2Descender
                         + " -> internal " + convertTTFUnit2PDFUnit(os2Descender));
             }
-            v = in.readTTFShort(); //sTypoLineGap
+            v = fontFile.readTTFShort(); //sTypoLineGap
             if (log.isDebugEnabled()) {
                 log.debug("sTypoLineGap: " + v);
             }
-            v = in.readTTFUShort(); //usWinAscent
+            v = fontFile.readTTFUShort(); //usWinAscent
             if (log.isDebugEnabled()) {
                 log.debug("usWinAscent: " + formatUnitsForDebug(v));
             }
-            v = in.readTTFUShort(); //usWinDescent
+            v = fontFile.readTTFUShort(); //usWinDescent
             if (log.isDebugEnabled()) {
                 log.debug("usWinDescent: " + formatUnitsForDebug(v));
             }
 
             //version 1 OS/2 table might end here
             if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) {
-                in.skip(2 * 4);
-                this.os2xHeight = in.readTTFShort(); //sxHeight
-                this.os2CapHeight = in.readTTFShort(); //sCapHeight
+                fontFile.skip(2 * 4);
+                this.os2xHeight = fontFile.readTTFShort(); //sxHeight
+                this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight
                 if (log.isDebugEnabled()) {
                     log.debug("sxHeight: " + this.os2xHeight);
                     log.debug("sCapHeight: " + this.os2CapHeight);
@@ -1223,42 +1243,40 @@
 
     /**
      * Read the "loca" table.
-     * @param in FontFileReader to read from
      * @throws IOException In case of a I/O problem
      */
-    protected final void readIndexToLocation(FontFileReader in)
+    protected final void readIndexToLocation()
             throws IOException {
-        if (!seekTab(in, "loca", 0)) {
+        if (!seekTab(fontFile, TTFTableName.LOCA, 0)) {
             throw new IOException("'loca' table not found, happens when the font file doesn't"
                     + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
         }
         for (int i = 0; i < numberOfGlyphs; i++) {
-            mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
-                                 : (in.readTTFUShort() << 1));
+            mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong()
+                                 : (fontFile.readTTFUShort() << 1));
         }
-        lastLoca = (locaFormat == 1 ? in.readTTFULong()
-                    : (in.readTTFUShort() << 1));
+        lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
+                    : (fontFile.readTTFUShort() << 1));
     }
 
     /**
      * Read the "glyf" table to find the bounding boxes.
-     * @param in FontFileReader to read from
      * @throws IOException In case of a I/O problem
      */
-    private void readGlyf(FontFileReader in) throws IOException {
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
+    private void readGlyf() throws IOException {
+        TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.GLYF);
         if (dirTab == null) {
             throw new IOException("glyf table not found, cannot continue");
         }
         for (int i = 0; i < (numberOfGlyphs - 1); i++) {
             if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
-                in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
-                in.skip(2);
+                fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+                fontFile.skip(2);
                 final int[] bbox = {
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort()};
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort()};
                 mtxTab[i].setBoundingBox(bbox);
             } else {
                 mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
@@ -1266,17 +1284,17 @@
         }
 
 
-        long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
+        long n = (dirTabs.get(TTFTableName.GLYF)).getOffset();
         for (int i = 0; i < numberOfGlyphs; i++) {
             if ((i + 1) >= mtxTab.length
                     || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
-                in.seekSet(n + mtxTab[i].getOffset());
-                in.skip(2);
+                fontFile.seekSet(n + mtxTab[i].getOffset());
+                fontFile.skip(2);
                 final int[] bbox = {
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort()};
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort(),
+                    fontFile.readTTFShort()};
                 mtxTab[i].setBoundingBox(bbox);
             } else {
                 /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
@@ -1297,34 +1315,33 @@
 
     /**
      * Read the "name" table.
-     * @param in FontFileReader to read from
      * @throws IOException In case of a I/O problem
      */
-    private void readName(FontFileReader in) throws IOException {
-        seekTab(in, "name", 2);
-        int i = in.getCurrentPos();
-        int n = in.readTTFUShort();
-        int j = in.readTTFUShort() + i - 2;
+    private void readName() throws IOException {
+        seekTab(fontFile, TTFTableName.NAME, 2);
+        int i = fontFile.getCurrentPos();
+        int n = fontFile.readTTFUShort();
+        int j = fontFile.readTTFUShort() + i - 2;
         i += 2 * 2;
 
         while (n-- > 0) {
             // getLogger().debug("Iteration: " + n);
-            in.seekSet(i);
-            final int platformID = in.readTTFUShort();
-            final int encodingID = in.readTTFUShort();
-            final int languageID = in.readTTFUShort();
+            fontFile.seekSet(i);
+            final int platformID = fontFile.readTTFUShort();
+            final int encodingID = fontFile.readTTFUShort();
+            final int languageID = fontFile.readTTFUShort();
 
-            int k = in.readTTFUShort();
-            int l = in.readTTFUShort();
+            int k = fontFile.readTTFUShort();
+            int l = fontFile.readTTFUShort();
 
             if (((platformID == 1 || platformID == 3)
                     && (encodingID == 0 || encodingID == 1))) {
-                in.seekSet(j + in.readTTFUShort());
+                fontFile.seekSet(j + fontFile.readTTFUShort());
                 String txt;
                 if (platformID == 3) {
-                    txt = in.readTTFString(l, encodingID);
+                    txt = fontFile.readTTFString(l, encodingID);
                 } else {
-                    txt = in.readTTFString(l);
+                    txt = fontFile.readTTFString(l);
                 }
 
                 if (log.isDebugEnabled()) {
@@ -1368,21 +1385,20 @@
 
     /**
      * Read the "PCLT" table to find xHeight and capHeight.
-     * @param in FontFileReader to read from
      * @throws IOException In case of a I/O problem
      */
-    private boolean readPCLT(FontFileReader in) throws IOException {
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
+    private boolean readPCLT() throws IOException {
+        TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT);
         if (dirTab != null) {
-            in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
-            xHeight = in.readTTFUShort();
+            fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+            xHeight = fontFile.readTTFUShort();
             log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
-            in.skip(2 * 2);
-            capHeight = in.readTTFUShort();
+            fontFile.skip(2 * 2);
+            capHeight = fontFile.readTTFUShort();
             log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
-            in.skip(2 + 16 + 8 + 6 + 1 + 1);
+            fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
 
-            int serifStyle = in.readTTFUByte();
+            int serifStyle = fontFile.readTTFUByte();
             serifStyle = serifStyle >> 6;
             serifStyle = serifStyle & 3;
             if (serifStyle == 1) {
@@ -1512,19 +1528,18 @@
     /**
      * Read the kerning table, create a table for both CIDs and
      * winAnsiEncoding.
-     * @param in FontFileReader to read from
      * @throws IOException In case of a I/O problem
      */
-    private void readKerning(FontFileReader in) throws IOException {
+    private void readKerning() throws IOException {
         // Read kerning
-        kerningTab = new java.util.HashMap();
-        ansiKerningTab = new java.util.HashMap();
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
+        kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+        ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+        TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN);
         if (dirTab != null) {
-            seekTab(in, "kern", 2);
-            for (int n = in.readTTFUShort(); n > 0; n--) {
-                in.skip(2 * 2);
-                int k = in.readTTFUShort();
+            seekTab(fontFile, TTFTableName.KERN, 2);
+            for (int n = fontFile.readTTFUShort(); n > 0; n--) {
+                fontFile.skip(2 * 2);
+                int k = fontFile.readTTFUShort();
                 if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
                     return;
                 }
@@ -1532,12 +1547,12 @@
                     continue;
                 }
 
-                k = in.readTTFUShort();
-                in.skip(3 * 2);
+                k = fontFile.readTTFUShort();
+                fontFile.skip(3 * 2);
                 while (k-- > 0) {
-                    int i = in.readTTFUShort();
-                    int j = in.readTTFUShort();
-                    int kpx = in.readTTFShort();
+                    int i = fontFile.readTTFUShort();
+                    int j = fontFile.readTTFUShort();
+                    int kpx = fontFile.readTTFShort();
                     if (kpx != 0) {
                         // CID kerning table entry, using unicode indexes
                         final Integer iObj = glyphToUnicode(i);
@@ -1551,9 +1566,9 @@
                             log.debug("Ignoring kerning pair because Unicode index was"
                                     + " found for the second glyph " + i);
                         } else {
-                            Map adjTab = kerningTab.get(iObj);
+                            Map<Integer, Integer> adjTab = kerningTab.get(iObj);
                             if (adjTab == null) {
-                                adjTab = new java.util.HashMap();
+                                adjTab = new HashMap<Integer, Integer>();
                             }
                             adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
                             kerningTab.put(iObj, adjTab);
@@ -1565,16 +1580,12 @@
             // Create winAnsiEncoded kerning table from kerningTab
             // (could probably be simplified, for now we remap back to CID indexes and
             // then to winAnsi)
-            Iterator ae = kerningTab.keySet().iterator();
-            while (ae.hasNext()) {
-                Integer unicodeKey1 = (Integer)ae.next();
+            for (Integer unicodeKey1 : kerningTab.keySet()) {
                 Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
-                Map<Integer, Integer> akpx = new java.util.HashMap();
-                Map ckpx = kerningTab.get(unicodeKey1);
+                Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
+                Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
 
-                Iterator aee = ckpx.keySet().iterator();
-                while (aee.hasNext()) {
-                    Integer unicodeKey2 = (Integer)aee.next();
+                for (Integer unicodeKey2 : ckpx.keySet()) {
                     Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
                     Integer kern = (Integer)ckpx.get(unicodeKey2);
 
@@ -1603,10 +1614,70 @@
     }
 
     /**
+     * Streams a font.
+     * @param ttfOut The interface for streaming True Type tables.
+     * @exception IOException file write error
+     */
+    public void stream(TTFOutputStream ttfOut) throws IOException {
+        SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs = sortDirTabMap(dirTabs);
+        byte[] file = fontFile.getAllBytes();
+        TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+        TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+        ttfOut.startFontStream();
+        for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
+            int offset = (int) entry.getValue().getOffset();
+            int paddedLength = (int) entry.getValue().getLength();
+            paddedLength += getPadSize(offset + paddedLength);
+            if (entry.getKey() == TTFTableName.GLYF) {
+                streamGlyf(glyphOut, file, offset, paddedLength);
+            } else {
+                tableOut.streamTable(file, offset, paddedLength);
+            }
+        }
+        ttfOut.endFontStream();
+    }
+
+    private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset,
+            int tableLength) throws IOException {
+        //Stream all but the last glyph
+        int glyphStart = 0;
+        int glyphEnd = 0;
+        glyphOut.startGlyphStream();
+        for (int i = 0; i < mtxTab.length - 1; i++) {
+            glyphStart = (int) mtxTab[i].getOffset() + tableOffset;
+            glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset;
+            glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart);
+        }
+        glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd);
+        glyphOut.endGlyphStream();
+    }
+
+    /**
+     * This returns the order in which the tables in a truetype font should be written to file.
+     * @param directoryTabs the map that is to be sorted.
+     * @return TTFTablesNames[] an array of table names sorted in the order they should appear in
+     * the TTF file.
+     */
+    SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>>
+                        sortDirTabMap(Map<TTFTableName, TTFDirTabEntry> directoryTabs) {
+        SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedSet
+            = new TreeSet<Map.Entry<TTFTableName, TTFDirTabEntry>>(
+                    new Comparator<Map.Entry<TTFTableName, TTFDirTabEntry>>() {
+
+            public int compare(Entry<TTFTableName, TTFDirTabEntry> o1,
+                    Entry<TTFTableName, TTFDirTabEntry> o2) {
+                return (int) (o1.getValue().getOffset() - o2.getValue().getOffset());
+            }
+        });
+        sortedSet.addAll(directoryTabs.entrySet());
+        return sortedSet;
+    }
+
+    /**
      * Return a List with TTFCmapEntry.
      * @return A list of TTFCmapEntry objects
      */
-    public List<TTFCmapEntry> getCMaps() {
+    public List<BFEntry> getCMaps() {
         return cmaps;
     }
 
@@ -1615,24 +1686,23 @@
      * name exists in the collection.
      * If it does, set offset in fontfile to the beginning of
      * the Table Directory for that font.
-     * @param in FontFileReader to read from
      * @param name The name to check
      * @return True if not collection or font name present, false otherwise
      * @throws IOException In case of an I/O problem
      */
-    protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
-        String tag = in.readTTFString(4);
+    protected final boolean checkTTC(String name) throws IOException {
+        String tag = fontFile.readTTFString(4);
 
         if ("ttcf".equals(tag)) {
             // This is a TrueType Collection
-            in.skip(4);
+            fontFile.skip(4);
 
             // Read directory offsets
-            int numDirectories = (int)in.readTTFULong();
+            int numDirectories = (int)fontFile.readTTFULong();
             // int numDirectories=in.readTTFUShort();
             long[] dirOffsets = new long[numDirectories];
             for (int i = 0; i < numDirectories; i++) {
-                dirOffsets[i] = in.readTTFULong();
+                dirOffsets[i] = fontFile.readTTFULong();
             }
 
             log.info("This is a TrueType collection file with "
@@ -1646,10 +1716,10 @@
             // Is found, just to show all the names
             long dirTabOffset = 0;
             for (int i = 0; (i < numDirectories); i++) {
-                in.seekSet(dirOffsets[i]);
-                readDirTabs(in);
+                fontFile.seekSet(dirOffsets[i]);
+                readDirTabs();
 
-                readName(in);
+                readName();
 
                 if (fullName.equals(name)) {
                     found = true;
@@ -1667,10 +1737,10 @@
                 subFamilyName = "";
             }
 
-            in.seekSet(dirTabOffset);
+            fontFile.seekSet(dirTabOffset);
             return found;
         } else {
-            in.seekSet(0);
+            fontFile.seekSet(0);
             return true;
         }
     }
@@ -1682,8 +1752,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)) {
@@ -1705,9 +1774,9 @@
 
             for (int i = 0; (i < numDirectories); i++) {
                 in.seekSet(dirOffsets[i]);
-                readDirTabs(in);
+                readDirTabs();
 
-                readName(in);
+                readName();
 
                 log.debug(fullName);
                 fontNames.add(fullName);
@@ -1733,13 +1802,13 @@
      * doesn't matter...
      */
     private Integer[] unicodeToWinAnsi(int unicode) {
-        List ret = new java.util.ArrayList();
+        List<Integer> ret = new ArrayList<Integer>();
         for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
             if (unicode == Glyphs.WINANSI_ENCODING[i]) {
                 ret.add(new Integer(i));
             }
         }
-        return (Integer[])ret.toArray(new Integer[0]);
+        return ret.toArray(new Integer[0]);
     }
 
     /**
@@ -1783,7 +1852,7 @@
      * @throws IOException if glyphIndex not found
      */
     private Integer glyphToUnicode(int glyphIndex) throws IOException {
-        return (Integer) glyphToUnicodeMap.get(new Integer(glyphIndex));
+        return glyphToUnicodeMap.get(new Integer(glyphIndex));
     }
 
     /**
@@ -1795,7 +1864,7 @@
      */
     private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
         final Integer result
-            = (Integer) unicodeToGlyphMap.get(new Integer(unicodeIndex));
+            = unicodeToGlyphMap.get(new Integer(unicodeIndex));
         if (result == null) {
             throw new IOException(
                     "Glyph index not found for unicode value " + unicodeIndex);
@@ -1830,4 +1899,4 @@
             ioe.printStackTrace(System.err);
         }
     }
-}
\ No newline at end of file
+}
Index: src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
===================================================================
--- src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java	(revision 0)
+++ src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java	(revision 0)
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+
+/**
+ * This is an interface for streaming individual glyphs from the glyf table in a True Type font.
+ */
+public interface TTFGlyphOutputStream {
+    /**
+     * Begins the streaming of glyphs.
+     * @throws IOException file write exception
+     */
+    void startGlyphStream() throws IOException;
+
+    /**
+     * Streams an individual glyph at offset from a byte array.
+     * @param byteArray byte[] the font byte array.
+     * @param offset int the starting position to stream from.
+     * @param length int the number of bytes to stream.
+     * @throws IOException file write exception.
+     */
+    void streamGlyph(byte[] byteArray, int offset, int length) throws IOException;
+
+    /**
+     * Ends the streaming of glyphs.
+     * @throws IOException file write exception.
+     */
+    void endGlyphStream() throws IOException;
+}
Index: src/java/org/apache/fop/fonts/MultiByteFont.java
===================================================================
--- src/java/org/apache/fop/fonts/MultiByteFont.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/MultiByteFont.java	(working copy)
@@ -20,7 +20,6 @@
 package org.apache.fop.fonts;
 
 //Java
-import java.text.DecimalFormat;
 import java.util.Map;
 
 
@@ -29,16 +28,12 @@
  */
 public class MultiByteFont extends CIDFont {
 
-    private static int uniqueCounter = -1;
-
     private String ttcName = null;
     private String encoding = "Identity-H";
 
     private int defaultWidth = 0;
     private CIDFontType cidType = CIDFontType.CIDTYPE2;
 
-    private String namePrefix = null;    // Quasi unique prefix
-
     private CIDSubset subset = new CIDSubset();
 
     /**
@@ -47,26 +42,6 @@
     public MultiByteFont() {
         // Make sure that the 3 first glyphs are included
         subset.setupFirstThreeGlyphs();
-
-        // Create a quasiunique prefix for fontname
-        synchronized (this.getClass()) {
-            uniqueCounter++;
-            if (uniqueCounter > 99999 || uniqueCounter < 0) {
-                uniqueCounter = 0; //We need maximum 5 character then we start again
-            }
-        }
-        DecimalFormat counterFormat = new DecimalFormat("00000");
-        String cntString = counterFormat.format(uniqueCounter);
-
-        //Subset prefix as described in chapter 5.5.3 of PDF 1.4
-        StringBuffer sb = new StringBuffer("E");
-        for (int i = 0, c = cntString.length(); i < c; i++) {
-            //translate numbers to uppercase characters
-            sb.append((char)(cntString.charAt(i) + (65 - 48)));
-        }
-        sb.append("+");
-        namePrefix = sb.toString();
-
         setFontType(FontType.TYPE0);
     }
 
@@ -108,18 +83,10 @@
         this.cidType = cidType;
     }
 
-    private String getPrefixedFontName() {
-        return namePrefix + FontUtil.stripWhiteSpace(super.getFontName());
-    }
-
     /** {@inheritDoc} */
     @Override
     public String getEmbedFontName() {
-        if (isEmbeddable()) {
-            return getPrefixedFontName();
-        } else {
-            return super.getFontName();
-        }
+        return FontUtil.stripWhiteSpace(super.getFontName());
     }
 
     /** {@inheritDoc} */
Index: src/java/org/apache/fop/fonts/FontInfoConfigurator.java
===================================================================
--- src/java/org/apache/fop/fonts/FontInfoConfigurator.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/FontInfoConfigurator.java	(working copy)
@@ -251,11 +251,15 @@
         }
 
         boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
-        EncodingMode encodingMode = EncodingMode.getEncodingMode(
+        EncodingMode encodingMode = EncodingMode.getValue(
                 fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()));
+        EmbeddingMode embeddingMode = EmbeddingMode.getValue(
+                fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString()));
         EmbedFontInfo embedFontInfo
                 = new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont);
         embedFontInfo.setEncodingMode(encodingMode);
+        embedFontInfo.setEmbeddingMode(embeddingMode);
+
         if (fontCache != null) {
             if (!fontCache.containsFont(embedFontInfo)) {
                 fontCache.addFont(embedFontInfo);
Index: src/java/org/apache/fop/fonts/LazyFont.java
===================================================================
--- src/java/org/apache/fop/fonts/LazyFont.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/LazyFont.java	(working copy)
@@ -27,12 +27,10 @@
 import javax.xml.transform.Source;
 import javax.xml.transform.stream.StreamSource;
 
-import org.xml.sax.InputSource;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
 import org.apache.fop.apps.FOPException;
+import org.xml.sax.InputSource;
 
 /**
  * This class is used to defer the loading of a font until it is really used.
@@ -45,6 +43,7 @@
     private String fontEmbedPath = null;
     private boolean useKerning = false;
     private EncodingMode encodingMode = EncodingMode.AUTO;
+    private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
     private boolean embedded = true;
     private String subFontName = null;
 
@@ -65,6 +64,7 @@
         this.fontEmbedPath = fontInfo.getEmbedFile();
         this.useKerning = fontInfo.getKerning();
         this.encodingMode = fontInfo.getEncodingMode();
+        this.embeddingMode = fontInfo.getEmbeddingMode();
         this.subFontName = fontInfo.getSubFontName();
         this.embedded = fontInfo.isEmbedded();
         this.resolver = resolver;
@@ -131,8 +131,9 @@
                     if (fontEmbedPath == null) {
                         throw new RuntimeException("Cannot load font. No font URIs available.");
                     }
-                    realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName,
-                            this.embedded, this.encodingMode, useKerning, resolver);
+                    realFont = FontLoader.loadFont(fontEmbedPath, subFontName,
+                            embedded, embeddingMode, encodingMode,
+                            useKerning, resolver);
                 }
                 if (realFont instanceof FontDescriptor) {
                     realFontDescriptor = (FontDescriptor) realFont;
Index: src/java/org/apache/fop/fonts/BFEntry.java
===================================================================
--- src/java/org/apache/fop/fonts/BFEntry.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/BFEntry.java	(working copy)
@@ -22,14 +22,13 @@
 /**
  * This is just a holder class for bfentries, groups of characters of a base font (bf).
  */
-public class BFEntry {
+public final class BFEntry {
 
     //TODO Think about renaming this class to CMapRange or something.
-    //TODO Copy equals() and hashCode() from TTFCmapEntry
 
-    private int unicodeStart;
-    private int unicodeEnd;
-    private int glyphStartIndex;
+    private final int unicodeStart;
+    private final int unicodeEnd;
+    private final int glyphStartIndex;
 
     /**
      * Main constructor.
@@ -44,6 +43,32 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        int hc = 17;
+        hc = 31 * hc + unicodeStart;
+        hc = 31 * hc + unicodeEnd;
+        hc = 31 * hc + glyphStartIndex;
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BFEntry) {
+            BFEntry ce = (BFEntry) o;
+            return ce.unicodeStart == this.unicodeStart
+                && ce.unicodeEnd == this.unicodeEnd
+                && ce.glyphStartIndex == this.glyphStartIndex;
+        }
+        return false;
+    }
+
+    /**
      * Returns the unicodeStart.
      * @return the Unicode start index
      */
Index: src/java/org/apache/fop/fonts/CustomFontCollection.java
===================================================================
--- src/java/org/apache/fop/fonts/CustomFontCollection.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/CustomFontCollection.java	(working copy)
@@ -71,7 +71,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);
             }
         }
Index: src/java/org/apache/fop/fonts/EmbedFontInfo.java
===================================================================
--- src/java/org/apache/fop/fonts/EmbedFontInfo.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/EmbedFontInfo.java	(working copy)
@@ -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 {
 
@@ -39,6 +41,8 @@
     protected boolean kerning;
     /** the requested encoding mode for the font */
     protected EncodingMode encodingMode = EncodingMode.AUTO;
+    /** the requested embedding mode for this font */
+    protected EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
 
     /** the PostScript name of the font */
     protected String postScriptName = null;
@@ -137,6 +141,14 @@
     }
 
     /**
+     * Returns the embedding mode for this font.
+     * @return the embedding mode.
+     */
+    public EmbeddingMode getEmbeddingMode() {
+       return embeddingMode;
+    }
+
+    /**
      * Defines whether the font is embedded or not.
      * @param value true to embed the font, false to reference it
      */
@@ -163,6 +175,17 @@
         this.encodingMode = mode;
     }
 
+    /**
+     * Sets the embedding mode for this font, currently not supported for type1 fonts.
+     * @param embeddingMode the new embedding mode.
+     */
+    public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+        if (embeddingMode == null) {
+            throw new NullPointerException("embeddingMode must not be null");
+        }
+        this.embeddingMode = embeddingMode;
+    }
+
     private void readObject(java.io.ObjectInputStream in)
                 throws IOException, ClassNotFoundException {
         in.defaultReadObject();
Index: src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
===================================================================
--- src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java	(working copy)
@@ -31,6 +31,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.apache.fop.fonts.CustomFont;
 import org.apache.fop.fonts.EmbedFontInfo;
+import org.apache.fop.fonts.EmbeddingMode;
 import org.apache.fop.fonts.EncodingMode;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontCache;
@@ -218,7 +219,8 @@
                 }
                 try {
                     TTFFontLoader ttfLoader = new TTFFontLoader(
-                            fontFileURL, fontName, true, EncodingMode.AUTO, true, resolver);
+                            fontFileURL, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO,
+                            true, resolver);
                     customFont = ttfLoader.getFont();
                     if (this.eventListener != null) {
                         customFont.setEventListener(this.eventListener);
@@ -242,7 +244,8 @@
         } 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);
                 }
Index: src/java/org/apache/fop/fonts/EmbeddingMode.java
===================================================================
--- src/java/org/apache/fop/fonts/EmbeddingMode.java	(revision 0)
+++ src/java/org/apache/fop/fonts/EmbeddingMode.java	(revision 0)
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts;
+
+/**
+ * This enumerates the embedding mode of fonts; full; subset; auto (auto defaults to full for
+ * type1 fonts and subset for truetype fonts.
+ */
+public enum EmbeddingMode {
+    /** Default option: assumes FULL for type1 fonts and SUBSET for truetype fonts. */
+    AUTO,
+    /** Full font embedding: This means the whole of the font is written to file. */
+    FULL,
+    /** Subset font embedding: Only the mandatory tables and a subset of glyphs are written
+     * to file.*/
+    SUBSET;
+
+    /**
+     * Returns the name of this embedding mode.
+     * @return String - lower case.
+     */
+    public String getName() {
+        return this.toString().toLowerCase();
+    }
+
+    /**
+     * Returns {@link EmbeddingMode} by name.
+     * @param value String - the name of the embedding mode (not case sensitive).
+     * @return embedding mode constant.
+     */
+    public static EmbeddingMode getValue(String value) {
+        for (EmbeddingMode mode : EmbeddingMode.values()) {
+            if (mode.toString().equalsIgnoreCase(value)) {
+                return mode;
+            }
+        }
+        throw new IllegalArgumentException("Invalid embedding-mode: " + value);
+    }
+}
Index: src/java/org/apache/fop/fonts/FontLoader.java
===================================================================
--- src/java/org/apache/fop/fonts/FontLoader.java	(revision 1042681)
+++ src/java/org/apache/fop/fonts/FontLoader.java	(working copy)
@@ -30,7 +30,6 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
 import org.apache.fop.fonts.truetype.TTFFontLoader;
 import org.apache.fop.fonts.type1.Type1FontLoader;
 
@@ -80,15 +79,17 @@
      * @param fontFile the File representation of the font
      * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
      * @param embedded indicates whether the font is embedded or referenced
+     * @param embeddingMode the embedding mode
      * @param encodingMode the requested encoding mode
      * @param resolver the font resolver to use when resolving URIs
      * @return the newly loaded font
      * @throws IOException In case of an I/O error
      */
     public static CustomFont loadFont(File fontFile, String subFontName,
-            boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
+            boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+            FontResolver resolver) throws IOException {
         return loadFont(fontFile.toURI().toURL(), subFontName,
-                embedded, encodingMode, resolver);
+                embedded, embeddingMode, encodingMode, resolver);
     }
 
     /**
@@ -96,16 +97,17 @@
      * @param fontUrl the URL representation of the font
      * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
      * @param embedded indicates whether the font is embedded or referenced
+     * @param embeddingMode the embedding mode of the font
      * @param encodingMode the requested encoding mode
      * @param resolver the font resolver to use when resolving URIs
      * @return the newly loaded font
      * @throws IOException In case of an I/O error
      */
     public static CustomFont loadFont(URL fontUrl, String subFontName,
-            boolean embedded, EncodingMode encodingMode,
+            boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
             FontResolver resolver) throws IOException {
         return loadFont(fontUrl.toExternalForm(), subFontName,
-                embedded, encodingMode, true,
+                embedded, embeddingMode, encodingMode, true,
                 resolver);
     }
 
@@ -114,6 +116,7 @@
      * @param fontFileURI the URI to the font
      * @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
      * @param embedded indicates whether the font is embedded or referenced
+     * @param embeddingMode the embedding mode of the font
      * @param encodingMode the requested encoding mode
      * @param useKerning indicates whether kerning information should be loaded if available
      * @param resolver the font resolver to use when resolving URIs
@@ -121,8 +124,8 @@
      * @throws IOException In case of an I/O error
      */
     public static CustomFont loadFont(String fontFileURI, String subFontName,
-            boolean embedded, EncodingMode encodingMode, boolean useKerning,
-            FontResolver resolver) throws IOException {
+            boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+            boolean useKerning, FontResolver resolver) throws IOException {
         fontFileURI = fontFileURI.trim();
         boolean type1 = isType1(fontFileURI);
         FontLoader loader;
@@ -131,10 +134,14 @@
                 throw new IllegalArgumentException(
                         "CID encoding mode not supported for Type 1 fonts");
             }
+            if (embeddingMode == EmbeddingMode.SUBSET) {
+                throw new IllegalArgumentException(
+                        "Subset embedding for Type 1 fonts is not supported");
+            }
             loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver);
         } else {
             loader = new TTFFontLoader(fontFileURI, subFontName,
-                    embedded, encodingMode, useKerning, resolver);
+                    embedded, embeddingMode, encodingMode, useKerning, resolver);
         }
         return loader.getFont();
     }
Index: src/java/org/apache/fop/pdf/PDFFactory.java
===================================================================
--- src/java/org/apache/fop/pdf/PDFFactory.java	(revision 1042681)
+++ src/java/org/apache/fop/pdf/PDFFactory.java	(working copy)
@@ -39,9 +39,6 @@
 import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
-import org.apache.xmlgraphics.xmp.Metadata;
-
 import org.apache.fop.fonts.CIDFont;
 import org.apache.fop.fonts.CIDSubset;
 import org.apache.fop.fonts.CodePointMapping;
@@ -59,6 +56,7 @@
 import org.apache.fop.fonts.truetype.TTFSubSetFile;
 import org.apache.fop.fonts.type1.PFBData;
 import org.apache.fop.fonts.type1.PFBParser;
+import org.apache.xmlgraphics.xmp.Metadata;
 
 /**
  * This class provides method to create and register PDF objects.
@@ -1663,8 +1661,8 @@
                         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);
Index: src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
===================================================================
--- src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java	(revision 1042681)
+++ src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java	(working copy)
@@ -37,19 +37,17 @@
 import javax.xml.transform.stream.StreamResult;
 import javax.xml.transform.stream.StreamSource;
 
-import org.w3c.dom.Node;
-
 import org.apache.commons.io.IOUtils;
+import org.apache.fop.events.model.EventModel;
+import org.apache.fop.events.model.EventProducerModel;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DirectoryScanner;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.Task;
 import org.apache.tools.ant.types.FileSet;
 import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.w3c.dom.Node;
 
-import org.apache.fop.events.model.EventModel;
-import org.apache.fop.events.model.EventProducerModel;
-
 /**
  * Ant task which inspects a file set for Java interfaces which extend the
  * {@link org.apache.fop.events.EventProducer} interface. For all such interfaces an event model
Index: build.xml
===================================================================
--- build.xml	(revision 1042681)
+++ build.xml	(working copy)
@@ -90,7 +90,10 @@
 	<include name="lib/build/jaxen*"/>
 	<include name="lib/build/pmd*"/>
 	<include name="lib/build/qdox*"/>
-        <include name="lib/build/xmlunit*"/>
+  <include name="lib/build/xmlunit*"/>
+  <include name="lib/build/mockito*"/>
+  <include name="lib/build/hamcrest*"/>
+  <include name="lib/build/objenesis*"/>
   </patternset>
   <fileset dir="${basedir}" id="dist.src">
     <include name="src/**"/>
@@ -944,7 +947,15 @@
       <test name="org.apache.fop.text.linebreak.LineBreakStatusTest" todir="${junit.reports.dir}"/>
     </junit>
   </target>
-  <target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree"/>
+	<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-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree, junit-fonts, junit-render-ps"/>
   <target name="junit-full" depends="junit-reduced, junit-layout, junit-area-tree-xml-format, junit-intermediate-format"/>
   <target name="junit" depends="junit-full" 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>
