Index: pom.xml
===================================================================
--- pom.xml	(revision 826050)
+++ pom.xml	(working copy)
@@ -56,7 +56,7 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>3.8.2</version>
+      <version>4.4</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
@@ -66,11 +66,14 @@
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
-          <source>1.4</source>
-          <target>1.4</target>
+          <source>1.5</source>
+          <target>1.5</target>
         </configuration>
       </plugin>
       <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <version>2.0.0</version>
Index: src/main/java/org/apache/fontbox/cff/AFMFormatter.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/AFMFormatter.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/AFMFormatter.java	(revision 0)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.awt.geom.*;
+import java.io.*;
+import java.util.*;
+
+public class AFMFormatter {
+
+	private AFMFormatter(){
+	}
+
+	static
+	public byte[] format(CFFFont font) throws IOException {
+		DataOutput output = new DataOutput();
+
+		printFont(font, output);
+
+		return output.getBytes();
+	}
+
+	static
+	private void printFont(CFFFont font, DataOutput output) throws IOException {
+		printFontMetrics(font, output);
+	}
+
+	@SuppressWarnings (
+		value = {"unchecked"}
+	)
+	static
+	private void printFontMetrics(CFFFont font, DataOutput output) throws IOException {
+		output.println("StartFontMetrics 2.0");
+
+		output.println("FontName " + font.getName());
+		output.println("FullName " + font.getProperty("FullName"));
+		output.println("FamilyName " + font.getProperty("FamilyName"));
+		output.println("Weight " + font.getProperty("Weight"));
+
+		CFFEncoding encoding = font.getEncoding();
+		if(encoding.isFontSpecific()){
+			output.println("EncodingScheme FontSpecific");
+		}
+
+		List<Number> fontBBox = (List<Number>)font.getProperty("FontBBox");
+		output.println("FontBBox " + fontBBox.get(0) + " " + fontBBox.get(1) + " " + fontBBox.get(2) + " " + fontBBox.get(3));
+
+		printDirectionMetrics(font, output);
+		printCharMetrics(font, output);
+
+		output.println("EndFontMetrics");
+	}
+
+	static
+	private void printDirectionMetrics(CFFFont font, DataOutput output) throws IOException {
+		//output.println("StartDirection 0");
+
+		output.println("UnderlinePosition " + font.getProperty("UnderlinePosition"));
+		output.println("UnderlineThickness " + font.getProperty("UnderlineThickness"));
+		output.println("ItalicAngle " + font.getProperty("ItalicAngle"));
+		output.println("IsFixedPitch " + font.getProperty("isFixedPitch"));
+
+		//output.println("EndDirection");
+	}
+
+	static
+	private void printCharMetrics(CFFFont font, DataOutput output) throws IOException {
+		List<CharMetric> metrics;
+
+		try {
+			metrics = renderFont(font);
+		} catch(IOException ioe){
+			return;
+		}
+
+		output.println("StartCharMetrics " + metrics.size());
+
+		Collections.sort(metrics);
+
+		for(CharMetric metric : metrics){
+			output.print("C " + metric.code + " ;");
+			output.print(" ");
+			output.print("WX " + metric.width + " ;");
+			output.print(" ");
+			output.print("N " + metric.name + " ;");
+			output.print(" ");
+			output.print("B " + ((int)metric.bounds.getX() + " " + (int)metric.bounds.getY() + " " + (int)metric.bounds.getMaxX() + " " + (int)metric.bounds.getMaxY()) + " ;");
+
+			output.println();
+		}
+
+		output.println("EndCharMetrics");
+	}
+
+	static
+	private List<CharMetric> renderFont(CFFFont font) throws IOException {
+		List<CharMetric> metrics = new ArrayList<CharMetric>();
+
+		CharStringRenderer renderer = font.createRenderer();
+
+		Collection<CFFFont.Mapping> mappings = font.getMappings();
+		for(CFFFont.Mapping mapping : mappings){
+			CharMetric metric = new CharMetric();
+			metric.code = mapping.getCode();
+			metric.name = mapping.getName();
+
+			renderer.render(mapping.toType1Sequence());
+
+			metric.width = renderer.getWidth();
+			metric.bounds = renderer.getBounds();
+
+			metrics.add(metric);
+		}
+
+		return metrics;
+	}
+
+	static
+	private class CharMetric implements Comparable<CharMetric> {
+
+		private int code;
+
+		private String name;
+
+		private int width;
+
+		private Rectangle2D bounds;
+
+
+		public int compareTo(CharMetric that){
+			return this.code - that.code;
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFCharset.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFCharset.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFCharset.java	(revision 0)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+abstract
+public class CFFCharset {
+
+	private List<Entry> entries = new ArrayList<Entry>();
+
+
+	public boolean isFontSpecific(){
+		return false;
+	}
+
+	public int getSID(String name){
+
+		for(Entry entry : this.entries){
+
+			if((entry.name).equals(name)){
+				return entry.sid;
+			}
+		}
+
+		return -1;
+	}
+
+	public String getName(int sid){
+
+		for(Entry entry : this.entries){
+
+			if(entry.sid == sid){
+				return entry.name;
+			}
+		}
+
+		return null;
+	}
+
+	protected void register(int sid, String name){
+		Entry entry = new Entry();
+		entry.sid = sid;
+		entry.name = name;
+
+		this.entries.add(entry);
+	}
+
+	protected List<Entry> getEntries(){
+		return this.entries;
+	}
+
+	static
+	protected class Entry {
+
+		protected int sid;
+
+		protected String name;
+
+
+		@Override
+		public String toString(){
+			return "[sid=" + this.sid + ", name=" + this.name + "]";
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFDataInput.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFDataInput.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFDataInput.java	(revision 0)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+
+public class CFFDataInput extends DataInput {
+
+	public CFFDataInput(byte[] buffer){
+		super(buffer);
+	}
+
+	public int readCard8() throws IOException {
+		return readUnsignedByte();
+	}
+
+	public int readCard16() throws IOException {
+		return readUnsignedShort();
+	}
+
+	public int readOffset(int offSize) throws IOException {
+		int value = 0;
+
+		for(int i = 0; i < offSize; i++){
+			value = value << 8 | readUnsignedByte();
+		}
+
+		return value;
+	}
+
+	public int readOffSize() throws IOException {
+		return readUnsignedByte();
+	}
+
+	public int readSID() throws IOException {
+		return readUnsignedShort();
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFEncoding.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFEncoding.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFEncoding.java	(revision 0)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+abstract
+public class CFFEncoding {
+
+	private List<Entry> entries = new ArrayList<Entry>();
+
+
+	public boolean isFontSpecific(){
+		return false;
+	}
+
+	public int getCode(int sid){
+
+		for(Entry entry : this.entries){
+
+			if(entry.sid == sid){
+				return entry.code;
+			}
+		}
+
+		return -1;
+	}
+
+	public int getSID(int code){
+
+		for(Entry entry : this.entries){
+
+			if(entry.code == code){
+				return entry.sid;
+			}
+		}
+
+		return -1;
+	}
+
+	protected void register(int code, int sid){
+		Entry entry = new Entry();
+		entry.code = code;
+		entry.sid = sid;
+
+		this.entries.add(entry);
+	}
+
+	protected List<Entry> getEntries(){
+		return this.entries;
+	}
+
+	static
+	protected class Entry {
+
+		protected int code;
+
+		protected int sid;
+
+
+		@Override
+		public String toString(){
+			return "[code=" +this.code+", sid=" +this.sid+ "]";
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFFont.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFFont.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFFont.java	(revision 0)
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+public class CFFFont {
+
+	private String name = null;
+
+	private Map<String, Object> topDict = new LinkedHashMap<String, Object>();
+
+	private Map<String, Object> privateDict = new LinkedHashMap<String, Object>();
+
+	private CFFEncoding encoding = null;
+
+	private CFFCharset charset = null;
+
+	private Map<String, byte[]> charStringsDict = new LinkedHashMap<String, byte[]>();
+
+
+	public String getName(){
+		return this.name;
+	}
+
+	public void setName(String name){
+		this.name = name;
+	}
+
+	public Object getProperty(String name){
+
+		Object topDictValue = this.topDict.get(name);
+		if(topDictValue != null){
+			return topDictValue;
+		}
+
+		Object privateDictValue = this.privateDict.get(name);
+		if(privateDictValue != null){
+			return privateDictValue;
+		}
+
+		return null;
+	}
+
+	public Map<String, Object> getTopDict(){
+		return this.topDict;
+	}
+
+	public Map<String, Object> getPrivateDict(){
+		return this.privateDict;
+	}
+
+	public Collection<Mapping> getMappings(){
+		List<Mapping> mappings = new ArrayList<Mapping>();
+
+		Set<String> mappedNames = new HashSet<String>();
+
+		for(CFFEncoding.Entry entry : this.encoding.getEntries()){
+			String name = this.charset.getName(entry.sid);
+
+			// Predefined encoding
+			if(name == null){
+				continue;
+			}
+
+			byte[] bytes = this.charStringsDict.get(name);
+			if(bytes == null){
+				continue;
+			}
+
+			Mapping mapping = new Mapping();
+			mapping.setCode(entry.code);
+			mapping.setSID(entry.sid);
+			mapping.setName(name);
+			mapping.setBytes(bytes);
+
+			mappings.add(mapping);
+
+			mappedNames.add(name);
+		}
+
+		// XXX
+		int code = 256;
+
+		for(CFFCharset.Entry entry : this.charset.getEntries()){
+			String name = entry.name;
+
+			if(mappedNames.contains(name)){
+				continue;
+			}
+
+			byte[] bytes = this.charStringsDict.get(name);
+			if(bytes == null){
+				continue;
+			}
+
+			Mapping mapping = new Mapping();
+			mapping.setCode(code++);
+			mapping.setSID(entry.sid);
+			mapping.setName(name);
+			mapping.setBytes(bytes);
+
+			mappings.add(mapping);
+
+			mappedNames.add(name);
+		}
+
+		return mappings;
+	}
+
+	public CFFEncoding getEncoding(){
+		return this.encoding;
+	}
+
+	public void setEncoding(CFFEncoding encoding){
+		this.encoding = encoding;
+	}
+
+	public CFFCharset getCharset(){
+		return this.charset;
+	}
+
+	public void setCharset(CFFCharset charset){
+		this.charset = charset;
+	}
+
+	public Map<String, byte[]> getCharStringsDict(){
+		return this.charStringsDict;
+	}
+
+	public CharStringConverter createConverter(){
+		Number defaultWidthX = (Number)getProperty("defaultWidthX");
+		Number nominalWidthX = (Number)getProperty("nominalWidthX");
+
+		return new CharStringConverter(defaultWidthX.intValue(), nominalWidthX.intValue());
+	}
+
+	public CharStringRenderer createRenderer(){
+		return new CharStringRenderer();
+	}
+
+	@Override
+	public String toString(){
+		return getClass().getName() + "[name=" + this.name+ ", topDict=" +this.topDict+ ", privateDict=" +this.privateDict+ ", encoding=" +this.encoding+ ", charset="+this.charset+", charStringsDict=" +this.charStringsDict+ "]";
+	}
+
+	public class Mapping {
+
+		private int code;
+
+		private int sid;
+
+		private String name;
+
+		private byte[] bytes;
+
+
+		public List<Object> toType1Sequence() throws IOException {
+			CharStringConverter converter = createConverter();
+
+			return converter.convert(toType2Sequence());
+		}
+
+		public List<Object> toType2Sequence() throws IOException {
+			Type2CharStringParser parser = new Type2CharStringParser();
+
+			return parser.parse(getBytes());
+		}
+
+		public int getCode(){
+			return this.code;
+		}
+
+		private void setCode(int code){
+			this.code = code;
+		}
+
+		public int getSID(){
+			return this.sid;
+		}
+
+		private void setSID(int sid){
+			this.sid = sid;
+		}
+
+		public String getName(){
+			return this.name;
+		}
+
+		private void setName(String name){
+			this.name = name;
+		}
+
+		public byte[] getBytes(){
+			return this.bytes;
+		}
+
+		private void setBytes(byte[] bytes){
+			this.bytes = bytes;
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFOperator.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFOperator.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFOperator.java	(revision 0)
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+public class CFFOperator {
+
+	private Key key = null;
+
+	private String name = null;
+
+
+	private CFFOperator(Key key, String name){
+		setKey(key);
+		setName(name);
+	}
+
+	public Key getKey(){
+		return this.key;
+	}
+
+	private void setKey(Key key){
+		this.key = key;
+	}
+
+	public String getName(){
+		return this.name;
+	}
+
+	private void setName(String name){
+		this.name = name;
+	}
+
+	@Override
+	public String toString(){
+		return getName();
+	}
+
+	@Override
+	public int hashCode(){
+		return (this.getKey()).hashCode();
+	}
+
+	@Override
+	public boolean equals(Object object){
+
+		if(object instanceof CFFOperator){
+			CFFOperator that = (CFFOperator)object;
+
+			return (this.getKey()).equals(that.getKey());
+		}
+
+		return false;
+	}
+
+	static
+	private void register(Key key, String name){
+		CFFOperator operator = new CFFOperator(key, name);
+
+		KEY_MAP.put(key, operator);
+		NAME_MAP.put(name, operator);
+	}
+
+	static
+	public CFFOperator getOperator(Key key){
+		return KEY_MAP.get(key);
+	}
+
+	static
+	public CFFOperator getOperator(String name){
+		return NAME_MAP.get(name);
+	}
+
+	static
+	public class Key {
+
+		private int[] value = null;
+
+
+		public Key(int b0){
+			this(new int[]{b0});
+		}
+
+		public Key(int b0, int b1){
+			this(new int[]{b0, b1});
+		}
+
+		private Key(int[] value){
+			setValue(value);
+		}
+
+		public int[] getValue(){
+			return this.value;
+		}
+
+		private void setValue(int[] value){
+			this.value = value;
+		}
+
+		@Override
+		public String toString(){
+			return Arrays.toString(this.getValue());
+		}
+
+		@Override
+		public int hashCode(){
+			return Arrays.hashCode(this.getValue());
+		}
+
+		@Override
+		public boolean equals(Object object){
+
+			if(object instanceof Key){
+				Key that = (Key)object;
+
+				return Arrays.equals(this.getValue(), that.getValue());
+			}
+
+			return false;
+		}
+	}
+
+	private static Map<CFFOperator.Key, CFFOperator> KEY_MAP = new LinkedHashMap<CFFOperator.Key, CFFOperator>();
+	private static Map<String, CFFOperator> NAME_MAP = new LinkedHashMap<String, CFFOperator>();
+
+	static {
+		// Top DICT
+		register(new Key(0), "version");
+		register(new Key(1), "Notice");
+		register(new Key(12, 0), "Copyright");
+		register(new Key(2), "FullName");
+		register(new Key(3), "FamilyName");
+		register(new Key(4), "Weight");
+		register(new Key(12, 1), "isFixedPitch");
+		register(new Key(12, 2), "ItalicAngle");
+		register(new Key(12, 3), "UnderlinePosition");
+		register(new Key(12, 4), "UnderlineThickness");
+		register(new Key(12, 5), "PaintType");
+		register(new Key(12, 6), "CharstringType");
+		register(new Key(12, 7), "FontMatrix");
+		register(new Key(13), "UniqueID");
+		register(new Key(5), "FontBBox");
+		register(new Key(12, 8), "StrokeWidth");
+		register(new Key(14), "XUID");
+		register(new Key(15), "charset");
+		register(new Key(16), "Encoding");
+		register(new Key(17), "CharStrings");
+		register(new Key(18), "Private");
+		register(new Key(12, 20), "SyntheticBase");
+		register(new Key(12, 21), "PostScript");
+		register(new Key(12, 22), "BaseFontName");
+		register(new Key(12, 23), "BaseFontBlend");
+		register(new Key(12, 30), "ROS");
+		register(new Key(12, 31), "CIDFontVersion");
+		register(new Key(12, 32), "CIDFontRevision");
+		register(new Key(12, 33), "CIDFontType");
+		register(new Key(12, 34), "CIDCount");
+		register(new Key(12, 35), "UIDBase");
+		register(new Key(12, 36), "FDArray");
+		register(new Key(12, 37), "FDSelect");
+		register(new Key(12, 38), "FontName");
+
+		// Private DICT
+		register(new Key(6), "BlueValues");
+		register(new Key(7), "OtherBlues");
+		register(new Key(8), "FamilyBlues");
+		register(new Key(9), "FamilyOtherBlues");
+		register(new Key(12, 9), "BlueScale");
+		register(new Key(12, 10), "BlueShift");
+		register(new Key(12, 11), "BlueFuzz");
+		register(new Key(10), "StdHW");
+		register(new Key(11), "StdVW");
+		register(new Key(12, 12), "StemSnapH");
+		register(new Key(12, 13), "StemSnapV");
+		register(new Key(12, 14), "ForceBold");
+		register(new Key(12, 15), "LanguageGroup");
+		register(new Key(12, 16), "ExpansionFactor");
+		register(new Key(12, 17), "initialRandomSeed");
+		register(new Key(19), "Subrs");
+		register(new Key(20), "defaultWidthX");
+		register(new Key(21), "nominalWidthX");
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFParser.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFParser.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFParser.java	(revision 0)
@@ -0,0 +1,824 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.fontbox.cff.charset.*;
+import org.apache.fontbox.cff.encoding.*;
+
+public class CFFParser {
+
+	private CFFDataInput input = null;
+
+	private Header header = null;
+
+	private IndexData nameIndex = null;
+
+	private IndexData topDictIndex = null;
+
+	private IndexData stringIndex = null;
+
+	private IndexData globalSubrIndex = null;
+
+
+	public List<CFFFont> parse(byte[] bytes) throws IOException {
+		this.input = new CFFDataInput(bytes);
+
+		this.header = readHeader(this.input);
+
+		this.nameIndex = readIndexData(this.input);
+		this.topDictIndex = readIndexData(this.input);
+		this.stringIndex = readIndexData(this.input);
+		this.globalSubrIndex = readIndexData(this.input);
+
+		List<CFFFont> fonts = new ArrayList<CFFFont>();
+
+		for(int i = 0; i < this.nameIndex.count; i++){
+			CFFFont font = parseFont(i);
+
+			fonts.add(font);
+		}
+
+		return fonts;
+	}
+
+	static
+	private Header readHeader(CFFDataInput input) throws IOException {
+		Header header = new Header();
+
+		header.major = input.readCard8();
+		header.minor = input.readCard8();
+		header.hdrSize = input.readCard8();
+		header.offSize = input.readOffSize();
+
+		return header;
+	}
+
+	static
+	private IndexData readIndexData(CFFDataInput input) throws IOException {
+		IndexData index = new IndexData();
+
+		index.count = input.readCard16();
+		if(index.count == 0){
+			return index;
+		}
+
+		index.offSize = input.readOffSize();
+		index.offset = new int[index.count + 1];
+		for(int i = 0; i < index.offset.length; i++){
+			index.offset[i] = input.readOffset(index.offSize);
+		}
+
+		index.data = new int[index.offset[index.offset.length - 1] - index.offset[0]];
+		for(int i = 0; i < index.data.length; i++){
+			index.data[i] = input.readCard8();
+		}
+
+		return index;
+	}
+
+	static
+	private DictData readDictData(CFFDataInput input) throws IOException {
+		DictData dict = new DictData();
+		dict.entries = new ArrayList<DictData.Entry>();
+
+		while(input.hasRemaining()){
+			DictData.Entry entry = readEntry(input);
+
+			dict.entries.add(entry);
+		}
+
+		return dict;
+	}
+
+	static
+	private DictData.Entry readEntry(CFFDataInput input) throws IOException {
+		DictData.Entry entry = new DictData.Entry();
+
+		while(true){
+			int b0 = input.readUnsignedByte();
+
+			if(b0 >= 0 && b0 <= 21){
+				entry.operator = readOperator(input, b0);
+
+				break;
+			} else
+
+			if(b0 == 28 || b0 == 29){
+				entry.operands.add(readIntegerNumber(input, b0));
+			} else
+
+			if(b0 == 30){
+				entry.operands.add(readRealNumber(input, b0));
+			} else
+
+			if(b0 >= 32 && b0 <= 254){
+				entry.operands.add(readIntegerNumber(input, b0));
+			} else
+
+			{
+				throw new IllegalArgumentException();
+			}
+		}
+
+		return entry;
+	}
+
+	static
+	private CFFOperator readOperator(CFFDataInput input, int b0) throws IOException {
+		CFFOperator.Key key = readOperatorKey(input, b0);
+
+		return CFFOperator.getOperator(key);
+	}
+
+	static
+	private CFFOperator.Key readOperatorKey(CFFDataInput input, int b0) throws IOException {
+
+		if(b0 == 12){
+			int b1 = input.readUnsignedByte();
+
+			return new CFFOperator.Key(b0, b1);
+		}
+
+		return new CFFOperator.Key(b0);
+	}
+
+	static
+	private Integer readIntegerNumber(CFFDataInput input, int b0) throws IOException {
+
+		if(b0 == 28){
+			int b1 = input.readUnsignedByte();
+			int b2 = input.readUnsignedByte();
+
+			return Integer.valueOf((short)(b1 << 8 | b2));
+		} else
+
+		if(b0 == 29){
+			int b1 = input.readUnsignedByte();
+			int b2 = input.readUnsignedByte();
+			int b3 = input.readUnsignedByte();
+			int b4 = input.readUnsignedByte();
+
+			return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4);
+		} else
+
+		if(b0 >= 32 && b0 <= 246){
+			return Integer.valueOf(b0 - 139);
+		} else
+
+		if(b0 >= 247 && b0 <= 250){
+			int b1 = input.readUnsignedByte();
+
+			return Integer.valueOf((b0 - 247) * 256 + b1 + 108);
+		} else
+
+		if(b0 >= 251 && b0 <= 254){
+			int b1 = input.readUnsignedByte();
+
+			return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108);
+		} else
+
+		{
+			throw new IllegalArgumentException();
+		}
+	}
+
+	static
+	private Double readRealNumber(CFFDataInput input, int b0) throws IOException {
+		StringBuffer sb = new StringBuffer();
+
+		boolean done = false;
+
+		while(!done){
+			int b = input.readUnsignedByte();
+
+			int[] nibbles = {b / 16, b % 16};
+			for(int nibble : nibbles){
+
+				switch(nibble){
+					case 0x0:
+					case 0x1:
+					case 0x2:
+					case 0x3:
+					case 0x4:
+					case 0x5:
+					case 0x6:
+					case 0x7:
+					case 0x8:
+					case 0x9:
+						sb.append(nibble);
+						break;
+					case 0xa:
+						sb.append(".");
+						break;
+					case 0xb:
+						sb.append("E");
+						break;
+					case 0xc:
+						sb.append("E-");
+						break;
+					case 0xd:
+						break;
+					case 0xe:
+						sb.append("-");
+						break;
+					case 0xf:
+						done = true;
+						break;
+					default:
+						throw new IllegalArgumentException();
+				}
+			}
+		}
+
+		return Double.valueOf(sb.toString());
+	}
+
+	private CFFFont parseFont(int index) throws IOException {
+		CFFFont font = new CFFFont();
+
+		DataInput nameInput = new DataInput(this.nameIndex.getBytes(index));
+
+		String name = nameInput.getString();
+		font.setName(name);
+
+		CFFDataInput topDictInput = new CFFDataInput(this.topDictIndex.getBytes(index));
+		DictData topDict = readDictData(topDictInput);
+
+		DictData.Entry syntheticBaseEntry = topDict.getEntry("SyntheticBase");
+		if(syntheticBaseEntry != null){
+			throw new IOException("Synthetic Fonts are not supported");
+		}
+
+		DictData.Entry rosEntry = topDict.getEntry("ROS");
+		if(rosEntry != null){
+			throw new IOException("CID-keyed Fonts are not supported");
+		}
+
+		copyString(topDict, font.getTopDict(), "version");
+		copyString(topDict, font.getTopDict(), "Notice");
+		copyString(topDict, font.getTopDict(), "Copyright");
+		copyString(topDict, font.getTopDict(), "FullName");
+		copyString(topDict, font.getTopDict(), "FamilyName");
+		copyString(topDict, font.getTopDict(), "Weight");
+		copyBoolean(topDict, font.getTopDict(), "isFixedPitch", Boolean.valueOf(false));
+		copyNumber(topDict, font.getTopDict(), "ItalicAngle", Integer.valueOf(0));
+		copyNumber(topDict, font.getTopDict(), "UnderlinePosition", Integer.valueOf(-100));
+		copyNumber(topDict, font.getTopDict(), "UnderlineThickness", Integer.valueOf(50));
+		copyNumber(topDict, font.getTopDict(), "PaintType", Integer.valueOf(0));
+		copyNumber(topDict, font.getTopDict(), "CharstringType", Integer.valueOf(2));
+		copyArray(topDict, font.getTopDict(), "FontMatrix", Arrays.<Number>asList(Double.valueOf(0.001), Double.valueOf(0), Double.valueOf(0), Double.valueOf(0.001), Double.valueOf(0), Double.valueOf(0)));
+		copyNumber(topDict, font.getTopDict(), "UniqueID", null);
+		copyArray(topDict, font.getTopDict(), "FontBBox", Arrays.<Number>asList(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)));
+		copyNumber(topDict, font.getTopDict(), "StrokeWidth", Integer.valueOf(0));
+		copyArray(topDict, font.getTopDict(), "XUID", null);
+
+		DictData.Entry charStringsEntry = topDict.getEntry("CharStrings");
+
+		int charStringsOffset = charStringsEntry.getNumber(0).intValue();
+		this.input.setPosition(charStringsOffset);
+
+		IndexData charStringsIndex = readIndexData(this.input);
+
+		DictData.Entry charsetEntry = topDict.getEntry("charset");
+
+		CFFCharset charset;
+
+		int charsetId = (charsetEntry != null ? charsetEntry.getNumber(0).intValue() : 0);
+		if(charsetId == 0){
+			charset = CFFISOAdobeCharset.getInstance();
+		} else
+
+		if(charsetId == 1){
+			charset = CFFExpertCharset.getInstance();
+		} else
+
+		if(charsetId == 2){
+			charset = CFFExpertSubsetCharset.getInstance();
+		} else
+
+		{
+			this.input.setPosition(charsetId);
+			charset = readCharset(this.input, charStringsIndex.count);
+		}
+
+		font.setCharset(charset);
+
+		font.getCharStringsDict().put(".notdef", charStringsIndex.getBytes(0));
+
+		int[] gids = new int[charStringsIndex.count];
+
+		List<CFFCharset.Entry> glyphEntries = charset.getEntries();
+		for(int i = 0; i < glyphEntries.size(); i++){
+			CFFCharset.Entry glyphEntry = glyphEntries.get(i);
+
+			gids[i] = glyphEntry.sid;
+
+			font.getCharStringsDict().put(glyphEntry.name, charStringsIndex.getBytes(i + 1));
+		}
+
+		DictData.Entry encodingEntry = topDict.getEntry("Encoding");
+
+		CFFEncoding encoding;
+
+		int encodingId = (encodingEntry != null ? encodingEntry.getNumber(0).intValue() : 0);
+		if(encodingId == 0){
+			encoding = CFFStandardEncoding.getInstance();
+		} else
+
+		if(encodingId == 1){
+			encoding = CFFExpertEncoding.getInstance();
+		} else
+
+		{
+			this.input.setPosition(encodingId);
+			encoding = readEncoding(this.input, gids);
+		}
+
+		font.setEncoding(encoding);
+
+		DictData.Entry privateEntry = topDict.getEntry("Private");
+
+		int privateOffset = privateEntry.getNumber(1).intValue();
+		this.input.setPosition(privateOffset);
+
+		int privateSize = privateEntry.getNumber(0).intValue();
+		CFFDataInput privateDictData = new CFFDataInput(this.input.readBytes(privateSize));
+
+		DictData privateDict = readDictData(privateDictData);
+
+		copyDelta(privateDict, font.getPrivateDict(), "BlueValues", null);
+		copyDelta(privateDict, font.getPrivateDict(), "OtherBlues", null);
+		copyDelta(privateDict, font.getPrivateDict(), "FamilyBlues", null);
+		copyDelta(privateDict, font.getPrivateDict(), "FamilyOtherBlues", null);
+		copyNumber(privateDict, font.getPrivateDict(), "BlueScale", Double.valueOf(0.039625));
+		copyNumber(privateDict, font.getPrivateDict(), "BlueShift", Integer.valueOf(7));
+		copyNumber(privateDict, font.getPrivateDict(), "BlueFuzz", Integer.valueOf(1));
+		copyNumber(privateDict, font.getPrivateDict(), "StdHW", null);
+		copyNumber(privateDict, font.getPrivateDict(), "StdVW", null);
+		copyDelta(privateDict, font.getPrivateDict(), "StemSnapH", null);
+		copyDelta(privateDict, font.getPrivateDict(), "StemSnapV", null);
+		copyBoolean(privateDict, font.getPrivateDict(), "ForceBold", Boolean.valueOf(false));
+		copyNumber(privateDict, font.getPrivateDict(), "LanguageGroup", Integer.valueOf(0));
+		copyNumber(privateDict, font.getPrivateDict(), "ExpansionFactor", Double.valueOf(0.06));
+		copyNumber(privateDict, font.getPrivateDict(), "initialRandomSeed", Integer.valueOf(0));
+		copyNumber(privateDict, font.getPrivateDict(), "defaultWidthX", Integer.valueOf(0));
+		copyNumber(privateDict, font.getPrivateDict(), "nominalWidthX", Integer.valueOf(0));
+
+		return font;
+	}
+
+	private String readString(int index) throws IOException {
+
+		if(index >= 0 && index <= 390){
+			return CFFStandardString.getName(index);
+		}
+
+		DataInput input = new DataInput(this.stringIndex.getBytes(index - 391));
+
+		return input.getString();
+	}
+
+	private void copyString(DictData dict, Map<String, Object> map, String name) throws IOException {
+		DictData.Entry entry = dict.getEntry(name);
+
+		String value = entry != null ? readString(entry.getNumber(0).intValue()) : null;
+		if(value != null){
+			map.put(name, value);
+		}
+	}
+
+	private void copyBoolean(DictData dict, Map<String, Object> map, String name, Boolean defaultValue){
+		DictData.Entry entry = dict.getEntry(name);
+
+		Boolean value = (entry != null ? entry.getBoolean(0) : defaultValue);
+		if(value != null){
+			map.put(name, value);
+		}
+	}
+
+	private void copyNumber(DictData dict, Map<String, Object> map, String name, Number defaultValue){
+		DictData.Entry entry = dict.getEntry(name);
+
+		Number value = (entry != null ? entry.getNumber(0) : defaultValue);
+		if(value != null){
+			map.put(name, value);
+		}
+	}
+
+	private void copyArray(DictData dict, Map<String, Object> map, String name, List<Number> defaultValue){
+		DictData.Entry entry = dict.getEntry(name);
+
+		List<Number> value = (entry != null ? entry.getArray() : defaultValue);
+		if(value != null){
+			map.put(name, value);
+		}
+	}
+
+	private void copyDelta(DictData dict, Map<String, Object> map, String name, List<Number> defaultValue){
+		DictData.Entry entry = dict.getEntry(name);
+
+		List<Number> value = (entry != null ? entry.getArray() : defaultValue);
+		if(value != null){
+			map.put(name, value);
+		}
+	}
+
+	private CFFEncoding readEncoding(CFFDataInput input, int[] gids) throws IOException {
+		int format = input.readCard8();
+		int baseFormat = format & 0x7f;
+
+		if(baseFormat == 0){
+			return readFormat0Encoding(input, format, gids);
+		} else
+
+		if(baseFormat == 1){
+			return readFormat1Encoding(input, format, gids);
+		} else
+
+		{
+			throw new IllegalArgumentException();
+		}
+	}
+
+	private Format0Encoding readFormat0Encoding(CFFDataInput input, int format, int[] gids) throws IOException {
+		Format0Encoding encoding = new Format0Encoding();
+		encoding.format = format;
+		encoding.nCodes = input.readCard8();
+
+		encoding.code = new int[encoding.nCodes];
+		for(int i = 0; i < encoding.code.length; i++){
+			encoding.code[i] = input.readCard8();
+
+			encoding.register(encoding.code[i], gids[i]);
+		}
+
+		if((format & 0x80) != 0){
+			readSupplement(input, encoding);
+		}
+
+		return encoding;
+	}
+
+	private Format1Encoding readFormat1Encoding(CFFDataInput input, int format, int[] gids) throws IOException {
+		Format1Encoding encoding = new Format1Encoding();
+		encoding.format = format;
+		encoding.nRanges = input.readCard8();
+
+		int count = 0;
+
+		encoding.range = new Format1Encoding.Range1[encoding.nRanges];
+		for(int i = 0; i < encoding.range.length; i++){
+			Format1Encoding.Range1 range = new Format1Encoding.Range1();
+			range.first = input.readCard8();
+			range.nLeft = input.readCard8();
+
+			encoding.range[i] = range;
+
+			for(int j = 0; j < (1 + range.nLeft); j++){
+				encoding.register(range.first + j, gids[count + j]);
+			}
+
+			count += (1 + range.nLeft);
+		}
+
+		if((format & 0x80) != 0){
+			readSupplement(input, encoding);
+		}
+
+		return encoding;
+	}
+
+	private void readSupplement(CFFDataInput input, EmbeddedEncoding encoding) throws IOException {
+		encoding.nSups = input.readCard8();
+
+		encoding.supplement = new EmbeddedEncoding.Supplement[encoding.nSups];
+		for(int i = 0; i < encoding.supplement.length; i++){
+			EmbeddedEncoding.Supplement supplement = new EmbeddedEncoding.Supplement();
+			supplement.code = input.readCard8();
+			supplement.glyph = input.readSID();
+
+			encoding.supplement[i] = supplement;
+		}
+	}
+
+	private CFFCharset readCharset(CFFDataInput input, int nGlyphs) throws IOException {
+		int format = input.readCard8();
+
+		if(format == 0){
+			return readFormat0Charset(input, format, nGlyphs);
+		} else
+
+		if(format == 1){
+			return readFormat1Charset(input, format, nGlyphs);
+		} else
+
+		{
+			throw new IllegalArgumentException();
+		}
+	}
+
+	private Format0Charset readFormat0Charset(CFFDataInput input, int format, int nGlyphs) throws IOException {
+		Format0Charset charset = new Format0Charset();
+		charset.format = format;
+
+		charset.glyph = new int[nGlyphs - 1];
+		for(int i = 0; i < charset.glyph.length; i++){
+			charset.glyph[i] = input.readSID();
+
+			charset.register(charset.glyph[i], readString(charset.glyph[i]));
+		}
+
+		return charset;
+	}
+
+	private Format1Charset readFormat1Charset(CFFDataInput input, int format, int nGlyphs) throws IOException {
+		Format1Charset charset = new Format1Charset();
+		charset.format = format;
+
+		charset.range = new Format1Charset.Range1[0];
+		for(int i = 0; i < nGlyphs - 1; ){
+			Format1Charset.Range1[] newRange = new Format1Charset.Range1[charset.range.length + 1];
+			System.arraycopy(charset.range, 0, newRange, 0, charset.range.length);
+			charset.range = newRange;
+
+			Format1Charset.Range1 range = new Format1Charset.Range1();
+			range.first = input.readSID();
+			range.nLeft = input.readCard8();
+
+			charset.range[charset.range.length - 1] = range;
+
+			for(int j = 0; j < (1 + range.nLeft); j++){
+				charset.register(range.first + j, readString(range.first + j));
+			}
+
+			i += (1 + range.nLeft);
+		}
+
+		return charset;
+	}
+
+	static
+	private class Header {
+
+		private int major;
+
+		private int minor;
+
+		private int hdrSize;
+
+		private int offSize;
+
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[major=" + this.major + ", minor=" + this.minor + ", hdrSize=" + this.hdrSize + ", offSize=" + this.offSize + "]";
+		}
+	}
+
+	static
+	private class IndexData {
+
+		private int count;
+
+		private int offSize;
+
+		private int[] offset;
+
+		private int[] data;
+
+
+		public byte[] getBytes(int index){
+			int length = (this.offset[index + 1] - this.offset[index]);
+
+			byte[] bytes = new byte[length];
+
+			for(int i = 0; i < length; i++){
+				bytes[i] = (byte)this.data[(this.offset[index] - 1) + i];
+			}
+
+			return bytes;
+		}
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[count=" +this.count+ ", offSize=" +this.offSize+ ", offset=" +Arrays.toString(this.offset)+ ", data=" +Arrays.toString(this.data)+ "]";
+		}
+	}
+
+	static
+	private class DictData {
+
+		private List<Entry> entries = null;
+
+
+		public Entry getEntry(CFFOperator.Key key){
+			return getEntry(CFFOperator.getOperator(key));
+		}
+
+		public Entry getEntry(String name){
+			return getEntry(CFFOperator.getOperator(name));
+		}
+
+		private Entry getEntry(CFFOperator operator){
+
+			for(Entry entry : this.entries){
+
+				if((entry.operator).equals(operator)){
+					return entry;
+				}
+			}
+
+			return null;
+		}
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[entries=" + this.entries + "]";
+		}
+
+		static
+		private class Entry {
+
+			private List<Number> operands = new ArrayList<Number>();
+
+			private CFFOperator operator = null;
+
+
+			public Number getNumber(int index){
+				return this.operands.get(index);
+			}
+
+			public Boolean getBoolean(int index){
+				Number operand = this.operands.get(index);
+
+				if(operand instanceof Integer){
+
+					switch(operand.intValue()){
+						case 0:
+							return Boolean.FALSE;
+						case 1:
+							return Boolean.TRUE;
+						default:
+							break;
+					}
+				}
+
+				throw new IllegalArgumentException();
+			}
+
+			public Integer getSID(int index){
+				Number operand = this.operands.get(index);
+
+				if(operand instanceof Integer){
+					return (Integer)operand;
+				}
+
+				throw new IllegalArgumentException();
+			}
+
+			public List<Number> getArray(){
+				return this.operands;
+			}
+
+			public List<Number> getDelta(){
+				return this.operands;
+			}
+
+			@Override
+			public String toString(){
+				return getClass().getName() + "[operands=" + this.operands + ", operator=" +this.operator+ "]";
+			}
+		}
+	}
+
+	static
+	abstract
+	private class EmbeddedEncoding extends CFFEncoding {
+
+		private int nSups;
+
+		private Supplement[] supplement;
+
+
+		@Override
+		public boolean isFontSpecific(){
+			return true;
+		}
+
+		static
+		private class Supplement {
+
+			private int code;
+
+			private int glyph;
+
+
+			@Override
+			public String toString(){
+				return getClass().getName() + "[code=" + this.code + ", glyph=" + this.glyph + "]";
+			}
+		}
+	}
+
+	static
+	private class Format0Encoding extends EmbeddedEncoding {
+
+		private int format;
+
+		private int nCodes;
+
+		private int[] code;
+
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[format=" +this.format+ ", nCodes=" +this.nCodes+ ", code=" +Arrays.toString(this.code)+ ", supplement=" +Arrays.toString(super.supplement)+ "]";
+		}
+	}
+
+	static
+	private class Format1Encoding extends EmbeddedEncoding {
+
+		private int format;
+
+		private int nRanges;
+
+		private Range1[] range;
+
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[format=" +this.format+ ", nRanges=" +this.nRanges+ ", range=" +Arrays.toString(this.range)+ ", supplement=" +Arrays.toString(super.supplement)+ "]";
+		}
+
+		static
+		private class Range1 {
+
+			private int first;
+
+			private int nLeft;
+
+
+			@Override
+			public String toString(){
+				return getClass().getName() + "[first=" +this.first+ ", nLeft=" +this.nLeft+ "]";
+			}
+		}
+	}
+
+	static
+	abstract
+	private class EmbeddedCharset extends CFFCharset {
+
+		@Override
+		public boolean isFontSpecific(){
+			return true;
+		}
+	}
+
+	static
+	private class Format0Charset extends EmbeddedCharset {
+
+		private int format;
+
+		private int[] glyph;
+
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[format=" +this.format+ ", glyph=" +Arrays.toString(this.glyph)+ "]";
+		}
+	}
+
+	static
+	private class Format1Charset extends EmbeddedCharset {
+
+		private int format;
+
+		private Range1[] range;
+
+
+		@Override
+		public String toString(){
+			return getClass().getName() + "[format=" +this.format+ ", range=" +Arrays.toString(this.range)+ "]";
+		}
+
+		static
+		private class Range1 {
+
+			private int first;
+
+			private int nLeft;
+
+
+			@Override
+			public String toString(){
+				return getClass().getName() + "[first=" +this.first+ ", nLeft=" +this.nLeft+ "]";
+			}
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CFFStandardString.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CFFStandardString.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CFFStandardString.java	(revision 0)
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+public class CFFStandardString {
+
+	private CFFStandardString(){
+	}
+
+	static
+	public String getName(int sid){
+		return array[sid];
+	}
+
+	private static final String[] array = {
+		".notdef",
+		"space",
+		"exclam",
+		"quotedbl",
+		"numbersign",
+		"dollar",
+		"percent",
+		"ampersand",
+		"quoteright",
+		"parenleft",
+		"parenright",
+		"asterisk",
+		"plus",
+		"comma",
+		"hyphen",
+		"period",
+		"slash",
+		"zero",
+		"one",
+		"two",
+		"three",
+		"four",
+		"five",
+		"six",
+		"seven",
+		"eight",
+		"nine",
+		"colon",
+		"semicolon",
+		"less",
+		"equal",
+		"greater",
+		"question",
+		"at",
+		"A",
+		"B",
+		"C",
+		"D",
+		"E",
+		"F",
+		"G",
+		"H",
+		"I",
+		"J",
+		"K",
+		"L",
+		"M",
+		"N",
+		"O",
+		"P",
+		"Q",
+		"R",
+		"S",
+		"T",
+		"U",
+		"V",
+		"W",
+		"X",
+		"Y",
+		"Z",
+		"bracketleft",
+		"backslash",
+		"bracketright",
+		"asciicircum",
+		"underscore",
+		"quoteleft",
+		"a",
+		"b",
+		"c",
+		"d",
+		"e",
+		"f",
+		"g",
+		"h",
+		"i",
+		"j",
+		"k",
+		"l",
+		"m",
+		"n",
+		"o",
+		"p",
+		"q",
+		"r",
+		"s",
+		"t",
+		"u",
+		"v",
+		"w",
+		"x",
+		"y",
+		"z",
+		"braceleft",
+		"bar",
+		"braceright",
+		"asciitilde",
+		"exclamdown",
+		"cent",
+		"sterling",
+		"fraction",
+		"yen",
+		"florin",
+		"section",
+		"currency",
+		"quotesingle",
+		"quotedblleft",
+		"guillemotleft",
+		"guilsinglleft",
+		"guilsinglright",
+		"fi",
+		"fl",
+		"endash",
+		"dagger",
+		"daggerdbl",
+		"periodcentered",
+		"paragraph",
+		"bullet",
+		"quotesinglbase",
+		"quotedblbase",
+		"quotedblright",
+		"guillemotright",
+		"ellipsis",
+		"perthousand",
+		"questiondown",
+		"grave",
+		"acute",
+		"circumflex",
+		"tilde",
+		"macron",
+		"breve",
+		"dotaccent",
+		"dieresis",
+		"ring",
+		"cedilla",
+		"hungarumlaut",
+		"ogonek",
+		"caron",
+		"emdash",
+		"AE",
+		"ordfeminine",
+		"Lslash",
+		"Oslash",
+		"OE",
+		"ordmasculine",
+		"ae",
+		"dotlessi",
+		"lslash",
+		"oslash",
+		"oe",
+		"germandbls",
+		"onesuperior",
+		"logicalnot",
+		"mu",
+		"trademark",
+		"Eth",
+		"onehalf",
+		"plusminus",
+		"Thorn",
+		"onequarter",
+		"divide",
+		"brokenbar",
+		"degree",
+		"thorn",
+		"threequarters",
+		"twosuperior",
+		"registered",
+		"minus",
+		"eth",
+		"multiply",
+		"threesuperior",
+		"copyright",
+		"Aacute",
+		"Acircumflex",
+		"Adieresis",
+		"Agrave",
+		"Aring",
+		"Atilde",
+		"Ccedilla",
+		"Eacute",
+		"Ecircumflex",
+		"Edieresis",
+		"Egrave",
+		"Iacute",
+		"Icircumflex",
+		"Idieresis",
+		"Igrave",
+		"Ntilde",
+		"Oacute",
+		"Ocircumflex",
+		"Odieresis",
+		"Ograve",
+		"Otilde",
+		"Scaron",
+		"Uacute",
+		"Ucircumflex",
+		"Udieresis",
+		"Ugrave",
+		"Yacute",
+		"Ydieresis",
+		"Zcaron",
+		"aacute",
+		"acircumflex",
+		"adieresis",
+		"agrave",
+		"aring",
+		"atilde",
+		"ccedilla",
+		"eacute",
+		"ecircumflex",
+		"edieresis",
+		"egrave",
+		"iacute",
+		"icircumflex",
+		"idieresis",
+		"igrave",
+		"ntilde",
+		"oacute",
+		"ocircumflex",
+		"odieresis",
+		"ograve",
+		"otilde",
+		"scaron",
+		"uacute",
+		"ucircumflex",
+		"udieresis",
+		"ugrave",
+		"yacute",
+		"ydieresis",
+		"zcaron",
+		"exclamsmall",
+		"Hungarumlautsmall",
+		"dollaroldstyle",
+		"dollarsuperior",
+		"ampersandsmall",
+		"Acutesmall",
+		"parenleftsuperior",
+		"parenrightsuperior",
+		"twodotenleader",
+		"onedotenleader",
+		"zerooldstyle",
+		"oneoldstyle",
+		"twooldstyle",
+		"threeoldstyle",
+		"fouroldstyle",
+		"fiveoldstyle",
+		"sixoldstyle",
+		"sevenoldstyle",
+		"eightoldstyle",
+		"nineoldstyle",
+		"commasuperior",
+		"threequartersemdash",
+		"periodsuperior",
+		"questionsmall",
+		"asuperior",
+		"bsuperior",
+		"centsuperior",
+		"dsuperior",
+		"esuperior",
+		"isuperior",
+		"lsuperior",
+		"msuperior",
+		"nsuperior",
+		"osuperior",
+		"rsuperior",
+		"ssuperior",
+		"tsuperior",
+		"ff",
+		"ffi",
+		"ffl",
+		"parenleftinferior",
+		"parenrightinferior",
+		"Circumflexsmall",
+		"hyphensuperior",
+		"Gravesmall",
+		"Asmall",
+		"Bsmall",
+		"Csmall",
+		"Dsmall",
+		"Esmall",
+		"Fsmall",
+		"Gsmall",
+		"Hsmall",
+		"Ismall",
+		"Jsmall",
+		"Ksmall",
+		"Lsmall",
+		"Msmall",
+		"Nsmall",
+		"Osmall",
+		"Psmall",
+		"Qsmall",
+		"Rsmall",
+		"Ssmall",
+		"Tsmall",
+		"Usmall",
+		"Vsmall",
+		"Wsmall",
+		"Xsmall",
+		"Ysmall",
+		"Zsmall",
+		"colonmonetary",
+		"onefitted",
+		"rupiah",
+		"Tildesmall",
+		"exclamdownsmall",
+		"centoldstyle",
+		"Lslashsmall",
+		"Scaronsmall",
+		"Zcaronsmall",
+		"Dieresissmall",
+		"Brevesmall",
+		"Caronsmall",
+		"Dotaccentsmall",
+		"Macronsmall",
+		"figuredash",
+		"hypheninferior",
+		"Ogoneksmall",
+		"Ringsmall",
+		"Cedillasmall",
+		"questiondownsmall",
+		"oneeighth",
+		"threeeighths",
+		"fiveeighths",
+		"seveneighths",
+		"onethird",
+		"twothirds",
+		"zerosuperior",
+		"foursuperior",
+		"fivesuperior",
+		"sixsuperior",
+		"sevensuperior",
+		"eightsuperior",
+		"ninesuperior",
+		"zeroinferior",
+		"oneinferior",
+		"twoinferior",
+		"threeinferior",
+		"fourinferior",
+		"fiveinferior",
+		"sixinferior",
+		"seveninferior",
+		"eightinferior",
+		"nineinferior",
+		"centinferior",
+		"dollarinferior",
+		"periodinferior",
+		"commainferior",
+		"Agravesmall",
+		"Aacutesmall",
+		"Acircumflexsmall",
+		"Atildesmall",
+		"Adieresissmall",
+		"Aringsmall",
+		"AEsmall",
+		"Ccedillasmall",
+		"Egravesmall",
+		"Eacutesmall",
+		"Ecircumflexsmall",
+		"Edieresissmall",
+		"Igravesmall",
+		"Iacutesmall",
+		"Icircumflexsmall",
+		"Idieresissmall",
+		"Ethsmall",
+		"Ntildesmall",
+		"Ogravesmall",
+		"Oacutesmall",
+		"Ocircumflexsmall",
+		"Otildesmall",
+		"Odieresissmall",
+		"OEsmall",
+		"Oslashsmall",
+		"Ugravesmall",
+		"Uacutesmall",
+		"Ucircumflexsmall",
+		"Udieresissmall",
+		"Yacutesmall",
+		"Thornsmall",
+		"Ydieresissmall",
+		"001.000",
+		"001.001",
+		"001.002",
+		"001.003",
+		"Black",
+		"Bold",
+		"Book",
+		"Light",
+		"Medium",
+		"Regular",
+		"Roman",
+		"Semibold",
+	};
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/charset/CFFExpertCharset.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/charset/CFFExpertCharset.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/charset/CFFExpertCharset.java	(revision 0)
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff.charset;
+
+import org.apache.fontbox.cff.*;
+
+public class CFFExpertCharset extends CFFCharset {
+
+	private CFFExpertCharset(){
+	}
+
+	static
+	public CFFExpertCharset getInstance(){
+		return CFFExpertCharset.instance;
+	}
+
+	private static final CFFExpertCharset instance = new CFFExpertCharset();
+
+	static {
+		instance.register(1, "space");
+		instance.register(13, "comma");
+		instance.register(14, "hyphen");
+		instance.register(15, "period");
+		instance.register(27, "colon");
+		instance.register(28, "semicolon");
+		instance.register(99, "fraction");
+		instance.register(109, "fi");
+		instance.register(110, "fl");
+		instance.register(150, "onesuperior");
+		instance.register(155, "onehalf");
+		instance.register(158, "onequarter");
+		instance.register(163, "threequarters");
+		instance.register(164, "twosuperior");
+		instance.register(169, "threesuperior");
+		instance.register(229, "exclamsmall");
+		instance.register(230, "Hungarumlautsmall");
+		instance.register(231, "dollaroldstyle");
+		instance.register(232, "dollarsuperior");
+		instance.register(233, "ampersandsmall");
+		instance.register(234, "Acutesmall");
+		instance.register(235, "parenleftsuperior");
+		instance.register(236, "parenrightsuperior");
+		instance.register(237, "twodotenleader");
+		instance.register(238, "onedotenleader");
+		instance.register(239, "zerooldstyle");
+		instance.register(240, "oneoldstyle");
+		instance.register(241, "twooldstyle");
+		instance.register(242, "threeoldstyle");
+		instance.register(243, "fouroldstyle");
+		instance.register(244, "fiveoldstyle");
+		instance.register(245, "sixoldstyle");
+		instance.register(246, "sevenoldstyle");
+		instance.register(247, "eightoldstyle");
+		instance.register(248, "nineoldstyle");
+		instance.register(249, "commasuperior");
+		instance.register(250, "threequartersemdash");
+		instance.register(251, "periodsuperior");
+		instance.register(252, "questionsmall");
+		instance.register(253, "asuperior");
+		instance.register(254, "bsuperior");
+		instance.register(255, "centsuperior");
+		instance.register(256, "dsuperior");
+		instance.register(257, "esuperior");
+		instance.register(258, "isuperior");
+		instance.register(259, "lsuperior");
+		instance.register(260, "msuperior");
+		instance.register(261, "nsuperior");
+		instance.register(262, "osuperior");
+		instance.register(263, "rsuperior");
+		instance.register(264, "ssuperior");
+		instance.register(265, "tsuperior");
+		instance.register(266, "ff");
+		instance.register(267, "ffi");
+		instance.register(268, "ffl");
+		instance.register(269, "parenleftinferior");
+		instance.register(270, "parenrightinferior");
+		instance.register(271, "Circumflexsmall");
+		instance.register(272, "hyphensuperior");
+		instance.register(273, "Gravesmall");
+		instance.register(274, "Asmall");
+		instance.register(275, "Bsmall");
+		instance.register(276, "Csmall");
+		instance.register(277, "Dsmall");
+		instance.register(278, "Esmall");
+		instance.register(279, "Fsmall");
+		instance.register(280, "Gsmall");
+		instance.register(281, "Hsmall");
+		instance.register(282, "Ismall");
+		instance.register(283, "Jsmall");
+		instance.register(284, "Ksmall");
+		instance.register(285, "Lsmall");
+		instance.register(286, "Msmall");
+		instance.register(287, "Nsmall");
+		instance.register(288, "Osmall");
+		instance.register(289, "Psmall");
+		instance.register(290, "Qsmall");
+		instance.register(291, "Rsmall");
+		instance.register(292, "Ssmall");
+		instance.register(293, "Tsmall");
+		instance.register(294, "Usmall");
+		instance.register(295, "Vsmall");
+		instance.register(296, "Wsmall");
+		instance.register(297, "Xsmall");
+		instance.register(298, "Ysmall");
+		instance.register(299, "Zsmall");
+		instance.register(300, "colonmonetary");
+		instance.register(301, "onefitted");
+		instance.register(302, "rupiah");
+		instance.register(303, "Tildesmall");
+		instance.register(304, "exclamdownsmall");
+		instance.register(305, "centoldstyle");
+		instance.register(306, "Lslashsmall");
+		instance.register(307, "Scaronsmall");
+		instance.register(308, "Zcaronsmall");
+		instance.register(309, "Dieresissmall");
+		instance.register(310, "Brevesmall");
+		instance.register(311, "Caronsmall");
+		instance.register(312, "Dotaccentsmall");
+		instance.register(313, "Macronsmall");
+		instance.register(314, "figuredash");
+		instance.register(315, "hypheninferior");
+		instance.register(316, "Ogoneksmall");
+		instance.register(317, "Ringsmall");
+		instance.register(318, "Cedillasmall");
+		instance.register(319, "questiondownsmall");
+		instance.register(320, "oneeighth");
+		instance.register(321, "threeeighths");
+		instance.register(322, "fiveeighths");
+		instance.register(323, "seveneighths");
+		instance.register(324, "onethird");
+		instance.register(325, "twothirds");
+		instance.register(326, "zerosuperior");
+		instance.register(327, "foursuperior");
+		instance.register(328, "fivesuperior");
+		instance.register(329, "sixsuperior");
+		instance.register(330, "sevensuperior");
+		instance.register(331, "eightsuperior");
+		instance.register(332, "ninesuperior");
+		instance.register(333, "zeroinferior");
+		instance.register(334, "oneinferior");
+		instance.register(335, "twoinferior");
+		instance.register(336, "threeinferior");
+		instance.register(337, "fourinferior");
+		instance.register(338, "fiveinferior");
+		instance.register(339, "sixinferior");
+		instance.register(340, "seveninferior");
+		instance.register(341, "eightinferior");
+		instance.register(342, "nineinferior");
+		instance.register(343, "centinferior");
+		instance.register(344, "dollarinferior");
+		instance.register(345, "periodinferior");
+		instance.register(346, "commainferior");
+		instance.register(347, "Agravesmall");
+		instance.register(348, "Aacutesmall");
+		instance.register(349, "Acircumflexsmall");
+		instance.register(350, "Atildesmall");
+		instance.register(351, "Adieresissmall");
+		instance.register(352, "Aringsmall");
+		instance.register(353, "AEsmall");
+		instance.register(354, "Ccedillasmall");
+		instance.register(355, "Egravesmall");
+		instance.register(356, "Eacutesmall");
+		instance.register(357, "Ecircumflexsmall");
+		instance.register(358, "Edieresissmall");
+		instance.register(359, "Igravesmall");
+		instance.register(360, "Iacutesmall");
+		instance.register(361, "Icircumflexsmall");
+		instance.register(362, "Idieresissmall");
+		instance.register(363, "Ethsmall");
+		instance.register(364, "Ntildesmall");
+		instance.register(365, "Ogravesmall");
+		instance.register(366, "Oacutesmall");
+		instance.register(367, "Ocircumflexsmall");
+		instance.register(368, "Otildesmall");
+		instance.register(369, "Odieresissmall");
+		instance.register(370, "OEsmall");
+		instance.register(371, "Oslashsmall");
+		instance.register(372, "Ugravesmall");
+		instance.register(373, "Uacutesmall");
+		instance.register(374, "Ucircumflexsmall");
+		instance.register(375, "Udieresissmall");
+		instance.register(376, "Yacutesmall");
+		instance.register(377, "Thornsmall");
+		instance.register(378, "Ydieresissmall");
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java	(revision 0)
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff.charset;
+
+import org.apache.fontbox.cff.*;
+
+public class CFFExpertSubsetCharset extends CFFCharset {
+
+	private CFFExpertSubsetCharset(){
+	}
+
+	static
+	public CFFExpertSubsetCharset getInstance(){
+		return CFFExpertSubsetCharset.instance;
+	}
+
+	private static final CFFExpertSubsetCharset instance = new CFFExpertSubsetCharset();
+
+	static {
+		instance.register(1, "space");
+		instance.register(13, "comma");
+		instance.register(14, "hyphen");
+		instance.register(15, "period");
+		instance.register(27, "colon");
+		instance.register(28, "semicolon");
+		instance.register(99, "fraction");
+		instance.register(109, "fi");
+		instance.register(110, "fl");
+		instance.register(150, "onesuperior");
+		instance.register(155, "onehalf");
+		instance.register(158, "onequarter");
+		instance.register(163, "threequarters");
+		instance.register(164, "twosuperior");
+		instance.register(169, "threesuperior");
+		instance.register(231, "dollaroldstyle");
+		instance.register(232, "dollarsuperior");
+		instance.register(235, "parenleftsuperior");
+		instance.register(236, "parenrightsuperior");
+		instance.register(237, "twodotenleader");
+		instance.register(238, "onedotenleader");
+		instance.register(239, "zerooldstyle");
+		instance.register(240, "oneoldstyle");
+		instance.register(241, "twooldstyle");
+		instance.register(242, "threeoldstyle");
+		instance.register(243, "fouroldstyle");
+		instance.register(244, "fiveoldstyle");
+		instance.register(245, "sixoldstyle");
+		instance.register(246, "sevenoldstyle");
+		instance.register(247, "eightoldstyle");
+		instance.register(248, "nineoldstyle");
+		instance.register(249, "commasuperior");
+		instance.register(250, "threequartersemdash");
+		instance.register(251, "periodsuperior");
+		instance.register(253, "asuperior");
+		instance.register(254, "bsuperior");
+		instance.register(255, "centsuperior");
+		instance.register(256, "dsuperior");
+		instance.register(257, "esuperior");
+		instance.register(258, "isuperior");
+		instance.register(259, "lsuperior");
+		instance.register(260, "msuperior");
+		instance.register(261, "nsuperior");
+		instance.register(262, "osuperior");
+		instance.register(263, "rsuperior");
+		instance.register(264, "ssuperior");
+		instance.register(265, "tsuperior");
+		instance.register(266, "ff");
+		instance.register(267, "ffi");
+		instance.register(268, "ffl");
+		instance.register(269, "parenleftinferior");
+		instance.register(270, "parenrightinferior");
+		instance.register(272, "hyphensuperior");
+		instance.register(300, "colonmonetary");
+		instance.register(301, "onefitted");
+		instance.register(302, "rupiah");
+		instance.register(305, "centoldstyle");
+		instance.register(314, "figuredash");
+		instance.register(315, "hypheninferior");
+		instance.register(320, "oneeighth");
+		instance.register(321, "threeeighths");
+		instance.register(322, "fiveeighths");
+		instance.register(323, "seveneighths");
+		instance.register(324, "onethird");
+		instance.register(325, "twothirds");
+		instance.register(326, "zerosuperior");
+		instance.register(327, "foursuperior");
+		instance.register(328, "fivesuperior");
+		instance.register(329, "sixsuperior");
+		instance.register(330, "sevensuperior");
+		instance.register(331, "eightsuperior");
+		instance.register(332, "ninesuperior");
+		instance.register(333, "zeroinferior");
+		instance.register(334, "oneinferior");
+		instance.register(335, "twoinferior");
+		instance.register(336, "threeinferior");
+		instance.register(337, "fourinferior");
+		instance.register(338, "fiveinferior");
+		instance.register(339, "sixinferior");
+		instance.register(340, "seveninferior");
+		instance.register(341, "eightinferior");
+		instance.register(342, "nineinferior");
+		instance.register(343, "centinferior");
+		instance.register(344, "dollarinferior");
+		instance.register(345, "periodinferior");
+		instance.register(346, "commainferior");
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/charset/CFFISOAdobeCharset.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/charset/CFFISOAdobeCharset.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/charset/CFFISOAdobeCharset.java	(revision 0)
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff.charset;
+
+import org.apache.fontbox.cff.*;
+
+public class CFFISOAdobeCharset extends CFFCharset {
+
+	private CFFISOAdobeCharset(){
+	}
+
+	static
+	public CFFISOAdobeCharset getInstance(){
+		return CFFISOAdobeCharset.instance;
+	}
+
+	private static final CFFISOAdobeCharset instance = new CFFISOAdobeCharset();
+
+	static {
+		instance.register(1, "space");
+		instance.register(2, "exclam");
+		instance.register(3, "quotedbl");
+		instance.register(4, "numbersign");
+		instance.register(5, "dollar");
+		instance.register(6, "percent");
+		instance.register(7, "ampersand");
+		instance.register(8, "quoteright");
+		instance.register(9, "parenleft");
+		instance.register(10, "parenright");
+		instance.register(11, "asterisk");
+		instance.register(12, "plus");
+		instance.register(13, "comma");
+		instance.register(14, "hyphen");
+		instance.register(15, "period");
+		instance.register(16, "slash");
+		instance.register(17, "zero");
+		instance.register(18, "one");
+		instance.register(19, "two");
+		instance.register(20, "three");
+		instance.register(21, "four");
+		instance.register(22, "five");
+		instance.register(23, "six");
+		instance.register(24, "seven");
+		instance.register(25, "eight");
+		instance.register(26, "nine");
+		instance.register(27, "colon");
+		instance.register(28, "semicolon");
+		instance.register(29, "less");
+		instance.register(30, "equal");
+		instance.register(31, "greater");
+		instance.register(32, "question");
+		instance.register(33, "at");
+		instance.register(34, "A");
+		instance.register(35, "B");
+		instance.register(36, "C");
+		instance.register(37, "D");
+		instance.register(38, "E");
+		instance.register(39, "F");
+		instance.register(40, "G");
+		instance.register(41, "H");
+		instance.register(42, "I");
+		instance.register(43, "J");
+		instance.register(44, "K");
+		instance.register(45, "L");
+		instance.register(46, "M");
+		instance.register(47, "N");
+		instance.register(48, "O");
+		instance.register(49, "P");
+		instance.register(50, "Q");
+		instance.register(51, "R");
+		instance.register(52, "S");
+		instance.register(53, "T");
+		instance.register(54, "U");
+		instance.register(55, "V");
+		instance.register(56, "W");
+		instance.register(57, "X");
+		instance.register(58, "Y");
+		instance.register(59, "Z");
+		instance.register(60, "bracketleft");
+		instance.register(61, "backslash");
+		instance.register(62, "bracketright");
+		instance.register(63, "asciicircum");
+		instance.register(64, "underscore");
+		instance.register(65, "quoteleft");
+		instance.register(66, "a");
+		instance.register(67, "b");
+		instance.register(68, "c");
+		instance.register(69, "d");
+		instance.register(70, "e");
+		instance.register(71, "f");
+		instance.register(72, "g");
+		instance.register(73, "h");
+		instance.register(74, "i");
+		instance.register(75, "j");
+		instance.register(76, "k");
+		instance.register(77, "l");
+		instance.register(78, "m");
+		instance.register(79, "n");
+		instance.register(80, "o");
+		instance.register(81, "p");
+		instance.register(82, "q");
+		instance.register(83, "r");
+		instance.register(84, "s");
+		instance.register(85, "t");
+		instance.register(86, "u");
+		instance.register(87, "v");
+		instance.register(88, "w");
+		instance.register(89, "x");
+		instance.register(90, "y");
+		instance.register(91, "z");
+		instance.register(92, "braceleft");
+		instance.register(93, "bar");
+		instance.register(94, "braceright");
+		instance.register(95, "asciitilde");
+		instance.register(96, "exclamdown");
+		instance.register(97, "cent");
+		instance.register(98, "sterling");
+		instance.register(99, "fraction");
+		instance.register(100, "yen");
+		instance.register(101, "florin");
+		instance.register(102, "section");
+		instance.register(103, "currency");
+		instance.register(104, "quotesingle");
+		instance.register(105, "quotedblleft");
+		instance.register(106, "guillemotleft");
+		instance.register(107, "guilsinglleft");
+		instance.register(108, "guilsinglright");
+		instance.register(109, "fi");
+		instance.register(110, "fl");
+		instance.register(111, "endash");
+		instance.register(112, "dagger");
+		instance.register(113, "daggerdbl");
+		instance.register(114, "periodcentered");
+		instance.register(115, "paragraph");
+		instance.register(116, "bullet");
+		instance.register(117, "quotesinglbase");
+		instance.register(118, "quotedblbase");
+		instance.register(119, "quotedblright");
+		instance.register(120, "guillemotright");
+		instance.register(121, "ellipsis");
+		instance.register(122, "perthousand");
+		instance.register(123, "questiondown");
+		instance.register(124, "grave");
+		instance.register(125, "acute");
+		instance.register(126, "circumflex");
+		instance.register(127, "tilde");
+		instance.register(128, "macron");
+		instance.register(129, "breve");
+		instance.register(130, "dotaccent");
+		instance.register(131, "dieresis");
+		instance.register(132, "ring");
+		instance.register(133, "cedilla");
+		instance.register(134, "hungarumlaut");
+		instance.register(135, "ogonek");
+		instance.register(136, "caron");
+		instance.register(137, "emdash");
+		instance.register(138, "AE");
+		instance.register(139, "ordfeminine");
+		instance.register(140, "Lslash");
+		instance.register(141, "Oslash");
+		instance.register(142, "OE");
+		instance.register(143, "ordmasculine");
+		instance.register(144, "ae");
+		instance.register(145, "dotlessi");
+		instance.register(146, "lslash");
+		instance.register(147, "oslash");
+		instance.register(148, "oe");
+		instance.register(149, "germandbls");
+		instance.register(150, "onesuperior");
+		instance.register(151, "logicalnot");
+		instance.register(152, "mu");
+		instance.register(153, "trademark");
+		instance.register(154, "Eth");
+		instance.register(155, "onehalf");
+		instance.register(156, "plusminus");
+		instance.register(157, "Thorn");
+		instance.register(158, "onequarter");
+		instance.register(159, "divide");
+		instance.register(160, "brokenbar");
+		instance.register(161, "degree");
+		instance.register(162, "thorn");
+		instance.register(163, "threequarters");
+		instance.register(164, "twosuperior");
+		instance.register(165, "registered");
+		instance.register(166, "minus");
+		instance.register(167, "eth");
+		instance.register(168, "multiply");
+		instance.register(169, "threesuperior");
+		instance.register(170, "copyright");
+		instance.register(171, "Aacute");
+		instance.register(172, "Acircumflex");
+		instance.register(173, "Adieresis");
+		instance.register(174, "Agrave");
+		instance.register(175, "Aring");
+		instance.register(176, "Atilde");
+		instance.register(177, "Ccedilla");
+		instance.register(178, "Eacute");
+		instance.register(179, "Ecircumflex");
+		instance.register(180, "Edieresis");
+		instance.register(181, "Egrave");
+		instance.register(182, "Iacute");
+		instance.register(183, "Icircumflex");
+		instance.register(184, "Idieresis");
+		instance.register(185, "Igrave");
+		instance.register(186, "Ntilde");
+		instance.register(187, "Oacute");
+		instance.register(188, "Ocircumflex");
+		instance.register(189, "Odieresis");
+		instance.register(190, "Ograve");
+		instance.register(191, "Otilde");
+		instance.register(192, "Scaron");
+		instance.register(193, "Uacute");
+		instance.register(194, "Ucircumflex");
+		instance.register(195, "Udieresis");
+		instance.register(196, "Ugrave");
+		instance.register(197, "Yacute");
+		instance.register(198, "Ydieresis");
+		instance.register(199, "Zcaron");
+		instance.register(200, "aacute");
+		instance.register(201, "acircumflex");
+		instance.register(202, "adieresis");
+		instance.register(203, "agrave");
+		instance.register(204, "aring");
+		instance.register(205, "atilde");
+		instance.register(206, "ccedilla");
+		instance.register(207, "eacute");
+		instance.register(208, "ecircumflex");
+		instance.register(209, "edieresis");
+		instance.register(210, "egrave");
+		instance.register(211, "iacute");
+		instance.register(212, "icircumflex");
+		instance.register(213, "idieresis");
+		instance.register(214, "igrave");
+		instance.register(215, "ntilde");
+		instance.register(216, "oacute");
+		instance.register(217, "ocircumflex");
+		instance.register(218, "odieresis");
+		instance.register(219, "ograve");
+		instance.register(220, "otilde");
+		instance.register(221, "scaron");
+		instance.register(222, "uacute");
+		instance.register(223, "ucircumflex");
+		instance.register(224, "udieresis");
+		instance.register(225, "ugrave");
+		instance.register(226, "yacute");
+		instance.register(227, "ydieresis");
+		instance.register(228, "zcaron");
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CharStringCommand.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CharStringCommand.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CharStringCommand.java	(revision 0)
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+public class CharStringCommand {
+
+	private Key key = null;
+
+
+	public CharStringCommand(int b0){
+		setKey(new Key(b0));
+	}
+
+	public CharStringCommand(int b0, int b1){
+		setKey(new Key(b0, b1));
+	}
+
+	public CharStringCommand(int[] values){
+		setKey(new Key(values));
+	}
+
+	public Key getKey(){
+		return this.key;
+	}
+
+	private void setKey(Key key){
+		this.key = key;
+	}
+
+	@Override
+	public String toString(){
+		return (this.getKey()).toString();
+	}
+
+	@Override
+	public int hashCode(){
+		return (this.getKey()).hashCode();
+	}
+
+	@Override
+	public boolean equals(Object object){
+
+		if(object instanceof CharStringCommand){
+			CharStringCommand that = (CharStringCommand)object;
+
+			return (this.getKey()).equals(that.getKey());
+		}
+
+		return false;
+	}
+
+	static
+	public class Key {
+
+		private int[] value = null;
+
+
+		public Key(int b0){
+			setValue(new int[]{b0});
+		}
+
+		public Key(int b0, int b1){
+			setValue(new int[]{b0, b1});
+		}
+
+		public Key(int[] value){
+			setValue(value);
+		}
+
+		public int[] getValue(){
+			return this.value;
+		}
+
+		private void setValue(int[] value){
+			this.value = value;
+		}
+
+		@Override
+		public String toString(){
+			return Arrays.toString(this.getValue());
+		}
+
+		@Override
+		public int hashCode(){
+
+			if(this.value[0] == 12){
+
+				if(this.value.length > 1){
+					return this.value[0] ^ this.value[1];
+				}
+			}
+
+			return this.value[0];
+		}
+
+		@Override
+		public boolean equals(Object object){
+
+			if(object instanceof Key){
+				Key that = (Key)object;
+
+				if(this.value[0] == 12 && that.value[0] == 12){
+
+					if(this.value.length > 1 && that.value.length > 1){
+						return this.value[1] == that.value[1];
+					}
+
+					return this.value.length == that.value.length;
+				}
+
+				return this.value[0] == that.value[0];
+			}
+
+			return false;
+		}
+	}
+
+	public static final Map<Key, String> TYPE1_VOCABULARY;
+
+	static {
+		Map<Key, String> map = new LinkedHashMap<Key, String>();
+		map.put(new Key(1), "hstem");
+		map.put(new Key(3), "vstem");
+		map.put(new Key(4), "vmoveto");
+		map.put(new Key(5), "rlineto");
+		map.put(new Key(6), "hlineto");
+		map.put(new Key(7), "vlineto");
+		map.put(new Key(8), "rrcurveto");
+		map.put(new Key(9), "closepath");
+		map.put(new Key(10), "callsubr");
+		map.put(new Key(11), "return");
+		map.put(new Key(12), "escape");
+		map.put(new Key(12, 0), "dotsection");
+		map.put(new Key(12, 1), "vstem3");
+		map.put(new Key(12, 2), "hstem3");
+		map.put(new Key(12, 6), "seac");
+		map.put(new Key(12, 7), "sbw");
+		map.put(new Key(12, 12), "div");
+		map.put(new Key(12, 16), "callothersubr");
+		map.put(new Key(12, 17), "pop");
+		map.put(new Key(12, 33), "setcurrentpoint");
+		map.put(new Key(13), "hsbw");
+		map.put(new Key(14), "endchar");
+		map.put(new Key(21), "rmoveto");
+		map.put(new Key(22), "hmoveto");
+		map.put(new Key(30), "vhcurveto");
+		map.put(new Key(31), "hvcurveto");
+
+		TYPE1_VOCABULARY = Collections.unmodifiableMap(map);
+	}
+
+	public static final Map<Key, String> TYPE2_VOCABULARY;
+
+	static {
+		Map<Key, String> map = new LinkedHashMap<Key, String>();
+		map.put(new Key(1), "hstem");
+		map.put(new Key(3), "vstem");
+		map.put(new Key(4), "vmoveto");
+		map.put(new Key(5), "rlineto");
+		map.put(new Key(6), "hlineto");
+		map.put(new Key(7), "vlineto");
+		map.put(new Key(8), "rrcurveto");
+		map.put(new Key(10), "callsubr");
+		map.put(new Key(11), "return");
+		map.put(new Key(12), "escape");
+		map.put(new Key(12, 3), "and");
+		map.put(new Key(12, 4), "or");
+		map.put(new Key(12, 5), "not");
+		map.put(new Key(12, 9), "abs");
+		map.put(new Key(12, 10), "add");
+		map.put(new Key(12, 11), "sub");
+		map.put(new Key(12, 12), "div");
+		map.put(new Key(12, 14), "neg");
+		map.put(new Key(12, 15), "eq");
+		map.put(new Key(12, 18), "drop");
+		map.put(new Key(12, 20), "put");
+		map.put(new Key(12, 21), "get");
+		map.put(new Key(12, 22), "ifelse");
+		map.put(new Key(12, 23), "random");
+		map.put(new Key(12, 24), "mul");
+		map.put(new Key(12, 26), "sqrt");
+		map.put(new Key(12, 27), "dup");
+		map.put(new Key(12, 28), "exch");
+		map.put(new Key(12, 29), "index");
+		map.put(new Key(12, 30), "roll");
+		map.put(new Key(12, 34), "hflex");
+		map.put(new Key(12, 35), "flex");
+		map.put(new Key(12, 36), "hflex1");
+		map.put(new Key(12, 37), "flex1");
+		map.put(new Key(14), "endchar");
+		map.put(new Key(18), "hstemhm");
+		map.put(new Key(19), "hintmask");
+		map.put(new Key(20), "cntrmask");
+		map.put(new Key(21), "rmoveto");
+		map.put(new Key(22), "hmoveto");
+		map.put(new Key(23), "vstemhm");
+		map.put(new Key(24), "rcurveline");
+		map.put(new Key(25), "rlinecurve");
+		map.put(new Key(26), "vvcurveto");
+		map.put(new Key(27), "hhcurveto");
+		map.put(new Key(28), "shortint");
+		map.put(new Key(29), "callgsubr");
+		map.put(new Key(30), "vhcurveto");
+		map.put(new Key(31), "hvcurveto");
+
+		TYPE2_VOCABULARY = Collections.unmodifiableMap(map);
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CharStringConverter.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CharStringConverter.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CharStringConverter.java	(revision 0)
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+public class CharStringConverter extends CharStringHandler {
+
+	private int defaultWidthX = 0;
+
+	private int nominalWidthX = 0;
+
+	private List<Object> sequence = null;
+
+	private int pathCount = 0;
+
+
+	public CharStringConverter(int defaultWidthX, int nominalWidthX){
+		this.defaultWidthX = defaultWidthX;
+		this.nominalWidthX = nominalWidthX;
+	}
+
+	public List<Object> convert(List<Object> sequence){
+		this.sequence = new ArrayList<Object>();
+
+		this.pathCount = 0;
+
+		handleSequence(sequence);
+
+		return this.sequence;
+	}
+
+	@Override
+	public void handleCommand(List<Integer> numbers, CharStringCommand command){
+
+		if((CharStringCommand.TYPE1_VOCABULARY).containsKey(command.getKey())){
+			handleType1Command(numbers, command);
+		} else {
+			handleType2Command(numbers, command);
+		}
+	}
+
+	private void handleType1Command(List<Integer> numbers, CharStringCommand command){
+		String name = (CharStringCommand.TYPE1_VOCABULARY).get(command.getKey());
+
+		if("hstem".equals(name)){
+			numbers = clearStack(numbers, numbers.size() % 2 != 0);
+
+			expandStemHints(numbers, true);
+		} else
+
+		if("vstem".equals(name)){
+			numbers = clearStack(numbers, numbers.size() % 2 != 0);
+
+			expandStemHints(numbers, false);
+		} else
+
+		if("vmoveto".equals(name)){
+			numbers = clearStack(numbers, numbers.size() > 1);
+
+			markPath();
+			addCommand(numbers, command);
+		} else
+
+		if("rlineto".equals(name)){
+			addCommandList(split(numbers, 2), command);
+		} else
+
+		if("hlineto".equals(name)){
+			drawAlternatingLine(numbers, true);
+		} else
+
+		if("vlineto".equals(name)){
+			drawAlternatingLine(numbers, false);
+		} else
+
+		if("rrcurveto".equals(name)){
+			addCommandList(split(numbers, 6), command);
+		} else
+
+		if("endchar".equals(name)){
+			numbers = clearStack(numbers, numbers.size() > 0);
+
+			closePath();
+			addCommand(numbers, command);
+		} else
+
+		if("rmoveto".equals(name)){
+			numbers = clearStack(numbers, numbers.size() > 2);
+
+			markPath();
+			addCommand(numbers, command);
+		} else
+
+		if("hmoveto".equals(name)){
+			numbers = clearStack(numbers, numbers.size() > 1);
+
+			markPath();
+			addCommand(numbers, command);
+		} else
+
+		if("vhcurveto".equals(name)){
+			drawAlternatingCurve(numbers, false);
+		} else
+
+		if("hvcurveto".equals(name)){
+			drawAlternatingCurve(numbers, true);
+		} else
+
+		{
+			addCommand(numbers, command);
+		}
+	}
+
+	private void handleType2Command(List<Integer> numbers, CharStringCommand command){
+		String name = (CharStringCommand.TYPE2_VOCABULARY).get(command.getKey());
+
+		if("hflex".equals(name) || "flex".equals(name) || "hflex1".equals(name) || "flex1".equals(name)){
+			throw new UnsupportedOperationException();
+		} else
+
+		if("hstemhm".equals(name)){
+			numbers = clearStack(numbers, numbers.size() % 2 != 0);
+
+			expandStemHints(numbers, true);
+		} else
+
+		if("hintmask".equals(name) || "cntrmask".equals(name)){
+			numbers = clearStack(numbers, numbers.size() % 2 != 0);
+
+			if(numbers.size() > 0){
+				expandStemHints(numbers, false);
+			}
+		} else
+
+		if("vstemhm".equals(name)){
+			numbers = clearStack(numbers, numbers.size() % 2 != 0);
+
+			expandStemHints(numbers, false);
+		} else
+
+		if("rcurveline".equals(name)){
+			addCommandList(split(numbers.subList(0, numbers.size() - 2), 6), new CharStringCommand(8));
+			addCommand(numbers.subList(numbers.size() - 2, numbers.size()), new CharStringCommand(5));
+		} else
+
+		if("rlinecurve".equals(name)){
+			addCommandList(split(numbers.subList(0, numbers.size() - 6), 2), new CharStringCommand(5));
+			addCommand(numbers.subList(numbers.size() - 6, numbers.size()), new CharStringCommand(8));
+		} else
+
+		if("vvcurveto".equals(name)){
+			drawCurve(numbers, false);
+		} else
+
+		if("hhcurveto".equals(name)){
+			drawCurve(numbers, true);
+		} else
+
+		{
+			//System.out.println("Not implemented: numbers=" + numbers + " command=" +command+ " (" +name+ ")");
+		}
+	}
+
+	private List<Integer> clearStack(List<Integer> numbers, boolean flag){
+
+		if(this.sequence.size() == 0){
+
+			if(flag){
+				addCommand(Arrays.asList(Integer.valueOf(0), Integer.valueOf(numbers.get(0).intValue() + this.nominalWidthX)), new CharStringCommand(13));
+
+				numbers = numbers.subList(1, numbers.size());
+			} else {
+				addCommand(Arrays.asList(Integer.valueOf(0), Integer.valueOf(this.defaultWidthX)), new CharStringCommand(13));
+			}
+		}
+
+		return numbers;
+	}
+
+	private void expandStemHints(List<Integer> numbers, boolean horizontal){
+		// TODO
+	}
+
+	private void markPath(){
+
+		if(this.pathCount > 0){
+			closePath();
+		}
+
+		this.pathCount++;
+	}
+
+	private void closePath(){
+		CharStringCommand command = (this.pathCount > 0 ? (CharStringCommand)this.sequence.get(this.sequence.size() - 1) : null);
+
+		CharStringCommand closepathCommand = new CharStringCommand(9);
+
+		if(command != null && !closepathCommand.equals(command)){
+			addCommand(Collections.<Integer>emptyList(), closepathCommand);
+		}
+	}
+
+	private void drawAlternatingLine(List<Integer> numbers, boolean horizontal){
+
+		while(numbers.size() > 0){
+			addCommand(numbers.subList(0, 1), new CharStringCommand(horizontal ? 6 : 7));
+
+			numbers = numbers.subList(1, numbers.size());
+			horizontal = !horizontal;
+		}
+	}
+
+	private void drawAlternatingCurve(List<Integer> numbers, boolean horizontal){
+
+		while(numbers.size() > 0){
+			boolean last = (numbers.size() == 5);
+
+			if(horizontal){
+				addCommand(Arrays.asList(numbers.get(0), Integer.valueOf(0), numbers.get(1), numbers.get(2), last ? numbers.get(4) : Integer.valueOf(0), numbers.get(3)), new CharStringCommand(8));
+			} else {
+				addCommand(Arrays.asList(Integer.valueOf(0), numbers.get(0), numbers.get(1), numbers.get(2), numbers.get(3), last ? numbers.get(4) : Integer.valueOf(0)), new CharStringCommand(8));
+			}
+
+			numbers = numbers.subList(last ? 5 : 4, numbers.size());
+			horizontal = !horizontal;
+		}
+	}
+
+	private void drawCurve(List<Integer> numbers, boolean horizontal){
+
+		while(numbers.size() > 0){
+			boolean first = (numbers.size() % 4 == 1);
+
+			if(horizontal){
+				addCommand(Arrays.asList(numbers.get(first ? 1 : 0), first ? numbers.get(0) : Integer.valueOf(0), numbers.get(first ? 2 : 1), numbers.get(first ? 3 : 2), numbers.get(first ? 4 : 3), Integer.valueOf(0)), new CharStringCommand(8));
+			} else {
+				addCommand(Arrays.asList(first ? numbers.get(0) : Integer.valueOf(0), numbers.get(first ? 1 : 0), numbers.get(first ? 2 : 1), numbers.get(first ? 3 : 2), Integer.valueOf(0), numbers.get(first ? 4 : 3)), new CharStringCommand(8));
+			}
+
+			numbers = numbers.subList(first ? 5 : 4, numbers.size());
+		}
+	}
+
+	private void addCommandList(List<List<Integer>> numbers, CharStringCommand command){
+
+		for(int i = 0; i < numbers.size(); i++){
+			addCommand(numbers.get(i), command);
+		}
+	}
+
+	private void addCommand(List<Integer> numbers, CharStringCommand command){
+		this.sequence.addAll(numbers);
+		this.sequence.add(command);
+	}
+
+	static
+	private <E> List<List<E>> split(List<E> list, int size){
+		List<List<E>> result = new ArrayList<List<E>>();
+
+		for(int i = 0; i < list.size() / size; i++){
+			result.add(list.subList(i * size, (i + 1) * size));
+		}
+
+		return result;
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CharStringHandler.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CharStringHandler.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CharStringHandler.java	(revision 0)
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+abstract
+public class CharStringHandler {
+
+	@SuppressWarnings (
+		value = {"unchecked"}
+	)
+	public void handleSequence(List<Object> sequence){
+		int offset = 0;
+
+		for(int i = 0; i < sequence.size(); i++){
+			Object object = sequence.get(i);
+
+			if(object instanceof CharStringCommand){
+				List<Integer> numbers = (List)sequence.subList(offset, i);
+				CharStringCommand command = (CharStringCommand)object;
+
+				handleCommand(numbers, command);
+
+				offset = (i + 1);
+			}
+		}
+	}
+
+	public void handleCommand(List<Integer> numbers, CharStringCommand command){
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/CharStringRenderer.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/CharStringRenderer.java	(revision 0)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.awt.geom.*;
+import java.util.*;
+
+public class CharStringRenderer extends CharStringHandler {
+
+	private GeneralPath path = null;
+
+	private Point2D sidebearingPoint = null;
+
+	private Point2D referencePoint = null;
+
+	private int width = 0;
+
+
+	public GeneralPath render(List<Object> sequence){
+		this.path = new GeneralPath();
+
+		this.sidebearingPoint = new Point2D.Float(0, 0);
+		this.referencePoint = null;
+
+		setWidth(0);
+
+		handleSequence(sequence);
+
+		return this.path;
+	}
+
+	@Override
+	public void handleCommand(List<Integer> numbers, CharStringCommand command){
+		String name = (CharStringCommand.TYPE1_VOCABULARY).get(command.getKey());
+
+		if("vmoveto".equals(name)){
+			rmoveTo(Integer.valueOf(0), numbers.get(0));
+		} else
+
+		if("rlineto".equals(name)){
+			rlineTo(numbers.get(0), numbers.get(1));
+		} else
+
+		if("hlineto".equals(name)){
+			rlineTo(numbers.get(0), Integer.valueOf(0));
+		} else
+
+		if("vlineto".equals(name)){
+			rlineTo(Integer.valueOf(0), numbers.get(0));
+		} else
+
+		if("rrcurveto".equals(name)){
+			rrcurveTo(numbers.get(0), numbers.get(1), numbers.get(2), numbers.get(3), numbers.get(4), numbers.get(5));
+		} else
+
+		if("closepath".equals(name)){
+			closePath();
+		} else
+
+		if("sbw".equals(name)){
+			pointSb(numbers.get(0), numbers.get(1));
+
+			setWidth(numbers.get(2).intValue());
+		} else
+
+		if("hsbw".equals(name)){
+			pointSb(numbers.get(0), Integer.valueOf(0));
+			setWidth(numbers.get(1).intValue());
+		} else
+
+		if("rmoveto".equals(name)){
+			rmoveTo(numbers.get(0), numbers.get(1));
+		} else
+
+		if("hmoveto".equals(name)){
+			rmoveTo(numbers.get(0), Integer.valueOf(0));
+		} else
+
+		if("vhcurveto".equals(name)){
+			rrcurveTo(Integer.valueOf(0), numbers.get(0), numbers.get(1), numbers.get(2), numbers.get(3), Integer.valueOf(0));
+		} else
+
+		if("hvcurveto".equals(name)){
+			rrcurveTo(numbers.get(0), Integer.valueOf(0), numbers.get(1), numbers.get(2), Integer.valueOf(0), numbers.get(3));
+		}
+	}
+
+	private void rmoveTo(Number dx, Number dy){
+		Point2D point = this.referencePoint;
+
+		if(point == null){
+			point = this.sidebearingPoint;
+		}
+
+		this.referencePoint = null;
+
+		this.path.moveTo(point.getX() + dx.floatValue(), point.getY() + dy.floatValue());
+	}
+
+	private void rlineTo(Number dx, Number dy){
+		Point2D point = this.path.getCurrentPoint();
+
+		this.path.lineTo(point.getX() + dx.floatValue(), point.getY() + dy.floatValue());
+	}
+
+	private void rrcurveTo(Number dx1, Number dy1, Number dx2, Number dy2, Number dx3, Number dy3){
+		Point2D point = this.path.getCurrentPoint();
+
+		float x1 = (float)point.getX() + dx1.floatValue();
+		float y1 = (float)point.getY() + dy1.floatValue();
+
+		float x2 = x1 + dx2.floatValue();
+		float y2 = y1 + dy2.floatValue();
+
+		float x3 = x2 + dx3.floatValue();
+		float y3 = y2 + dy3.floatValue();
+
+		this.path.curveTo(x1, y1, x2, y2, x3, y3);
+	}
+
+	private void closePath(){
+		this.referencePoint = this.path.getCurrentPoint();
+
+		this.path.closePath();
+	}
+
+	private void pointSb(Number x, Number y){
+		this.sidebearingPoint = new Point2D.Float(x.floatValue(), y.floatValue());
+	}
+
+	public Rectangle2D getBounds(){
+		return this.path.getBounds2D();
+	}
+
+	public int getWidth(){
+		return this.width;
+	}
+
+	private void setWidth(int width){
+		this.width = width;
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/DataInput.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/DataInput.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/DataInput.java	(revision 0)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+
+public class DataInput {
+
+	private byte[] buffer = null;
+
+	private int position = 0;
+
+
+	public DataInput(byte[] buffer){
+		this.buffer = buffer;
+	}
+
+	public boolean hasRemaining(){
+		return this.position < this.buffer.length;
+	}
+
+	public int getPosition(){
+		return this.position;
+	}
+
+	public void setPosition(int position){
+		this.position = position;
+	}
+
+	public String getString() throws IOException {
+		return new String(this.buffer, "ISO-8859-1");
+	}
+
+	public byte readByte() throws IOException {
+		return (byte)readUnsignedByte();
+	}
+
+	public int readUnsignedByte() throws IOException {
+		int b = read();
+
+		if(b < 0){
+			throw new EOFException();
+		}
+
+		return b;
+	}
+
+	public short readShort() throws IOException {
+		return (short)readUnsignedShort();
+	}
+
+	public int readUnsignedShort() throws IOException {
+		int b1 = read();
+		int b2 = read();
+
+		if((b1 | b2) < 0){
+			throw new EOFException();
+		}
+
+		return (b1 << 8 | b2);
+	}
+
+	public int readInt() throws IOException {
+		int b1 = read();
+		int b2 = read();
+		int b3 = read();
+		int b4 = read();
+
+		if((b1 | b2 | b3 | b4) < 0){
+			throw new EOFException();
+		}
+
+		return (b1 << 24 | b2 << 16 | b3 << 8 | b4);
+	}
+
+	public byte[] readBytes(int length) throws IOException {
+		byte[] bytes = new byte[length];
+
+		for(int i = 0; i < length; i++){
+			bytes[i] = readByte();
+		}
+
+		return bytes;
+	}
+
+	private int read(){
+
+		try {
+			int value = this.buffer[this.position] & 0xff;
+
+			this.position++;
+
+			return value;
+		} catch(RuntimeException re){
+			return -1;
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/DataOutput.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/DataOutput.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/DataOutput.java	(revision 0)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+
+public class DataOutput {
+
+	private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+	private String encoding = null;
+
+
+	public DataOutput(){
+		this("ISO-8859-1");
+	}
+
+	public DataOutput(String encoding){
+		this.encoding = encoding;
+	}
+
+	public byte[] getBytes(){
+		return this.buffer.toByteArray();
+	}
+
+	public void write(int value){
+		this.buffer.write(value);
+	}
+
+	public void write(byte[] buffer){
+		this.buffer.write(buffer, 0, buffer.length);
+	}
+
+	public void write(byte[] buffer, int offset, int length){
+		this.buffer.write(buffer, offset, length);
+	}
+
+	public void print(String string) throws IOException {
+		write(string.getBytes(this.encoding));
+	}
+
+	public void println(String string) throws IOException {
+		write(string.getBytes(this.encoding));
+
+		write('\n');
+	}
+
+	public void println(){
+		write('\n');
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/encoding/CFFExpertEncoding.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/encoding/CFFExpertEncoding.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/encoding/CFFExpertEncoding.java	(revision 0)
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff.encoding;
+
+import org.apache.fontbox.cff.*;
+
+public class CFFExpertEncoding extends CFFEncoding {
+
+	private CFFExpertEncoding(){
+	}
+
+	static
+	public CFFExpertEncoding getInstance(){
+		return CFFExpertEncoding.instance;
+	}
+
+	private static final CFFExpertEncoding instance = new CFFExpertEncoding();
+
+	static {
+		instance.register(0, 0);
+		instance.register(1, 0);
+		instance.register(2, 0);
+		instance.register(3, 0);
+		instance.register(4, 0);
+		instance.register(5, 0);
+		instance.register(6, 0);
+		instance.register(7, 0);
+		instance.register(8, 0);
+		instance.register(9, 0);
+		instance.register(10, 0);
+		instance.register(11, 0);
+		instance.register(12, 0);
+		instance.register(13, 0);
+		instance.register(14, 0);
+		instance.register(15, 0);
+		instance.register(16, 0);
+		instance.register(17, 0);
+		instance.register(18, 0);
+		instance.register(19, 0);
+		instance.register(20, 0);
+		instance.register(21, 0);
+		instance.register(22, 0);
+		instance.register(23, 0);
+		instance.register(24, 0);
+		instance.register(25, 0);
+		instance.register(26, 0);
+		instance.register(27, 0);
+		instance.register(28, 0);
+		instance.register(29, 0);
+		instance.register(30, 0);
+		instance.register(31, 0);
+		instance.register(32, 1);
+		instance.register(33, 229);
+		instance.register(34, 230);
+		instance.register(35, 0);
+		instance.register(36, 231);
+		instance.register(37, 232);
+		instance.register(38, 233);
+		instance.register(39, 234);
+		instance.register(40, 235);
+		instance.register(41, 236);
+		instance.register(42, 237);
+		instance.register(43, 238);
+		instance.register(44, 13);
+		instance.register(45, 14);
+		instance.register(46, 15);
+		instance.register(47, 99);
+		instance.register(48, 239);
+		instance.register(49, 240);
+		instance.register(50, 241);
+		instance.register(51, 242);
+		instance.register(52, 243);
+		instance.register(53, 244);
+		instance.register(54, 245);
+		instance.register(55, 246);
+		instance.register(56, 247);
+		instance.register(57, 248);
+		instance.register(58, 27);
+		instance.register(59, 28);
+		instance.register(60, 249);
+		instance.register(61, 250);
+		instance.register(62, 251);
+		instance.register(63, 252);
+		instance.register(64, 0);
+		instance.register(65, 253);
+		instance.register(66, 254);
+		instance.register(67, 255);
+		instance.register(68, 256);
+		instance.register(69, 257);
+		instance.register(70, 0);
+		instance.register(71, 0);
+		instance.register(72, 0);
+		instance.register(73, 258);
+		instance.register(74, 0);
+		instance.register(75, 0);
+		instance.register(76, 259);
+		instance.register(77, 260);
+		instance.register(78, 261);
+		instance.register(79, 262);
+		instance.register(80, 0);
+		instance.register(81, 0);
+		instance.register(82, 263);
+		instance.register(83, 264);
+		instance.register(84, 265);
+		instance.register(85, 0);
+		instance.register(86, 266);
+		instance.register(87, 109);
+		instance.register(88, 110);
+		instance.register(89, 267);
+		instance.register(90, 268);
+		instance.register(91, 269);
+		instance.register(92, 0);
+		instance.register(93, 270);
+		instance.register(94, 271);
+		instance.register(95, 272);
+		instance.register(96, 273);
+		instance.register(97, 274);
+		instance.register(98, 275);
+		instance.register(99, 276);
+		instance.register(100, 277);
+		instance.register(101, 278);
+		instance.register(102, 279);
+		instance.register(103, 280);
+		instance.register(104, 281);
+		instance.register(105, 282);
+		instance.register(106, 283);
+		instance.register(107, 284);
+		instance.register(108, 285);
+		instance.register(109, 286);
+		instance.register(110, 287);
+		instance.register(111, 288);
+		instance.register(112, 289);
+		instance.register(113, 290);
+		instance.register(114, 291);
+		instance.register(115, 292);
+		instance.register(116, 293);
+		instance.register(117, 294);
+		instance.register(118, 295);
+		instance.register(119, 296);
+		instance.register(120, 297);
+		instance.register(121, 298);
+		instance.register(122, 299);
+		instance.register(123, 300);
+		instance.register(124, 301);
+		instance.register(125, 302);
+		instance.register(126, 303);
+		instance.register(127, 0);
+		instance.register(128, 0);
+		instance.register(129, 0);
+		instance.register(130, 0);
+		instance.register(131, 0);
+		instance.register(132, 0);
+		instance.register(133, 0);
+		instance.register(134, 0);
+		instance.register(135, 0);
+		instance.register(136, 0);
+		instance.register(137, 0);
+		instance.register(138, 0);
+		instance.register(139, 0);
+		instance.register(140, 0);
+		instance.register(141, 0);
+		instance.register(142, 0);
+		instance.register(143, 0);
+		instance.register(144, 0);
+		instance.register(145, 0);
+		instance.register(146, 0);
+		instance.register(147, 0);
+		instance.register(148, 0);
+		instance.register(149, 0);
+		instance.register(150, 0);
+		instance.register(151, 0);
+		instance.register(152, 0);
+		instance.register(153, 0);
+		instance.register(154, 0);
+		instance.register(155, 0);
+		instance.register(156, 0);
+		instance.register(157, 0);
+		instance.register(158, 0);
+		instance.register(159, 0);
+		instance.register(160, 0);
+		instance.register(161, 304);
+		instance.register(162, 305);
+		instance.register(163, 306);
+		instance.register(164, 0);
+		instance.register(165, 0);
+		instance.register(166, 307);
+		instance.register(167, 308);
+		instance.register(168, 309);
+		instance.register(169, 310);
+		instance.register(170, 311);
+		instance.register(171, 0);
+		instance.register(172, 312);
+		instance.register(173, 0);
+		instance.register(174, 0);
+		instance.register(175, 313);
+		instance.register(176, 0);
+		instance.register(177, 0);
+		instance.register(178, 314);
+		instance.register(179, 315);
+		instance.register(180, 0);
+		instance.register(181, 0);
+		instance.register(182, 316);
+		instance.register(183, 317);
+		instance.register(184, 318);
+		instance.register(185, 0);
+		instance.register(186, 0);
+		instance.register(187, 0);
+		instance.register(188, 158);
+		instance.register(189, 155);
+		instance.register(190, 163);
+		instance.register(191, 319);
+		instance.register(192, 320);
+		instance.register(193, 321);
+		instance.register(194, 322);
+		instance.register(195, 323);
+		instance.register(196, 324);
+		instance.register(197, 325);
+		instance.register(198, 0);
+		instance.register(199, 0);
+		instance.register(200, 326);
+		instance.register(201, 150);
+		instance.register(202, 164);
+		instance.register(203, 169);
+		instance.register(204, 327);
+		instance.register(205, 328);
+		instance.register(206, 329);
+		instance.register(207, 330);
+		instance.register(208, 331);
+		instance.register(209, 332);
+		instance.register(210, 333);
+		instance.register(211, 334);
+		instance.register(212, 335);
+		instance.register(213, 336);
+		instance.register(214, 337);
+		instance.register(215, 338);
+		instance.register(216, 339);
+		instance.register(217, 340);
+		instance.register(218, 341);
+		instance.register(219, 342);
+		instance.register(220, 343);
+		instance.register(221, 344);
+		instance.register(222, 345);
+		instance.register(223, 346);
+		instance.register(224, 347);
+		instance.register(225, 348);
+		instance.register(226, 349);
+		instance.register(227, 350);
+		instance.register(228, 351);
+		instance.register(229, 352);
+		instance.register(230, 353);
+		instance.register(231, 354);
+		instance.register(232, 355);
+		instance.register(233, 356);
+		instance.register(234, 357);
+		instance.register(235, 358);
+		instance.register(236, 359);
+		instance.register(237, 360);
+		instance.register(238, 361);
+		instance.register(239, 362);
+		instance.register(240, 363);
+		instance.register(241, 364);
+		instance.register(242, 365);
+		instance.register(243, 366);
+		instance.register(244, 367);
+		instance.register(245, 368);
+		instance.register(246, 369);
+		instance.register(247, 370);
+		instance.register(248, 371);
+		instance.register(249, 372);
+		instance.register(250, 373);
+		instance.register(251, 374);
+		instance.register(252, 375);
+		instance.register(253, 376);
+		instance.register(254, 377);
+		instance.register(255, 378);
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/encoding/CFFStandardEncoding.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/encoding/CFFStandardEncoding.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/encoding/CFFStandardEncoding.java	(revision 0)
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff.encoding;
+
+import org.apache.fontbox.cff.*;
+
+public class CFFStandardEncoding extends CFFEncoding {
+
+	private CFFStandardEncoding(){
+	}
+
+	static
+	public CFFStandardEncoding getInstance(){
+		return CFFStandardEncoding.instance;
+	}
+
+	private static final CFFStandardEncoding instance = new CFFStandardEncoding();
+
+	static {
+		instance.register(0, 0);
+		instance.register(1, 0);
+		instance.register(2, 0);
+		instance.register(3, 0);
+		instance.register(4, 0);
+		instance.register(5, 0);
+		instance.register(6, 0);
+		instance.register(7, 0);
+		instance.register(8, 0);
+		instance.register(9, 0);
+		instance.register(10, 0);
+		instance.register(11, 0);
+		instance.register(12, 0);
+		instance.register(13, 0);
+		instance.register(14, 0);
+		instance.register(15, 0);
+		instance.register(16, 0);
+		instance.register(17, 0);
+		instance.register(18, 0);
+		instance.register(19, 0);
+		instance.register(20, 0);
+		instance.register(21, 0);
+		instance.register(22, 0);
+		instance.register(23, 0);
+		instance.register(24, 0);
+		instance.register(25, 0);
+		instance.register(26, 0);
+		instance.register(27, 0);
+		instance.register(28, 0);
+		instance.register(29, 0);
+		instance.register(30, 0);
+		instance.register(31, 0);
+		instance.register(32, 1);
+		instance.register(33, 2);
+		instance.register(34, 3);
+		instance.register(35, 4);
+		instance.register(36, 5);
+		instance.register(37, 6);
+		instance.register(38, 7);
+		instance.register(39, 8);
+		instance.register(40, 9);
+		instance.register(41, 10);
+		instance.register(42, 11);
+		instance.register(43, 12);
+		instance.register(44, 13);
+		instance.register(45, 14);
+		instance.register(46, 15);
+		instance.register(47, 16);
+		instance.register(48, 17);
+		instance.register(49, 18);
+		instance.register(50, 19);
+		instance.register(51, 20);
+		instance.register(52, 21);
+		instance.register(53, 22);
+		instance.register(54, 23);
+		instance.register(55, 24);
+		instance.register(56, 25);
+		instance.register(57, 26);
+		instance.register(58, 27);
+		instance.register(59, 28);
+		instance.register(60, 29);
+		instance.register(61, 30);
+		instance.register(62, 31);
+		instance.register(63, 32);
+		instance.register(64, 33);
+		instance.register(65, 34);
+		instance.register(66, 35);
+		instance.register(67, 36);
+		instance.register(68, 37);
+		instance.register(69, 38);
+		instance.register(70, 39);
+		instance.register(71, 40);
+		instance.register(72, 41);
+		instance.register(73, 42);
+		instance.register(74, 43);
+		instance.register(75, 44);
+		instance.register(76, 45);
+		instance.register(77, 46);
+		instance.register(78, 47);
+		instance.register(79, 48);
+		instance.register(80, 49);
+		instance.register(81, 50);
+		instance.register(82, 51);
+		instance.register(83, 52);
+		instance.register(84, 53);
+		instance.register(85, 54);
+		instance.register(86, 55);
+		instance.register(87, 56);
+		instance.register(88, 57);
+		instance.register(89, 58);
+		instance.register(90, 59);
+		instance.register(91, 60);
+		instance.register(92, 61);
+		instance.register(93, 62);
+		instance.register(94, 63);
+		instance.register(95, 64);
+		instance.register(96, 65);
+		instance.register(97, 66);
+		instance.register(98, 67);
+		instance.register(99, 68);
+		instance.register(100, 69);
+		instance.register(101, 70);
+		instance.register(102, 71);
+		instance.register(103, 72);
+		instance.register(104, 73);
+		instance.register(105, 74);
+		instance.register(106, 75);
+		instance.register(107, 76);
+		instance.register(108, 77);
+		instance.register(109, 78);
+		instance.register(110, 79);
+		instance.register(111, 80);
+		instance.register(112, 81);
+		instance.register(113, 82);
+		instance.register(114, 83);
+		instance.register(115, 84);
+		instance.register(116, 85);
+		instance.register(117, 86);
+		instance.register(118, 87);
+		instance.register(119, 88);
+		instance.register(120, 89);
+		instance.register(121, 90);
+		instance.register(122, 91);
+		instance.register(123, 92);
+		instance.register(124, 93);
+		instance.register(125, 94);
+		instance.register(126, 95);
+		instance.register(127, 0);
+		instance.register(128, 0);
+		instance.register(129, 0);
+		instance.register(130, 0);
+		instance.register(131, 0);
+		instance.register(132, 0);
+		instance.register(133, 0);
+		instance.register(134, 0);
+		instance.register(135, 0);
+		instance.register(136, 0);
+		instance.register(137, 0);
+		instance.register(138, 0);
+		instance.register(139, 0);
+		instance.register(140, 0);
+		instance.register(141, 0);
+		instance.register(142, 0);
+		instance.register(143, 0);
+		instance.register(144, 0);
+		instance.register(145, 0);
+		instance.register(146, 0);
+		instance.register(147, 0);
+		instance.register(148, 0);
+		instance.register(149, 0);
+		instance.register(150, 0);
+		instance.register(151, 0);
+		instance.register(152, 0);
+		instance.register(153, 0);
+		instance.register(154, 0);
+		instance.register(155, 0);
+		instance.register(156, 0);
+		instance.register(157, 0);
+		instance.register(158, 0);
+		instance.register(159, 0);
+		instance.register(160, 0);
+		instance.register(161, 96);
+		instance.register(162, 97);
+		instance.register(163, 98);
+		instance.register(164, 99);
+		instance.register(165, 100);
+		instance.register(166, 101);
+		instance.register(167, 102);
+		instance.register(168, 103);
+		instance.register(169, 104);
+		instance.register(170, 105);
+		instance.register(171, 106);
+		instance.register(172, 107);
+		instance.register(173, 108);
+		instance.register(174, 109);
+		instance.register(175, 110);
+		instance.register(176, 0);
+		instance.register(177, 111);
+		instance.register(178, 112);
+		instance.register(179, 113);
+		instance.register(180, 114);
+		instance.register(181, 0);
+		instance.register(182, 115);
+		instance.register(183, 116);
+		instance.register(184, 117);
+		instance.register(185, 118);
+		instance.register(186, 119);
+		instance.register(187, 120);
+		instance.register(188, 121);
+		instance.register(189, 122);
+		instance.register(190, 0);
+		instance.register(191, 123);
+		instance.register(192, 0);
+		instance.register(193, 124);
+		instance.register(194, 125);
+		instance.register(195, 126);
+		instance.register(196, 127);
+		instance.register(197, 128);
+		instance.register(198, 129);
+		instance.register(199, 130);
+		instance.register(200, 131);
+		instance.register(201, 0);
+		instance.register(202, 132);
+		instance.register(203, 133);
+		instance.register(204, 0);
+		instance.register(205, 134);
+		instance.register(206, 135);
+		instance.register(207, 136);
+		instance.register(208, 137);
+		instance.register(209, 0);
+		instance.register(210, 0);
+		instance.register(211, 0);
+		instance.register(212, 0);
+		instance.register(213, 0);
+		instance.register(214, 0);
+		instance.register(215, 0);
+		instance.register(216, 0);
+		instance.register(217, 0);
+		instance.register(218, 0);
+		instance.register(219, 0);
+		instance.register(220, 0);
+		instance.register(221, 0);
+		instance.register(222, 0);
+		instance.register(223, 0);
+		instance.register(224, 0);
+		instance.register(225, 138);
+		instance.register(226, 0);
+		instance.register(227, 139);
+		instance.register(228, 0);
+		instance.register(229, 0);
+		instance.register(230, 0);
+		instance.register(231, 0);
+		instance.register(232, 140);
+		instance.register(233, 141);
+		instance.register(234, 142);
+		instance.register(235, 143);
+		instance.register(236, 0);
+		instance.register(237, 0);
+		instance.register(238, 0);
+		instance.register(239, 0);
+		instance.register(240, 0);
+		instance.register(241, 144);
+		instance.register(242, 0);
+		instance.register(243, 0);
+		instance.register(244, 0);
+		instance.register(245, 145);
+		instance.register(246, 0);
+		instance.register(247, 0);
+		instance.register(248, 146);
+		instance.register(249, 147);
+		instance.register(250, 148);
+		instance.register(251, 149);
+		instance.register(252, 0);
+		instance.register(253, 0);
+		instance.register(254, 0);
+		instance.register(255, 0);
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/Type1CharStringFormatter.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/Type1CharStringFormatter.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/Type1CharStringFormatter.java	(revision 0)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+public class Type1CharStringFormatter {
+
+	private ByteArrayOutputStream output = null;
+
+
+	public byte[] format(List<Object> sequence){
+		this.output = new ByteArrayOutputStream();
+
+		for(Object object : sequence){
+
+			if(object instanceof CharStringCommand){
+				writeCommand((CharStringCommand)object);
+			} else
+
+			if(object instanceof Integer){
+				writeNumber((Integer)object);
+			} else
+
+			{
+				throw new IllegalArgumentException();
+			}
+		}
+
+		return this.output.toByteArray();
+	}
+
+	private void writeCommand(CharStringCommand command){
+		int[] value = (command.getKey()).getValue();
+
+		for(int i = 0; i < value.length; i++){
+			this.output.write(value[i]);
+		}
+	}
+
+	private void writeNumber(Integer number){
+		int value = number.intValue();
+
+		if(value >= -107 && value <= 107){
+			this.output.write(value + 139);
+		} else
+
+		if(value >= 108 && value <= 1131){
+			int b1 = (value - 108) % 256;
+			int b0 = (value - 108 - b1) / 256 + 247;
+
+			this.output.write(b0);
+			this.output.write(b1);
+		} else
+
+		if(value >= -1131 && value <= -108){
+			int b1 = -((value + 108) % 256);
+			int b0 = -((value + 108 + b1) / 256 - 251);
+
+			this.output.write(b0);
+			this.output.write(b1);
+		} else
+
+		{
+			int b1 = (value >>> 24) & 0xff;
+			int b2 = (value >>> 16) & 0xff;
+			int b3 = (value >>> 8) & 0xff;
+			int b4 = (value >>> 0) & 0xff;
+
+			this.output.write(255);
+			this.output.write(b1);
+			this.output.write(b2);
+			this.output.write(b3);
+			this.output.write(b4);
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java	(revision 0)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+public class Type1CharStringParser {
+
+	private DataInput input = null;
+
+	private List<Object> sequence = null;
+
+
+	public List<Object> parse(byte[] bytes) throws IOException {
+		this.input = new DataInput(bytes);
+
+		this.sequence = new ArrayList<Object>();
+
+		while(this.input.hasRemaining()){
+			int b0 = this.input.readUnsignedByte();
+
+			if(b0 >= 0 && b0 <= 31){
+				this.sequence.add(readCommand(b0));
+			} else
+
+			if(b0 >= 32 && b0 <= 255){
+				this.sequence.add(readNumber(b0));
+			} else
+
+			{
+				throw new IllegalArgumentException();
+			}
+		}
+
+		return this.sequence;
+	}
+
+	private CharStringCommand readCommand(int b0) throws IOException {
+
+		if(b0 == 12){
+			int b1 = this.input.readUnsignedByte();
+
+			return new CharStringCommand(b0, b1);
+		}
+
+		return new CharStringCommand(b0);
+	}
+
+	private Integer readNumber(int b0) throws IOException {
+
+		if(b0 >= 32 && b0 <= 246){
+			return Integer.valueOf(b0 - 139);
+		} else
+
+		if(b0 >= 247 && b0 <= 250){
+			int b1 = this.input.readUnsignedByte();
+
+			return Integer.valueOf((b0 - 247) * 256 + b1 + 108);
+		} else
+
+		if(b0 >= 251 && b0 <= 254){
+			int b1 = this.input.readUnsignedByte();
+
+			return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108);
+		} else
+
+		if(b0 == 255){
+			int b1 = this.input.readUnsignedByte();
+			int b2 = this.input.readUnsignedByte();
+			int b3 = this.input.readUnsignedByte();
+			int b4 = this.input.readUnsignedByte();
+
+			return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4);
+		} else
+
+		{
+			throw new IllegalArgumentException();
+		}
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/Type1FontFormatter.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/Type1FontFormatter.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/Type1FontFormatter.java	(revision 0)
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+public class Type1FontFormatter {
+
+	private Type1FontFormatter(){
+	}
+
+	static
+	public byte[] format(CFFFont font) throws IOException {
+		DataOutput output = new DataOutput();
+
+		printFont(font, output);
+
+		return output.getBytes();
+	}
+
+	static
+	private void printFont(CFFFont font, DataOutput output) throws IOException {
+		output.println("%!FontType1-1.0 " + font.getName() + " " + font.getProperty("version"));
+
+		printFontDictionary(font, output);
+
+		for(int i = 0; i < 8; i++){
+			StringBuilder sb = new StringBuilder();
+
+			for(int j = 0; j < 64; j++){
+				sb.append("0");
+			}
+
+			output.println(sb.toString());
+		}
+
+		output.println("cleartomark");
+	}
+
+	static
+	private void printFontDictionary(CFFFont font, DataOutput output) throws IOException {
+		output.println("10 dict begin");
+		output.println("/FontInfo 10 dict dup begin");
+		output.println("/version (" + font.getProperty("version") + ") readonly def");
+		output.println("/Notice (" + font.getProperty("Notice") + ") readonly def");
+		output.println("/FullName (" + font.getProperty("FullName") + ") readonly def");
+		output.println("/FamilyName (" + font.getProperty("FamilyName") + ") readonly def");
+		output.println("/Weight (" +font.getProperty("Weight")+ ") readonly def");
+		output.println("/ItalicAngle " + font.getProperty("ItalicAngle") + " def");
+		output.println("/isFixedPitch " + font.getProperty("isFixedPitch") + " def");
+		output.println("/UnderlinePosition " + font.getProperty("UnderlinePosition") + " def");
+		output.println("/UnderlineThickness " + font.getProperty("UnderlineThickness") + " def");
+		output.println("end readonly def");
+		output.println("/FontName /" + font.getName() + " def");
+		output.println("/PaintType " + font.getProperty("PaintType") + " def");
+		output.println("/FontType 1 def");
+		output.println("/FontMatrix " + formatArray(font.getProperty("FontMatrix"), false) + " readonly def");
+		output.println("/FontBBox " + formatArray(font.getProperty("FontBBox"), false) + " readonly def");
+		output.println("/StrokeWidth " + font.getProperty("StrokeWidth") + " def");
+
+		Collection<CFFFont.Mapping> mappings = font.getMappings();
+
+		output.println("/Encoding 256 array");
+		output.println("0 1 255 {1 index exch /.notdef put} for");
+
+		for(CFFFont.Mapping mapping : mappings){
+			output.println("dup " + mapping.getCode() + " /" + mapping.getName() + " put");
+		}
+
+		output.println("readonly def");
+		output.println("currentdict end");
+
+		DataOutput eexecOutput = new DataOutput();
+
+		printEexecFontDictionary(font, eexecOutput);
+
+		output.println("currentfile eexec");
+
+		byte[] eexecBytes = Type1FontUtil.eexecEncrypt(eexecOutput.getBytes());
+
+		String hexString = Type1FontUtil.hexEncode(eexecBytes);
+		for(int i = 0; i < hexString.length(); ){
+			String hexLine = hexString.substring(i, Math.min(i + 72, hexString.length()));
+
+			output.println(hexLine);
+
+			i += hexLine.length();
+		}
+	}
+
+	static
+	private void printEexecFontDictionary(CFFFont font, DataOutput output) throws IOException {
+		output.println("dup /Private 15 dict dup begin");
+		output.println("/RD {string currentfile exch readstring pop} executeonly def");
+		output.println("/ND {noaccess def} executeonly def");
+		output.println("/NP {noaccess put} executeonly def");
+		output.println("/BlueValues " + formatArray(font.getProperty("BlueValues"), true) + " ND");
+		output.println("/OtherBlues " + formatArray(font.getProperty("OtherBlues"), true) + " ND");
+		output.println("/BlueScale " + font.getProperty("BlueScale") + " def");
+		output.println("/BlueShift " + font.getProperty("BlueShift") + " def");
+		output.println("/BlueFuzz " + font.getProperty("BlueFuzz") + " def");
+		output.println("/StdHW "  + formatArray(font.getProperty("StdHW"), true) + " ND");
+		output.println("/StdVW " + formatArray(font.getProperty("StdVW"), true) + " ND");
+		output.println("/ForceBold " + font.getProperty("ForceBold") + " def");
+		output.println("/MinFeature {16 16} def");
+		output.println("/password 5839 def");
+
+		Collection<CFFFont.Mapping> mappings = font.getMappings();
+
+		output.println("2 index /CharStrings " + mappings.size() +  " dict dup begin");
+
+		Type1CharStringFormatter formatter = new Type1CharStringFormatter();
+
+		for(CFFFont.Mapping mapping : mappings){
+			byte[] type1Bytes = formatter.format(mapping.toType1Sequence());
+
+			byte[] charstringBytes = Type1FontUtil.charstringEncrypt(type1Bytes, 4);
+
+			output.print("/" + mapping.getName() + " " + charstringBytes.length + " RD ");
+			output.write(charstringBytes);
+			output.print(" ND");
+
+			output.println();
+		}
+
+		output.println("end");
+		output.println("end");
+
+		output.println("readonly put");
+		output.println("noaccess put");
+		output.println("dup /FontName get exch definefont pop");
+		output.println("mark currentfile closefile");
+	}
+
+	static
+	private String formatArray(Object object, boolean executable){
+		StringBuffer sb = new StringBuffer();
+
+		sb.append(executable ? "{" : "[");
+
+		if(object instanceof Collection){
+			String sep = "";
+
+			Collection<?> elements = (Collection<?>)object;
+			for(Object element : elements){
+				sb.append(sep).append(element);
+
+				sep = " ";
+			}
+		} else
+
+		if(object instanceof Number){
+			sb.append(object);
+		}
+
+		sb.append(executable ? "}" : "]");
+
+		return sb.toString();
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/Type1FontUtil.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/Type1FontUtil.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/Type1FontUtil.java	(revision 0)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+public class Type1FontUtil {
+
+	private Type1FontUtil(){
+	}
+
+	static
+	public String hexEncode(byte[] bytes){
+		StringBuffer sb = new StringBuffer();
+
+		for(int i = 0; i < bytes.length; i++){
+			String string = Integer.toHexString(bytes[i] & 0xff);
+
+			if(string.length() == 1){
+				sb.append("0");
+			}
+
+			sb.append(string.toUpperCase());
+		}
+
+		return sb.toString();
+	}
+
+	static
+	public byte[] hexDecode(String string){
+
+		if(string.length() % 2 != 0){
+			throw new IllegalArgumentException();
+		}
+
+		byte[] bytes = new byte[string.length() / 2];
+
+		for(int i = 0; i < string.length(); i += 2){
+			bytes[i / 2] = (byte)Integer.parseInt(string.substring(i, i + 2), 16);
+		}
+
+		return bytes;
+	}
+
+	static
+	public byte[] eexecEncrypt(byte[] buffer){
+		return encrypt(buffer, 55665, 4);
+	}
+
+	static
+	public byte[] charstringEncrypt(byte[] buffer, int n){
+		return encrypt(buffer, 4330, n);
+	}
+
+	static
+	public byte[] encrypt(byte[] plaintextBytes, int r, int n){
+		byte[] buffer = new byte[plaintextBytes.length + n];
+
+		for(int i = 0; i < n; i++){
+			buffer[i] = 0;
+		}
+
+		System.arraycopy(plaintextBytes, 0, buffer, n, buffer.length - n);
+
+		int c1 = 52845;
+		int c2 = 22719;
+
+		byte[] ciphertextBytes = new byte[buffer.length];
+
+		for(int i = 0; i < buffer.length; i++){
+			int plain = buffer[i] & 0xff;
+			int cipher = (plain ^ (r >> 8));
+
+			ciphertextBytes[i] = (byte)cipher;
+
+			r = ((cipher + r) * c1 + c2) & 0xffff;
+		}
+
+		return ciphertextBytes;
+	}
+
+	static
+	public byte[] eexecDecrypt(byte[] buffer){
+		return decrypt(buffer, 55665, 4);
+	}
+
+	static
+	public byte[] charstringDecrypt(byte[] buffer, int n){
+		return decrypt(buffer, 4330, n);
+	}
+
+	static
+	public byte[] decrypt(byte[] ciphertextBytes, int r, int n){
+		byte[] buffer = new byte[ciphertextBytes.length];
+
+		int c1 = 52845;
+		int c2 = 22719;
+
+		for(int i = 0; i < ciphertextBytes.length; i++){
+			int cipher = ciphertextBytes[i] & 0xff;
+			int plain = (cipher ^ (r >> 8));
+
+			buffer[i] = (byte)plain;
+
+			r = ((cipher + r) * c1 + c2) & 0xffff;
+		}
+
+		byte[] plaintextBytes = new byte[ciphertextBytes.length - n];
+		System.arraycopy(buffer, n, plaintextBytes, 0, plaintextBytes.length);
+
+		return plaintextBytes;
+	}
+}
\ No newline at end of file
Index: src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java
===================================================================
--- src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java	(revision 0)
+++ src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java	(revision 0)
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+public class Type2CharStringParser {
+
+	private DataInput input = null;
+
+	private int hstemCount = 0;
+
+	private int vstemCount = 0;
+
+	private List<Object> sequence = null;
+
+
+	public List<Object> parse(byte[] bytes) throws IOException {
+		this.input = new DataInput(bytes);
+
+		this.hstemCount = 0;
+		this.vstemCount = 0;
+
+		this.sequence = new ArrayList<Object>();
+
+		while(this.input.hasRemaining()){
+			int b0 = this.input.readUnsignedByte();
+
+			if(b0 >= 0 && b0 <= 27){
+				this.sequence.add(readCommand(b0));
+			} else
+
+			if(b0 == 28){
+				this.sequence.add(readNumber(b0));
+			} else
+
+			if(b0 >= 29 && b0 <= 31){
+				this.sequence.add(readCommand(b0));
+			} else
+
+			if(b0 >= 32 && b0 <= 255){
+				this.sequence.add(readNumber(b0));
+			} else
+
+			{
+				throw new IllegalArgumentException();
+			}
+		}
+
+		return this.sequence;
+	}
+
+	private CharStringCommand readCommand(int b0) throws IOException {
+
+		if(b0 == 1 || b0 == 18){
+			this.hstemCount += peekNumbers().size() / 2;
+		} else
+
+		if(b0 == 3 || b0 == 19 || b0 == 20 || b0 == 23){
+			this.vstemCount += peekNumbers().size() / 2;
+		} // End if
+
+		if(b0 == 12){
+			int b1 = this.input.readUnsignedByte();
+
+			return new CharStringCommand(b0, b1);
+		} else
+
+		if(b0 == 19 || b0 == 20){
+			int[] value = new int[1 + getMaskLength()];
+			value[0] = b0;
+
+			for(int i = 1; i < value.length; i++){
+				value[i] = this.input.readUnsignedByte();
+			}
+
+			return new CharStringCommand(value);
+		}
+
+		return new CharStringCommand(b0);
+	}
+
+	private Integer readNumber(int b0) throws IOException {
+
+		if(b0 == 28){
+			int b1 = this.input.readUnsignedByte();
+			int b2 = this.input.readUnsignedByte();
+
+			return Integer.valueOf((short)(b1 << 8 | b2));
+		} else
+
+		if(b0 >= 32 && b0 <= 246){
+			return Integer.valueOf(b0 - 139);
+		} else
+
+		if(b0 >= 247 && b0 <= 250){
+			int b1 = this.input.readUnsignedByte();
+
+			return Integer.valueOf((b0 - 247) * 256 + b1 + 108);
+		} else
+
+		if(b0 >= 251 && b0 <= 254){
+			int b1 = this.input.readUnsignedByte();
+
+			return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108);
+		} else
+
+		if(b0 == 255){
+			int b1 = this.input.readUnsignedByte();
+			int b2 = this.input.readUnsignedByte();
+			int b3 = this.input.readUnsignedByte();
+			int b4 = this.input.readUnsignedByte();
+
+			return Integer.valueOf((short)(b1 << 24 | b2 << 16 | b3 << 8 | b4));
+		} else
+
+		{
+			throw new IllegalArgumentException();
+		}
+	}
+
+	private int getMaskLength(){
+		int length = 1;
+
+		int hintCount = this.hstemCount + this.vstemCount;
+		while((hintCount -= 8) > 0){
+			length++;
+		}
+
+		return length;
+	}
+
+	private List<Number> peekNumbers(){
+		List<Number> numbers = new ArrayList<Number>();
+
+		for(int i = this.sequence.size() - 1; i > -1; i--){
+			Object object = this.sequence.get(i);
+
+			if(object instanceof Number){
+				Number number = (Number)object;
+
+				numbers.add(0, number);
+
+				continue;
+			}
+
+			return numbers;
+		}
+
+		return numbers;
+	}
+}
\ No newline at end of file
Index: src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java
===================================================================
--- src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java	(revision 0)
+++ src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java	(revision 0)
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.io.*;
+import java.util.*;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+public class Type1CharStringTest {
+
+	@Test
+	public void commandEncoding() throws IOException {
+		List<Object> commands = createCommandSequence(new int[]{0}, new int[]{12, 0}, new int[]{31});
+
+		byte[] encodedCommands = new Type1CharStringFormatter().format(commands);
+		List<Object> decodedCommands = new Type1CharStringParser().parse(encodedCommands);
+
+		assertEquals(1 + 2 + 1, encodedCommands.length);
+
+		assertEquals(commands, decodedCommands);
+	}
+
+	@Test
+	public void numberEncoding() throws IOException {
+		List<Object> numbers = createNumberSequence(-10000, -1131, -108, -107, 0, 107, 108, 1131, 10000);
+
+		byte[] encodedNumbers = new Type1CharStringFormatter().format(numbers);
+		List<Object> decodedNumbers = new Type1CharStringParser().parse(encodedNumbers);
+
+		assertEquals(5 + 2 * 2 + 3 * 1 + 2 * 2 + 5, encodedNumbers.length);
+
+		assertEquals(numbers, decodedNumbers);
+	}
+
+	static
+	private List<Object> createCommandSequence(int[]... values){
+		List<Object> sequence = new ArrayList<Object>();
+
+		for(int[] value : values){
+			sequence.add(value.length > 1 ? new CharStringCommand(value[0], value[1]) : new CharStringCommand(value[0]));
+		}
+
+		return sequence;
+	}
+
+	static
+	private List<Object> createNumberSequence(int... values){
+		List<Object> sequence = new ArrayList<Object>();
+
+		for(int value : values){
+			sequence.add(Integer.valueOf(value));
+		}
+
+		return sequence;
+	}
+}
\ No newline at end of file
Index: src/test/java/org/apache/fontbox/cff/Type1FontUtilTest.java
===================================================================
--- src/test/java/org/apache/fontbox/cff/Type1FontUtilTest.java	(revision 0)
+++ src/test/java/org/apache/fontbox/cff/Type1FontUtilTest.java	(revision 0)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009 Villu Ruusmann
+ */
+package org.apache.fontbox.cff;
+
+import java.util.*;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+public class Type1FontUtilTest {
+
+	@Test
+	public void hexEncoding(){
+		byte[] bytes = randomBytes(128);
+
+		String encodedBytes = Type1FontUtil.hexEncode(bytes);
+		byte[] decodedBytes = Type1FontUtil.hexDecode(encodedBytes);
+
+		assertArrayEquals(bytes, decodedBytes);
+	}
+
+	@Test
+	public void eexecEncryption(){
+		byte[] bytes = randomBytes(128);
+
+		byte[] encryptedBytes = Type1FontUtil.eexecEncrypt(bytes);
+		byte[] decryptedBytes = Type1FontUtil.eexecDecrypt(encryptedBytes);
+
+		assertArrayEquals(bytes, decryptedBytes);
+	}
+
+	@Test
+	public void charstringEncryption(){
+		byte[] bytes = randomBytes(128);
+
+		byte[] encryptedBytes = Type1FontUtil.charstringEncrypt(bytes, 4);
+		byte[] decryptedBytes = Type1FontUtil.charstringDecrypt(encryptedBytes, 4);
+
+		assertArrayEquals(bytes, decryptedBytes);
+	}
+
+	static
+	private byte[] randomBytes(int length){
+		byte[] bytes = new byte[length];
+
+		for(int i = 0; i < length; i++){
+			bytes[i] = (byte)random.nextInt(256);
+		}
+
+		return bytes;
+	}
+
+	private static final Random random = new Random();
+}
\ No newline at end of file
