You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2013/07/23 19:13:11 UTC

svn commit: r1506164 - in /pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf: AbstractTTFParser.java CMAPEncodingEntry.java CMAPTable.java Glyph2D.java TTFParser.java

Author: lehmi
Date: Tue Jul 23 17:13:11 2013
New Revision: 1506164

URL: http://svn.apache.org/r1506164
Log:
PDFBOX-490: fixed CMap format 2, added some new constants fixed some parser checks, removed Glyph2D class

Removed:
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/Glyph2D.java
Modified:
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/AbstractTTFParser.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPEncodingEntry.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPTable.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/AbstractTTFParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/AbstractTTFParser.java?rev=1506164&r1=1506163&r2=1506164&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/AbstractTTFParser.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/AbstractTTFParser.java Tue Jul 23 17:13:11 2013
@@ -23,70 +23,86 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
-abstract class AbstractTTFParser {
-	
+/**
+ * This class represents a true type font parser.
+ * 
+ */
+abstract class AbstractTTFParser
+{
+
     protected boolean isEmbedded = false;
 
-	public AbstractTTFParser(boolean isEmbedded) {
-		this.isEmbedded = isEmbedded;
-	}
-	
+    /**
+     * Constructor.
+     * 
+     * @param fontIsEmbedded indicates whether the font is embedded or not.
+     * 
+     */
+    public AbstractTTFParser(boolean fontIsEmbedded)
+    {
+        isEmbedded = fontIsEmbedded;
+    }
+
     /**
      * Parse a file and get a true type font.
+     * 
      * @param ttfFile The TTF file.
      * @return A true type font.
      * @throws IOException If there is an error parsing the true type font.
      */
-    public TrueTypeFont parseTTF( String ttfFile ) throws IOException
+    public TrueTypeFont parseTTF(String ttfFile) throws IOException
     {
-        RAFDataStream raf = new RAFDataStream( ttfFile, "r" );
-        return parseTTF( raf );
+        RAFDataStream raf = new RAFDataStream(ttfFile, "r");
+        return parseTTF(raf);
     }
-    
+
     /**
      * Parse a file and get a true type font.
+     * 
      * @param ttfFile The TTF file.
      * @return A true type font.
      * @throws IOException If there is an error parsing the true type font.
      */
-    public TrueTypeFont parseTTF( File ttfFile ) throws IOException
+    public TrueTypeFont parseTTF(File ttfFile) throws IOException
     {
-        RAFDataStream raf = new RAFDataStream( ttfFile, "r" );
-        return parseTTF( raf );
+        RAFDataStream raf = new RAFDataStream(ttfFile, "r");
+        return parseTTF(raf);
     }
-    
+
     /**
      * Parse a file and get a true type font.
+     * 
      * @param ttfData The TTF data to parse.
      * @return A true type font.
      * @throws IOException If there is an error parsing the true type font.
      */
-    public TrueTypeFont parseTTF( InputStream ttfData ) throws IOException
+    public TrueTypeFont parseTTF(InputStream ttfData) throws IOException
     {
-        return parseTTF( new MemoryTTFDataStream( ttfData ));
+        return parseTTF(new MemoryTTFDataStream(ttfData));
     }
-    
+
     /**
      * Parse a file and get a true type font.
+     * 
      * @param raf The TTF file.
      * @return A true type font.
      * @throws IOException If there is an error parsing the true type font.
      */
-    public TrueTypeFont parseTTF( TTFDataStream raf ) throws IOException
+    public TrueTypeFont parseTTF(TTFDataStream raf) throws IOException
     {
-        TrueTypeFont font = new TrueTypeFont( raf );
-        font.setVersion( raf.read32Fixed() );
+        TrueTypeFont font = new TrueTypeFont(raf);
+        font.setVersion(raf.read32Fixed());
         int numberOfTables = raf.readUnsignedShort();
         int searchRange = raf.readUnsignedShort();
         int entrySelector = raf.readUnsignedShort();
         int rangeShift = raf.readUnsignedShort();
-        for( int i=0; i<numberOfTables; i++ )
+        for (int i = 0; i < numberOfTables; i++)
         {
-            TTFTable table = readTableDirectory( raf );   
-            font.addTable( table );
+            TTFTable table = readTableDirectory(raf);
+            font.addTable(table);
         }
 
-        //need to initialize a couple tables in a certain order
+        // need to initialize a couple tables in a certain order
         parseTables(font, raf);
 
         return font;
@@ -94,177 +110,149 @@ abstract class AbstractTTFParser {
 
     /**
      * Parse all tables and check if all needed tables are present.
+     * 
      * @param font the TrueTypeFont instance holding the parsed data.
      * @param raf the data stream of the to be parsed ttf font
      * @throws IOException If there is an error parsing the true type font.
      */
-    protected void parseTables(TrueTypeFont font, TTFDataStream raf)
-    throws IOException {
+    protected void parseTables(TrueTypeFont font, TTFDataStream raf) throws IOException
+    {
         List<TTFTable> initialized = new ArrayList<TTFTable>();
         HeaderTable head = font.getHeader();
-        if (head == null) 
+        if (head == null)
         {
             throw new IOException("head is mandatory");
         }
-        raf.seek( head.getOffset() );
-        head.initData( font, raf );
-        initialized.add( head );
+        raf.seek(head.getOffset());
+        head.initData(font, raf);
+        initialized.add(head);
 
         HorizontalHeaderTable hh = font.getHorizontalHeader();
-        if (hh == null) 
+        if (hh == null)
         {
             throw new IOException("hhead is mandatory");
         }
-        raf.seek( hh.getOffset() );
-        hh.initData( font, raf );
-        initialized.add( hh );
+        raf.seek(hh.getOffset());
+        hh.initData(font, raf);
+        initialized.add(hh);
 
         MaximumProfileTable maxp = font.getMaximumProfile();
-        if (maxp != null) 
+        if (maxp != null)
         {
-            raf.seek( maxp.getOffset() );
-            maxp.initData( font, raf );
-            initialized.add( maxp );
-        } 
-        else 
+            raf.seek(maxp.getOffset());
+            maxp.initData(font, raf);
+            initialized.add(maxp);
+        }
+        else
         {
             throw new IOException("maxp is mandatory");
         }
 
         PostScriptTable post = font.getPostScript();
-        if (post != null) {
-            raf.seek( post.getOffset() );
-            post.initData( font, raf );
-            initialized.add( post );
-        } 
-        else if ( !isEmbedded ) 
+        if (post != null)
+        {
+            raf.seek(post.getOffset());
+            post.initData(font, raf);
+            initialized.add(post);
+        }
+        else if (!isEmbedded)
         {
             // in an embedded font this table is optional
             throw new IOException("post is mandatory");
         }
 
         IndexToLocationTable loc = font.getIndexToLocation();
-        if (loc == null) 
+        if (loc == null)
         {
             throw new IOException("loca is mandatory");
         }
-        raf.seek( loc.getOffset() );
-        loc.initData( font, raf );
-        initialized.add( loc );
+        raf.seek(loc.getOffset());
+        loc.initData(font, raf);
+        initialized.add(loc);
 
-        boolean cvt = false, prep = false, fpgm = false;
         Iterator<TTFTable> iter = font.getTables().iterator();
-        while( iter.hasNext() )
+        while (iter.hasNext())
         {
             TTFTable table = iter.next();
-            if( !initialized.contains( table ) )
+            if (!initialized.contains(table))
             {
-                raf.seek( table.getOffset() );
-                table.initData( font, raf );
+                raf.seek(table.getOffset());
+                table.initData(font, raf);
             }
-            if (table.getTag().startsWith("cvt")) 
-            {
-                cvt = true;
-            } 
-            else if ("prep".equals(table.getTag())) 
-            {
-                prep = true;
-            } 
-            else if ("fpgm".equals(table.getTag())) 
-            {
-                fpgm = true;
-            }
-        }   
+        }
 
-        // check others mandatory tables
-        if ( font.getGlyph() == null )
+        // check other mandatory tables
+        if (font.getGlyph() == null)
         {
             throw new IOException("glyf is mandatory");
         }
-        if ( font.getNaming() == null && !isEmbedded )
+        if (font.getNaming() == null && !isEmbedded)
         {
             throw new IOException("name is mandatory");
         }
-        if ( font.getHorizontalMetrics() == null )
+        if (font.getHorizontalMetrics() == null)
         {
             throw new IOException("hmtx is mandatory");
         }
-
-        if (isEmbedded) {
-            // in a embedded truetype font prep, cvt_ and fpgm tables 
-            // are mandatory
-            if (!fpgm) 
-            {
-                throw new IOException("fpgm is mandatory");
-            }
-            if (!prep)
-            {
-                throw new IOException("prep is mandatory");
-            }
-            if (!cvt) 
-            {
-                throw new IOException("cvt_ is mandatory");
-            }
-        }
     }
 
-    private TTFTable readTableDirectory( TTFDataStream raf ) throws IOException
+    private TTFTable readTableDirectory(TTFDataStream raf) throws IOException
     {
         TTFTable retval = null;
-        String tag = raf.readString( 4 );
-        if( tag.equals( CMAPTable.TAG ) )
+        String tag = raf.readString(4);
+        if (tag.equals(CMAPTable.TAG))
         {
             retval = new CMAPTable();
         }
-        else if( tag.equals( GlyphTable.TAG ) )
+        else if (tag.equals(GlyphTable.TAG))
         {
             retval = new GlyphTable();
         }
-        else if( tag.equals( HeaderTable.TAG ) )
+        else if (tag.equals(HeaderTable.TAG))
         {
             retval = new HeaderTable();
         }
-        else if( tag.equals( HorizontalHeaderTable.TAG ) )
+        else if (tag.equals(HorizontalHeaderTable.TAG))
         {
             retval = new HorizontalHeaderTable();
         }
-        else if( tag.equals( HorizontalMetricsTable.TAG ) )
+        else if (tag.equals(HorizontalMetricsTable.TAG))
         {
             retval = new HorizontalMetricsTable();
         }
-        else if( tag.equals( IndexToLocationTable.TAG ) )
+        else if (tag.equals(IndexToLocationTable.TAG))
         {
             retval = new IndexToLocationTable();
         }
-        else if( tag.equals( MaximumProfileTable.TAG ) )
+        else if (tag.equals(MaximumProfileTable.TAG))
         {
             retval = new MaximumProfileTable();
         }
-        else if( tag.equals( NamingTable.TAG ) )
+        else if (tag.equals(NamingTable.TAG))
         {
             retval = new NamingTable();
         }
-        else if( tag.equals( OS2WindowsMetricsTable.TAG ) )
+        else if (tag.equals(OS2WindowsMetricsTable.TAG))
         {
             retval = new OS2WindowsMetricsTable();
         }
-        else if( tag.equals( PostScriptTable.TAG ) )
+        else if (tag.equals(PostScriptTable.TAG))
         {
             retval = new PostScriptTable();
         }
-        else if( tag.equals( DigitalSignatureTable.TAG ) )
+        else if (tag.equals(DigitalSignatureTable.TAG))
         {
             retval = new DigitalSignatureTable();
         }
         else
         {
-            //unknown table type but read it anyway.
+            // unknown table type but read it anyway.
             retval = new TTFTable();
         }
-        retval.setTag( tag );
-        retval.setCheckSum( raf.readUnsignedInt() );
-        retval.setOffset( raf.readUnsignedInt() );
-        retval.setLength( raf.readUnsignedInt() );
+        retval.setTag(tag);
+        retval.setCheckSum(raf.readUnsignedInt());
+        retval.setOffset(raf.readUnsignedInt());
+        retval.setLength(raf.readUnsignedInt());
         return retval;
     }
 }

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPEncodingEntry.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPEncodingEntry.java?rev=1506164&r1=1506163&r2=1506164&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPEncodingEntry.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPEncodingEntry.java Tue Jul 23 17:13:11 2013
@@ -22,566 +22,608 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+
 /**
  * An encoding entry for a cmap.
  * 
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.2 $
+ * 
  */
 public class CMAPEncodingEntry
 {
+    private static final long LEAD_OFFSET = 0xD800 - (0x10000 >> 10);
+    private static final long SURROGATE_OFFSET = 0x10000 - (0xD800 << 10) - 0xDC00;
 
-	private int platformId;
-	private int platformEncodingId;
-	private long subTableOffset;
-	private int[] glyphIdToCharacterCode;
-	private Map<Integer, Integer> characterCodeToGlyphId = new HashMap<Integer, Integer>();
-
-	/**
-	 * This will read the required data from the stream.
-	 * 
-	 * @param ttf The font that is being read.
-	 * @param data The stream to read the data from.
-	 * @throws IOException If there is an error reading the data.
-	 */
-	public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
-	{
-		platformId = data.readUnsignedShort();
-		platformEncodingId = data.readUnsignedShort();
-		subTableOffset = data.readUnsignedInt();
-	}
-
-	/**
-	 * This will read the required data from the stream.
-	 * 
-	 * @param ttf The font that is being read.
-	 * @param data The stream to read the data from.
-	 * @throws IOException If there is an error reading the data.
-	 */
-	public void initSubtable( TrueTypeFont ttf, TTFDataStream data ) throws IOException
-	{
-		data.seek( ttf.getCMAP().getOffset() + subTableOffset );
-		int subtableFormat = data.readUnsignedShort();
-		long length;
-		long version;
-		int numGlyphs;
-		if (subtableFormat < 8) {
-			length = data.readUnsignedShort();
-			version = data.readUnsignedShort();
-			numGlyphs = ttf.getMaximumProfile().getNumGlyphs();
-		} else {
-			// read an other UnsignedShort to read a Fixed32
-			data.readUnsignedShort();
-			length = data.readUnsignedInt();
-			version = data.readUnsignedInt();
-			numGlyphs = ttf.getMaximumProfile().getNumGlyphs();         
-		}
-
-		switch (subtableFormat) {
-		case 0:
-			processSubtype0(ttf, data);
-			break;
-		case 2:
-			processSubtype2(ttf, data, numGlyphs);
-			break;
-		case 4:
-			processSubtype4(ttf, data, numGlyphs);
-			break;
-		case 6:
-			processSubtype6(ttf, data, numGlyphs);
-			break;
-		case 8:
-			processSubtype8(ttf, data, numGlyphs);
-			break;
-		case 10:
-			processSubtype10(ttf, data, numGlyphs);
-			break;
-		case 12:
-			processSubtype12(ttf, data, numGlyphs);
-			break;
-		case 13:
-			processSubtype13(ttf, data, numGlyphs); 
-			break;
-		case 14:
-			processSubtype14(ttf, data, numGlyphs);
-			break;
-		default:
-			throw new IOException( "Unknown cmap format:" + subtableFormat );   
-		}
-	}
-
-	/**
-	 * Reads a format 8 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype8( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		// --- is32 is a 65536 BITS array ( = 8192 BYTES) 
-		int[] is32 = data.readUnsignedByteArray(8192);
-		long nbGroups = data.readUnsignedInt();
-
-		// --- nbGroups shouldn't be greater than 65536
-		if (nbGroups > 65536) {
-			throw new IOException("CMap ( Subtype8 ) is invalid");
-		}
-
-		glyphIdToCharacterCode = new int[numGlyphs];
-		// -- Read all sub header
-		for (long i = 0; i <= nbGroups ; ++i ) 
-		{
-			long firstCode = data.readUnsignedInt();
-			long endCode = data.readUnsignedInt();
-			long startGlyph = data.readUnsignedInt();
-
-			// -- process simple validation
-			if (firstCode > endCode || 0 > firstCode) {
-				throw new IOException("Range invalid");
-			}
-
-			for (long j = firstCode; j <= endCode; ++j) {
-				// -- Convert the Character code in decimal
-				if (j > Integer.MAX_VALUE) {
-					throw new IOException("[Sub Format 8] Invalid Character code");
-				}
-
-				int currentCharCode;
-				if ( (is32[ (int)j / 8 ] & (1 << ((int)j % 8 ))) == 0) {
-					currentCharCode = (int)j;
-				} else {
-					// the character code uses a 32bits format 
-					// convert it in decimal : see http://www.unicode.org/faq//utf_bom.html#utf16-4
-					long LEAD_OFFSET = 0xD800 - (0x10000 >> 10);
-					long SURROGATE_OFFSET = 0x10000 - (0xD800 << 10) - 0xDC00;
-					long lead = LEAD_OFFSET + (j >> 10);
-					long trail = 0xDC00 + (j & 0x3FF);
-
-					long codepoint = (lead << 10) + trail + SURROGATE_OFFSET;
-					if (codepoint > Integer.MAX_VALUE) {
-						throw new IOException("[Sub Format 8] Invalid Character code");
-					}
-					currentCharCode = (int)codepoint;
-				}
-
-				long glyphIndex = startGlyph + (j-firstCode);
-				if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) {
-					throw new IOException("CMap contains an invalid glyph index");
-				}
-
-				glyphIdToCharacterCode[(int)glyphIndex] = currentCharCode;
-				characterCodeToGlyphId.put(currentCharCode, (int)glyphIndex);
-			}
-		}
-	}
-
-	/**
-	 * Reads a format 10 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype10( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		long startCode = data.readUnsignedInt();
-		long numChars = data.readUnsignedInt();
-		if (numChars > Integer.MAX_VALUE) {
-			throw new IOException("Invalid number of Characters");
-		}
-
-		if ( startCode < 0 || startCode > 0x0010FFFF 
-				|| (startCode + numChars) > 0x0010FFFF
-				|| ((startCode + numChars) >= 0x0000D800 && (startCode + numChars) <= 0x0000DFFF)) {
-			throw new IOException("Invalid Characters codes");
-
-		}   
-	}   
-
-	/**
-	 * Reads a format 12 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype12( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		long nbGroups = data.readUnsignedInt();
-		glyphIdToCharacterCode = new int[numGlyphs];
-		for (long i = 0; i <= nbGroups ; ++i ) 
-		{
-			long firstCode = data.readUnsignedInt();
-			long endCode = data.readUnsignedInt();
-			long startGlyph = data.readUnsignedInt();
-
-			if ( firstCode < 0 || firstCode > 0x0010FFFF 
-					|| ( firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF ) ) {
-				throw new IOException("Invalid Characters codes");
-			}
-
-			if ( endCode > 0 && (endCode < firstCode || endCode > 0x0010FFFF 
-					|| ( endCode >= 0x0000D800 && endCode <= 0x0000DFFF ) ) ) {
-				throw new IOException("Invalid Characters codes");
-			}
-
-			for (long j = 0; j <= (endCode - firstCode); ++j) {
-
-				if ( (firstCode + j) > Integer.MAX_VALUE ) {
-					throw new IOException("Character Code greater than Integer.MAX_VALUE");                 
-				}
-
-				long glyphIndex = (startGlyph + j);
-				if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) {
-					throw new IOException("CMap contains an invalid glyph index");
-				}
-				glyphIdToCharacterCode[(int)glyphIndex] = (int)(firstCode + j);
-				characterCodeToGlyphId.put((int)(firstCode + j), (int)glyphIndex);
-			}
-		}
-	}
-
-	/**
-	 * Reads a format 13 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype13( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		long nbGroups = data.readUnsignedInt();
-		for (long i = 0; i <= nbGroups ; ++i ) 
-		{
-			long firstCode = data.readUnsignedInt();
-			long endCode = data.readUnsignedInt();
-			long glyphId = data.readUnsignedInt();
-
-			if (glyphId > numGlyphs) {
-				throw new IOException("CMap contains an invalid glyph index");  
-			}
-
-			if ( firstCode < 0 || firstCode > 0x0010FFFF 
-					|| ( firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF ) ) {
-				throw new IOException("Invalid Characters codes");
-			}
-
-			if ( endCode > 0 && (endCode < firstCode || endCode > 0x0010FFFF 
-					|| ( endCode >= 0x0000D800 && endCode <= 0x0000DFFF )) ) {
-				throw new IOException("Invalid Characters codes");
-			}
-
-			for (long j = 0; j <= (endCode - firstCode); ++j) {
-
-				if ( (firstCode + j) > Integer.MAX_VALUE ) {
-					throw new IOException("Character Code greater than Integer.MAX_VALUE");                 
-				}
-				glyphIdToCharacterCode[(int)glyphId] = (int)(firstCode + j);
-				characterCodeToGlyphId.put((int)(firstCode + j), (int)glyphId);
-			}
-		}
-	}
-
-	/**
-	 * Reads a format 14 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype14( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		throw new IOException("CMap subtype 14 not yet implemented");
-	}
-
-	/**
-	 * Reads a format 6 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype6( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		int firstCode = data.readUnsignedShort();
-		int entryCount = data.readUnsignedShort();
-		glyphIdToCharacterCode = new int[numGlyphs];
-		int[] glyphIdArray = data.readUnsignedShortArray( entryCount );
-		for( int i=0; i<entryCount; i++)
-		{
-			glyphIdToCharacterCode[glyphIdArray[i]] = firstCode+i;
-			characterCodeToGlyphId.put((firstCode+i), glyphIdArray[i]);
-		}
-	}
-
-	/**
-	 * Reads a format 4 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype4( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		int segCountX2 = data.readUnsignedShort();
-		int segCount = segCountX2/2;
-		int searchRange = data.readUnsignedShort();
-		int entrySelector = data.readUnsignedShort();
-		int rangeShift = data.readUnsignedShort();
-		int[] endCount = data.readUnsignedShortArray( segCount );
-		int reservedPad = data.readUnsignedShort();
-		int[] startCount = data.readUnsignedShortArray( segCount );
-		int[] idDelta = data.readUnsignedShortArray( segCount );
-		int[] idRangeOffset = data.readUnsignedShortArray( segCount );
-
-		Map<Integer, Integer> tmpGlyphToChar = new HashMap<Integer, Integer>();
-
-		long currentPosition = data.getCurrentPosition();
-
-		for( int i=0; i<segCount; i++ )
-		{
-			int start = startCount[i];
-			int end = endCount[i];
-			int delta = idDelta[i];
-			int rangeOffset = idRangeOffset[i];
-			if( start != 65535 && end != 65535 )
-			{
-				for( int j=start; j<=end; j++ )
-				{
-					if( rangeOffset == 0 )
-					{
-						int glyphid = (j+delta)%65536;
-						tmpGlyphToChar.put(glyphid,j);
-						characterCodeToGlyphId.put(j, glyphid);
-					}
-					else
-					{
-						long glyphOffset = currentPosition +
-								((rangeOffset/2) + //idRangeOffset[i]/2 
-										(j-start) + //(c - startCount[i])                                   
-										(i-segCount))*2; //&idRangeOffset[i]); 
-						data.seek( glyphOffset );
-						int glyphIndex = data.readUnsignedShort();
-						if( glyphIndex != 0 )
-						{
-							glyphIndex += delta;
-							glyphIndex = glyphIndex % 65536;
-							if(!tmpGlyphToChar.containsKey(glyphIndex))
-							{
-								tmpGlyphToChar.put(glyphIndex,j);
-								characterCodeToGlyphId.put(j, glyphIndex);
-							}
-						}
-					}
-				}
-			}
-		}
-
-		/* this is the final result
-		 * key=glyphId, value is character codes
-		 * Create an array that contains MAX(GlyphIds) element and fill this array with the .notdef character
-		 */
-		glyphIdToCharacterCode = new int[Collections.max(tmpGlyphToChar.keySet())+1];
-		Arrays.fill(glyphIdToCharacterCode, 0);
-		for (Entry<Integer, Integer> entry : tmpGlyphToChar.entrySet()) {
-			// link the glyphId with the right character code
-			glyphIdToCharacterCode[entry.getKey()] = entry.getValue();
-		}
-	}
-
-	/**
-	 * Read a format 2 subtable.
-	 * @param ttf the TrueTypeFont instance holding the parsed data.
-	 * @param data the data stream of the to be parsed ttf font
-	 * @param numGlyphs number of glyphs to be read
-	 * @throws IOException If there is an error parsing the true type font.
-	 */
-	protected void processSubtype2( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) 
-			throws IOException {
-		int[] subHeaderKeys = new int[256];
-		// ---- keep the Max Index of the SubHeader array to know its length
-		int maxSubHeaderIndex = 0;
-		for( int i=0; i<256; i++)
-		{
-			subHeaderKeys[i] = data.readUnsignedShort();
-			maxSubHeaderIndex = Math.max(maxSubHeaderIndex, (int)(subHeaderKeys[i]/8));
-		}
-
-		// ---- Read all SubHeaders to avoid useless seek on DataSource
-		SubHeader[] subHeaders = new SubHeader[maxSubHeaderIndex + 1]; 
-		for (int i = 0; i <= maxSubHeaderIndex ; ++i ) 
-		{
-			int firstCode = data.readUnsignedShort();
-			int entryCount = data.readUnsignedShort();
-			short idDelta = data.readSignedShort();
-			int idRangeOffset = data.readUnsignedShort();
-			subHeaders[i] = new SubHeader(firstCode, entryCount, idDelta, idRangeOffset);
-		}
-		long startGlyphIndexOffset = data.getCurrentPosition();
-		glyphIdToCharacterCode = new int[numGlyphs];
-		for ( int i = 0; i <= maxSubHeaderIndex ; ++i )
-		{
-			SubHeader sh = subHeaders[i];
-			int firstCode = sh.getFirstCode();
-			for ( int j = 0 ; j < sh.getEntryCount() ; ++j)
-			{
-				// ---- compute the Character Code
-				int charCode = ( i * 8 );
-				charCode = (charCode << 8 ) + (firstCode + j);
-
-				// ---- Go to the CharacterCOde position in the Sub Array 
-				//      of the glyphIndexArray 
-				//      glyphIndexArray contains Unsigned Short so add (j * 2) bytes 
-				//      at the index position
-				data.seek(startGlyphIndexOffset + sh.getIdRangeOffset() + (j*2));
-				int p = data.readUnsignedShort();
-				// ---- compute the glyphIndex 
-				p = p + sh.getIdDelta() % 65536;
-
-				glyphIdToCharacterCode[p] = charCode;
-				characterCodeToGlyphId.put(charCode, p);
-			}
-		}
-	}
-
-	/**
-	 * Initialize the CMapEntry when it is a subtype 0
-	 * 
-	 * @param ttf
-	 * @param data
-	 * @throws IOException
-	 */
-	protected void processSubtype0( TrueTypeFont ttf, TTFDataStream data ) 
-			throws IOException {
-		byte[] glyphMapping = data.read( 256 );
-		glyphIdToCharacterCode = new int[256];
-		for( int i=0;i < glyphMapping.length; i++ )
-		{
-			int glyphIndex = (glyphMapping[i]+256)%256;
-			glyphIdToCharacterCode[glyphIndex]=i;
-			characterCodeToGlyphId.put(i, glyphIndex);
-		}
-	}
-
-	/**
-	 * @return Returns the glyphIdToCharacterCode.
-	 */
-	public int[] getGlyphIdToCharacterCode()
-	{
-		return glyphIdToCharacterCode;
-	}
-	/**
-	 * @param glyphIdToCharacterCodeValue The glyphIdToCharacterCode to set.
-	 */
-	public void setGlyphIdToCharacterCode(int[] glyphIdToCharacterCodeValue)
-	{
-		this.glyphIdToCharacterCode = glyphIdToCharacterCodeValue;
-	}
-
-	/**
-	 * @return Returns the platformEncodingId.
-	 */
-	public int getPlatformEncodingId()
-	{
-		return platformEncodingId;
-	}
-	/**
-	 * @param platformEncodingIdValue The platformEncodingId to set.
-	 */
-	public void setPlatformEncodingId(int platformEncodingIdValue)
-	{
-		this.platformEncodingId = platformEncodingIdValue;
-	}
-	/**
-	 * @return Returns the platformId.
-	 */
-	public int getPlatformId()
-	{
-		return platformId;
-	}
-	/**
-	 * @param platformIdValue The platformId to set.
-	 */
-	public void setPlatformId(int platformIdValue)
-	{
-		this.platformId = platformIdValue;
-	}
-
-	/**
-	 * Returns the GlyphId linked with the given character code. 
-	 * @param characterCode
-	 * @return glyphId
-	 */
-	public int getGlyphId(int characterCode) {
-		if (this.characterCodeToGlyphId.containsKey(characterCode)) 
-		{
-			return this.characterCodeToGlyphId.get(characterCode);
-		} 
-		else 
-		{
-			return 0;
-		}
-	}
-
-	/**
-	 * Class used to manage CMap - Format 2
-	 */
-	private class SubHeader {
-
-		private int firstCode;
-		private int entryCount;
-		/**
-		 * used to compute the GlyphIndex :
-		 * P = glyphIndexArray.SubArray[pos]
-		 * GlyphIndex = P + idDelta % 65536
-		 */
-		private short idDelta;
-		/**
-		 * Number of bytes to skip to reach the firstCode in the 
-		 * glyphIndexArray 
-		 */
-		private int idRangeOffset;
-
-		private SubHeader(int firstCode, int entryCount, short idDelta, int idRangeOffset) 
-		{
-			this.firstCode = firstCode;
-			this.entryCount = entryCount;
-			this.idDelta = idDelta;
-			this.idRangeOffset = idRangeOffset;
-		}    
-
-		/**
-		 * @return the firstCode
-		 */
-		private int getFirstCode() 
-		{
-			return firstCode;
-		}
-
-		/**
-		 * @return the entryCount
-		 */
-		private int getEntryCount() 
-		{
-			return entryCount;
-		}    
-
-		/**
-		 * @return the idDelta
-		 */
-		private short getIdDelta() 
-		{
-			return idDelta;
-		}
-
-		/**
-		 * @return the idRangeOffset
-		 */
-		private int getIdRangeOffset() 
-		{
-			return idRangeOffset;
-		}
-	}
+    private int platformId;
+    private int platformEncodingId;
+    private long subTableOffset;
+    private int[] glyphIdToCharacterCode;
+    private Map<Integer, Integer> characterCodeToGlyphId = new HashMap<Integer, Integer>();
+
+    /**
+     * This will read the required data from the stream.
+     * 
+     * @param ttf The font that is being read.
+     * @param data The stream to read the data from.
+     * @throws IOException If there is an error reading the data.
+     */
+    public void initData(TrueTypeFont ttf, TTFDataStream data) throws IOException
+    {
+        platformId = data.readUnsignedShort();
+        platformEncodingId = data.readUnsignedShort();
+        subTableOffset = data.readUnsignedInt();
+    }
+
+    /**
+     * This will read the required data from the stream.
+     * 
+     * @param ttf The font that is being read.
+     * @param data The stream to read the data from.
+     * @throws IOException If there is an error reading the data.
+     */
+    public void initSubtable(TrueTypeFont ttf, TTFDataStream data) throws IOException
+    {
+        data.seek(ttf.getCMAP().getOffset() + subTableOffset);
+        int subtableFormat = data.readUnsignedShort();
+        long length;
+        long version;
+        int numGlyphs;
+        if (subtableFormat < 8)
+        {
+            length = data.readUnsignedShort();
+            version = data.readUnsignedShort();
+            numGlyphs = ttf.getMaximumProfile().getNumGlyphs();
+        }
+        else
+        {
+            // read an other UnsignedShort to read a Fixed32
+            data.readUnsignedShort();
+            length = data.readUnsignedInt();
+            version = data.readUnsignedInt();
+            numGlyphs = ttf.getMaximumProfile().getNumGlyphs();
+        }
+
+        switch (subtableFormat)
+        {
+        case 0:
+            processSubtype0(ttf, data);
+            break;
+        case 2:
+            processSubtype2(ttf, data, numGlyphs);
+            break;
+        case 4:
+            processSubtype4(ttf, data, numGlyphs);
+            break;
+        case 6:
+            processSubtype6(ttf, data, numGlyphs);
+            break;
+        case 8:
+            processSubtype8(ttf, data, numGlyphs);
+            break;
+        case 10:
+            processSubtype10(ttf, data, numGlyphs);
+            break;
+        case 12:
+            processSubtype12(ttf, data, numGlyphs);
+            break;
+        case 13:
+            processSubtype13(ttf, data, numGlyphs);
+            break;
+        case 14:
+            processSubtype14(ttf, data, numGlyphs);
+            break;
+        default:
+            throw new IOException("Unknown cmap format:" + subtableFormat);
+        }
+    }
+
+    /**
+     * Reads a format 8 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype8(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        // --- is32 is a 65536 BITS array ( = 8192 BYTES)
+        int[] is32 = data.readUnsignedByteArray(8192);
+        long nbGroups = data.readUnsignedInt();
+
+        // --- nbGroups shouldn't be greater than 65536
+        if (nbGroups > 65536)
+        {
+            throw new IOException("CMap ( Subtype8 ) is invalid");
+        }
+
+        glyphIdToCharacterCode = new int[numGlyphs];
+        // -- Read all sub header
+        for (long i = 0; i <= nbGroups; ++i)
+        {
+            long firstCode = data.readUnsignedInt();
+            long endCode = data.readUnsignedInt();
+            long startGlyph = data.readUnsignedInt();
+
+            // -- process simple validation
+            if (firstCode > endCode || 0 > firstCode)
+            {
+                throw new IOException("Range invalid");
+            }
+
+            for (long j = firstCode; j <= endCode; ++j)
+            {
+                // -- Convert the Character code in decimal
+                if (j > Integer.MAX_VALUE)
+                {
+                    throw new IOException("[Sub Format 8] Invalid Character code");
+                }
+
+                int currentCharCode;
+                if ((is32[(int) j / 8] & (1 << ((int) j % 8))) == 0)
+                {
+                    currentCharCode = (int) j;
+                }
+                else
+                {
+                    // the character code uses a 32bits format
+                    // convert it in decimal : see http://www.unicode.org/faq//utf_bom.html#utf16-4
+                    long lead = LEAD_OFFSET + (j >> 10);
+                    long trail = 0xDC00 + (j & 0x3FF);
+
+                    long codepoint = (lead << 10) + trail + SURROGATE_OFFSET;
+                    if (codepoint > Integer.MAX_VALUE)
+                    {
+                        throw new IOException("[Sub Format 8] Invalid Character code");
+                    }
+                    currentCharCode = (int) codepoint;
+                }
+
+                long glyphIndex = startGlyph + (j - firstCode);
+                if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE)
+                {
+                    throw new IOException("CMap contains an invalid glyph index");
+                }
+
+                glyphIdToCharacterCode[(int) glyphIndex] = currentCharCode;
+                characterCodeToGlyphId.put(currentCharCode, (int) glyphIndex);
+            }
+        }
+    }
+
+    /**
+     * Reads a format 10 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype10(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        long startCode = data.readUnsignedInt();
+        long numChars = data.readUnsignedInt();
+        if (numChars > Integer.MAX_VALUE)
+        {
+            throw new IOException("Invalid number of Characters");
+        }
+
+        if (startCode < 0 || startCode > 0x0010FFFF || (startCode + numChars) > 0x0010FFFF
+                || ((startCode + numChars) >= 0x0000D800 && (startCode + numChars) <= 0x0000DFFF))
+        {
+            throw new IOException("Invalid Characters codes");
+
+        }
+    }
+
+    /**
+     * Reads a format 12 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype12(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        long nbGroups = data.readUnsignedInt();
+        glyphIdToCharacterCode = new int[numGlyphs];
+        for (long i = 0; i <= nbGroups; ++i)
+        {
+            long firstCode = data.readUnsignedInt();
+            long endCode = data.readUnsignedInt();
+            long startGlyph = data.readUnsignedInt();
+
+            if (firstCode < 0 || firstCode > 0x0010FFFF || (firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF))
+            {
+                throw new IOException("Invalid Characters codes");
+            }
+
+            if ((endCode > 0 && endCode < firstCode) || endCode > 0x0010FFFF
+                    || (endCode >= 0x0000D800 && endCode <= 0x0000DFFF))
+            {
+                throw new IOException("Invalid Characters codes");
+            }
+
+            for (long j = 0; j <= (endCode - firstCode); ++j)
+            {
+
+                if ((firstCode + j) > Integer.MAX_VALUE)
+                {
+                    throw new IOException("Character Code greater than Integer.MAX_VALUE");
+                }
+
+                long glyphIndex = (startGlyph + j);
+                if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE)
+                {
+                    throw new IOException("CMap contains an invalid glyph index");
+                }
+                glyphIdToCharacterCode[(int) glyphIndex] = (int) (firstCode + j);
+                characterCodeToGlyphId.put((int) (firstCode + j), (int) glyphIndex);
+            }
+        }
+    }
+
+    /**
+     * Reads a format 13 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype13(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        long nbGroups = data.readUnsignedInt();
+        for (long i = 0; i <= nbGroups; ++i)
+        {
+            long firstCode = data.readUnsignedInt();
+            long endCode = data.readUnsignedInt();
+            long glyphId = data.readUnsignedInt();
+
+            if (glyphId > numGlyphs)
+            {
+                throw new IOException("CMap contains an invalid glyph index");
+            }
+
+            if (firstCode < 0 || firstCode > 0x0010FFFF || (firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF))
+            {
+                throw new IOException("Invalid Characters codes");
+            }
+
+            if ((endCode > 0 && endCode < firstCode) || endCode > 0x0010FFFF
+                    || (endCode >= 0x0000D800 && endCode <= 0x0000DFFF))
+            {
+                throw new IOException("Invalid Characters codes");
+            }
+
+            for (long j = 0; j <= (endCode - firstCode); ++j)
+            {
+
+                if ((firstCode + j) > Integer.MAX_VALUE)
+                {
+                    throw new IOException("Character Code greater than Integer.MAX_VALUE");
+                }
+                glyphIdToCharacterCode[(int) glyphId] = (int) (firstCode + j);
+                characterCodeToGlyphId.put((int) (firstCode + j), (int) glyphId);
+            }
+        }
+    }
+
+    /**
+     * Reads a format 14 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype14(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        throw new IOException("CMap subtype 14 not yet implemented");
+    }
+
+    /**
+     * Reads a format 6 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype6(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        int firstCode = data.readUnsignedShort();
+        int entryCount = data.readUnsignedShort();
+        glyphIdToCharacterCode = new int[numGlyphs];
+        int[] glyphIdArray = data.readUnsignedShortArray(entryCount);
+        for (int i = 0; i < entryCount; i++)
+        {
+            glyphIdToCharacterCode[glyphIdArray[i]] = firstCode + i;
+            characterCodeToGlyphId.put((firstCode + i), glyphIdArray[i]);
+        }
+    }
+
+    /**
+     * Reads a format 4 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype4(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        int segCountX2 = data.readUnsignedShort();
+        int segCount = segCountX2 / 2;
+        int searchRange = data.readUnsignedShort();
+        int entrySelector = data.readUnsignedShort();
+        int rangeShift = data.readUnsignedShort();
+        int[] endCount = data.readUnsignedShortArray(segCount);
+        int reservedPad = data.readUnsignedShort();
+        int[] startCount = data.readUnsignedShortArray(segCount);
+        int[] idDelta = data.readUnsignedShortArray(segCount);
+        int[] idRangeOffset = data.readUnsignedShortArray(segCount);
+
+        Map<Integer, Integer> tmpGlyphToChar = new HashMap<Integer, Integer>();
+
+        long currentPosition = data.getCurrentPosition();
+
+        for (int i = 0; i < segCount; i++)
+        {
+            int start = startCount[i];
+            int end = endCount[i];
+            int delta = idDelta[i];
+            int rangeOffset = idRangeOffset[i];
+            if (start != 65535 && end != 65535)
+            {
+                for (int j = start; j <= end; j++)
+                {
+                    if (rangeOffset == 0)
+                    {
+                        int glyphid = (j + delta) % 65536;
+                        tmpGlyphToChar.put(glyphid, j);
+                        characterCodeToGlyphId.put(j, glyphid);
+                    }
+                    else
+                    {
+                        long glyphOffset = currentPosition + ((rangeOffset / 2) +
+                                (j - start) + 
+                                (i - segCount)) * 2;
+                        data.seek(glyphOffset);
+                        int glyphIndex = data.readUnsignedShort();
+                        if (glyphIndex != 0)
+                        {
+                            glyphIndex += delta;
+                            glyphIndex = glyphIndex % 65536;
+                            if (!tmpGlyphToChar.containsKey(glyphIndex))
+                            {
+                                tmpGlyphToChar.put(glyphIndex, j);
+                                characterCodeToGlyphId.put(j, glyphIndex);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /*
+         * this is the final result key=glyphId, value is character codes Create an array that contains MAX(GlyphIds)
+         * element and fill this array with the .notdef character
+         */
+        glyphIdToCharacterCode = new int[Collections.max(tmpGlyphToChar.keySet()) + 1];
+        Arrays.fill(glyphIdToCharacterCode, 0);
+        for (Entry<Integer, Integer> entry : tmpGlyphToChar.entrySet())
+        {
+            // link the glyphId with the right character code
+            glyphIdToCharacterCode[entry.getKey()] = entry.getValue();
+        }
+    }
+
+    /**
+     * Read a format 2 subtable.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @param numGlyphs number of glyphs to be read
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype2(TrueTypeFont ttf, TTFDataStream data, int numGlyphs) throws IOException
+    {
+        int[] subHeaderKeys = new int[256];
+        // ---- keep the Max Index of the SubHeader array to know its length
+        int maxSubHeaderIndex = 0;
+        for (int i = 0; i < 256; i++)
+        {
+            subHeaderKeys[i] = data.readUnsignedShort();
+            maxSubHeaderIndex = Math.max(maxSubHeaderIndex, (int) (subHeaderKeys[i] / 8));
+        }
+
+        // ---- Read all SubHeaders to avoid useless seek on DataSource
+        SubHeader[] subHeaders = new SubHeader[maxSubHeaderIndex + 1];
+        for (int i = 0; i <= maxSubHeaderIndex; ++i)
+        {
+            int firstCode = data.readUnsignedShort();
+            int entryCount = data.readUnsignedShort();
+            short idDelta = data.readSignedShort();
+            int idRangeOffset = data.readUnsignedShort() - (maxSubHeaderIndex + 1 - i - 1) * 8 - 2;
+            subHeaders[i] = new SubHeader(firstCode, entryCount, idDelta, idRangeOffset);
+        }
+        long startGlyphIndexOffset = data.getCurrentPosition();
+        glyphIdToCharacterCode = new int[numGlyphs];
+        for (int i = 0; i <= maxSubHeaderIndex; ++i)
+        {
+            SubHeader sh = subHeaders[i];
+            int firstCode = sh.getFirstCode();
+            int idRangeOffset = sh.getIdRangeOffset();
+            int idDelta = sh.getIdDelta();
+            int entryCount = sh.getEntryCount();
+            data.seek(startGlyphIndexOffset + idRangeOffset);
+            for (int j = 0; j < entryCount; ++j)
+            {
+                // ---- compute the Character Code
+                int charCode = i;
+                charCode = (charCode << 8) + (firstCode + j);
+
+                // ---- Go to the CharacterCOde position in the Sub Array
+                // of the glyphIndexArray
+                // glyphIndexArray contains Unsigned Short so add (j * 2) bytes
+                // at the index position
+                int p = data.readUnsignedShort();
+                // ---- compute the glyphIndex
+                if (p > 0)
+                {
+                    p = (p + idDelta) % 65536;
+                }
+                glyphIdToCharacterCode[p] = charCode;
+                characterCodeToGlyphId.put(charCode, p);
+            }
+        }
+    }
+
+    /**
+     * Initialize the CMapEntry when it is a subtype 0.
+     * 
+     * @param ttf the TrueTypeFont instance holding the parsed data.
+     * @param data the data stream of the to be parsed ttf font
+     * @throws IOException If there is an error parsing the true type font.
+     */
+    protected void processSubtype0(TrueTypeFont ttf, TTFDataStream data) throws IOException
+    {
+        byte[] glyphMapping = data.read(256);
+        glyphIdToCharacterCode = new int[256];
+        for (int i = 0; i < glyphMapping.length; i++)
+        {
+            int glyphIndex = (glyphMapping[i] + 256) % 256;
+            glyphIdToCharacterCode[glyphIndex] = i;
+            characterCodeToGlyphId.put(i, glyphIndex);
+        }
+    }
+
+    /**
+     * @return Returns the glyphIdToCharacterCode.
+     */
+    public int[] getGlyphIdToCharacterCode()
+    {
+        return glyphIdToCharacterCode;
+    }
+
+    /**
+     * @param glyphIdToCharacterCodeValue The glyphIdToCharacterCode to set.
+     */
+    public void setGlyphIdToCharacterCode(int[] glyphIdToCharacterCodeValue)
+    {
+        glyphIdToCharacterCode = glyphIdToCharacterCodeValue;
+    }
+
+    /**
+     * @return Returns the platformEncodingId.
+     */
+    public int getPlatformEncodingId()
+    {
+        return platformEncodingId;
+    }
+
+    /**
+     * @param platformEncodingIdValue The platformEncodingId to set.
+     */
+    public void setPlatformEncodingId(int platformEncodingIdValue)
+    {
+        platformEncodingId = platformEncodingIdValue;
+    }
+
+    /**
+     * @return Returns the platformId.
+     */
+    public int getPlatformId()
+    {
+        return platformId;
+    }
+
+    /**
+     * @param platformIdValue The platformId to set.
+     */
+    public void setPlatformId(int platformIdValue)
+    {
+        platformId = platformIdValue;
+    }
+
+    /**
+     * Returns the GlyphId linked with the given character code.
+     * 
+     * @param characterCode the given character code to be mapped
+     * @return glyphId the corresponding glyph id for the given character code
+     */
+    public int getGlyphId(int characterCode)
+    {
+        if (characterCodeToGlyphId.containsKey(characterCode))
+        {
+            return characterCodeToGlyphId.get(characterCode);
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    /**
+     * 
+     * Class used to manage CMap - Format 2.
+     * 
+     */
+    private class SubHeader
+    {
+
+        private int firstCode;
+        private int entryCount;
+        /**
+         * used to compute the GlyphIndex : P = glyphIndexArray.SubArray[pos] GlyphIndex = P + idDelta % 65536.
+         */
+        private short idDelta;
+        /**
+         * Number of bytes to skip to reach the firstCode in the glyphIndexArray.
+         */
+        private int idRangeOffset;
+
+        private SubHeader(int firstCodeValue, int entryCountValue, short idDeltaValue, int idRangeOffsetValue)
+        {
+            firstCode = firstCodeValue;
+            entryCount = entryCountValue;
+            idDelta = idDeltaValue;
+            idRangeOffset = idRangeOffsetValue;
+        }
+
+        /**
+         * @return the firstCode
+         */
+        private int getFirstCode()
+        {
+            return firstCode;
+        }
+
+        /**
+         * @return the entryCount
+         */
+        private int getEntryCount()
+        {
+            return entryCount;
+        }
+
+        /**
+         * @return the idDelta
+         */
+        private short getIdDelta()
+        {
+            return idDelta;
+        }
+
+        /**
+         * @return the idRangeOffset
+         */
+        private int getIdRangeOffset()
+        {
+            return idRangeOffset;
+        }
+    }
 
 }

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPTable.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPTable.java?rev=1506164&r1=1506163&r2=1506164&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPTable.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/CMAPTable.java Tue Jul 23 17:13:11 2013
@@ -19,10 +19,10 @@ package org.apache.fontbox.ttf;
 import java.io.IOException;
 
 /**
- * A table in a true type font.
+ * The CMAP table of a true type font.
  * 
  * @author Ben Litchfield (ben@benlitchfield.com)
- * @version $Revision: 1.1 $
+ * 
  */
 public class CMAPTable extends TTFTable
 {
@@ -30,12 +30,22 @@ public class CMAPTable extends TTFTable
      * A tag used to identify this table.
      */
     public static final String TAG = "cmap";
-    
+
+    /**
+     * A constant for the platform.
+     */
+    public static final int PLATFORM_MISC = 0;
+
+    /**
+     * A constant for the platform.
+     */
+    public static final int PLATFORM_MACINTOSH = 1;
+
     /**
      * A constant for the platform.
      */
     public static final int PLATFORM_WINDOWS = 3;
-    
+
     /**
      * An encoding constant.
      */
@@ -64,9 +74,9 @@ public class CMAPTable extends TTFTable
      * An encoding constant.
      */
     public static final int ENCODING_JOHAB = 6;
-    
+
     private CMAPEncodingEntry[] cmaps;
-    
+
     /**
      * This will read the required data from the stream.
      * 
@@ -74,23 +84,24 @@ public class CMAPTable extends TTFTable
      * @param data The stream to read the data from.
      * @throws IOException If there is an error reading the data.
      */
-    public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+    public void initData(TrueTypeFont ttf, TTFDataStream data) throws IOException
     {
         int version = data.readUnsignedShort();
         int numberOfTables = data.readUnsignedShort();
-        cmaps = new CMAPEncodingEntry[ numberOfTables ];
-        for( int i=0; i< numberOfTables; i++ )
+        cmaps = new CMAPEncodingEntry[numberOfTables];
+        for (int i = 0; i < numberOfTables; i++)
         {
             CMAPEncodingEntry cmap = new CMAPEncodingEntry();
-            cmap.initData( ttf, data );
-            cmaps[i]=cmap;
+            cmap.initData(ttf, data);
+            cmaps[i] = cmap;
         }
-        for( int i=0; i< numberOfTables; i++ )
+        for (int i = 0; i < numberOfTables; i++)
         {
-            cmaps[i].initSubtable( ttf, data );
+            cmaps[i].initSubtable(ttf, data);
         }
-        
+
     }
+
     /**
      * @return Returns the cmaps.
      */
@@ -98,11 +109,12 @@ public class CMAPTable extends TTFTable
     {
         return cmaps;
     }
+
     /**
      * @param cmapsValue The cmaps to set.
      */
     public void setCmaps(CMAPEncodingEntry[] cmapsValue)
     {
-        this.cmaps = cmapsValue;
+        cmaps = cmapsValue;
     }
 }

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java?rev=1506164&r1=1506163&r2=1506164&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java Tue Jul 23 17:13:11 2013
@@ -17,6 +17,7 @@
 package org.apache.fontbox.ttf;
 
 import java.io.IOException;
+
 /**
  * A true type font file parser.
  * 
@@ -24,15 +25,17 @@ import java.io.IOException;
  * @version $Revision: 1.2 $
  */
 public class TTFParser extends AbstractTTFParser
-{   
-    public TTFParser() {
+{
+    public TTFParser()
+    {
         super(false);
     }
-       
-    public TTFParser(boolean isEmbedded) {
+
+    public TTFParser(boolean isEmbedded)
+    {
         super(isEmbedded);
     }
-        
+
     /**
      * A simple command line program to test parsing of a TTF file. <br/>
      * usage: java org.pdfbox.ttf.TTFParser &lt;ttf-file&gt;
@@ -41,29 +44,30 @@ public class TTFParser extends AbstractT
      * 
      * @throws IOException If there is an error while parsing the font file.
      */
-    public static void main( String[] args ) throws IOException
+    public static void main(String[] args) throws IOException
     {
-        if( args.length != 1 )
+        if (args.length != 1)
         {
-            System.err.println( "usage: java org.pdfbox.ttf.TTFParser <ttf-file>" );
-            System.exit( -1 );
+            System.err.println("usage: java org.pdfbox.ttf.TTFParser <ttf-file>");
+            System.exit(-1);
         }
         TTFParser parser = new TTFParser();
-        TrueTypeFont font = parser.parseTTF( args[0] );
-        System.out.println( "Font:" + font );
+        TrueTypeFont font = parser.parseTTF(args[0]);
+        System.out.println("Font:" + font);
     }
-    
+
     /**
      * {@inheritDoc}
      */
-    protected void parseTables(TrueTypeFont font, TTFDataStream raf) throws IOException 
+    protected void parseTables(TrueTypeFont font, TTFDataStream raf) throws IOException
     {
         super.parseTables(font, raf);
-        
+
         // check others mandatory tables
-        if ( font.getCMAP() == null ){
+        if (!isEmbedded && font.getCMAP() == null)
+        {
             throw new IOException("cmap is mandatory");
         }
     }
 
-}
\ No newline at end of file
+}