]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
First part of my refactoring of fonts.
authorJeremias Maerki <jeremias@apache.org>
Wed, 8 Jan 2003 13:54:04 +0000 (13:54 +0000)
committerJeremias Maerki <jeremias@apache.org>
Wed, 8 Jan 2003 13:54:04 +0000 (13:54 +0000)
TrueType font classes have moved to subpackage (like Type1 before)
Lots of Javadocs
Fixed Checkstyle errors

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@195819 13f79535-47bb-0310-9956-ffa450edef68

26 files changed:
src/org/apache/fop/fonts/BFEntry.java [new file with mode: 0644]
src/org/apache/fop/fonts/CIDFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/CIDFontType.java [new file with mode: 0644]
src/org/apache/fop/fonts/CustomFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/Font.java [new file with mode: 0644]
src/org/apache/fop/fonts/FontDescriptor.java [new file with mode: 0644]
src/org/apache/fop/fonts/FontFileReader.java [deleted file]
src/org/apache/fop/fonts/FontMetrics.java [new file with mode: 0644]
src/org/apache/fop/fonts/FontType.java [new file with mode: 0644]
src/org/apache/fop/fonts/LazyFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/MultiByteFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/MutableFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/SingleByteFont.java [new file with mode: 0644]
src/org/apache/fop/fonts/TTFCmapEntry.java [deleted file]
src/org/apache/fop/fonts/TTFDirTabEntry.java [deleted file]
src/org/apache/fop/fonts/TTFFile.java [deleted file]
src/org/apache/fop/fonts/TTFMtxEntry.java [deleted file]
src/org/apache/fop/fonts/TTFSubSetFile.java [deleted file]
src/org/apache/fop/fonts/apps/TTFReader.java
src/org/apache/fop/fonts/package.html [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/FontFileReader.java [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/TTFCmapEntry.java [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/TTFDirTabEntry.java [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/TTFFile.java [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/TTFMtxEntry.java [new file with mode: 0644]
src/org/apache/fop/fonts/truetype/TTFSubSetFile.java [new file with mode: 0644]

diff --git a/src/org/apache/fop/fonts/BFEntry.java b/src/org/apache/fop/fonts/BFEntry.java
new file mode 100644 (file)
index 0000000..726f28a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * This is just a holder class for bfentries.
+ */
+public class BFEntry {
+    
+    private int unicodeStart;
+    private int unicodeEnd;
+    private int glyphStartIndex;
+
+    /**
+     * Main constructor.
+     * @param unicodeStart Unicode start index
+     * @param unicodeEnd Unicode end index
+     * @param glyphStartIndex glyph start index
+     */
+    public BFEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+        this.unicodeStart = unicodeStart;
+        this.unicodeEnd = unicodeEnd;
+        this.glyphStartIndex = glyphStartIndex;
+    }
+
+    /**
+     * Returns the unicodeStart.
+     * @return the Unicode start index
+     */
+    public int getUnicodeStart() {
+        return unicodeStart;
+    }
+
+    /**
+     * Returns the unicodeEnd.
+     * @return the Unicode end index
+     */
+    public int getUnicodeEnd() {
+        return unicodeEnd;
+    }
+
+    /**
+     * Returns the glyphStartIndex.
+     * @return the glyph start index
+     */
+    public int getGlyphStartIndex() {
+        return glyphStartIndex;
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/CIDFont.java b/src/org/apache/fop/fonts/CIDFont.java
new file mode 100644 (file)
index 0000000..fc9cc90
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * Abstract base class for CID fonts.
+ */
+public abstract class CIDFont extends CustomFont {
+
+    // ---- Required ----
+    /**
+     * Returns the name of the base font.
+     * @return the name of the base font
+     */
+    public abstract String getCidBaseFont();
+    
+    /**
+     * Returns the type of the CID font.
+     * @return the type of the CID font
+     */
+    public abstract CIDFontType getCIDType();
+    
+    /**
+     * Returns the name of the issuer of the font.
+     * @return a String identifying an issuer of character collections \97 
+     * for example, Adobe
+     */
+    public abstract String getRegistry();
+    
+    /**
+     * Returns a font name for use within a registry.
+     * @return a String that uniquely names a character collection issued by 
+     * a specific registry \97 for example, Japan1.
+     */
+    public abstract String getOrdering();
+    
+    /**
+     * Returns the supplement number of the character collection.
+     * @return the supplement number
+     */
+    public abstract int getSupplement();
+    
+
+    // ---- Optional ----
+    /**
+     * Returns the default width for this font.
+     * @return the default width
+     */
+    public int getDefaultWidth() {
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.Font#isMultiByte()
+     */
+    public boolean isMultiByte() {
+        return true;
+    }
+}
diff --git a/src/org/apache/fop/fonts/CIDFontType.java b/src/org/apache/fop/fonts/CIDFontType.java
new file mode 100644 (file)
index 0000000..938b7ae
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import org.apache.avalon.framework.ValuedEnum;
+
+/**
+ * This class enumerates all supported CID font types.
+ */
+public class CIDFontType extends ValuedEnum {
+
+    /**
+     * CID Font Type 0
+     */
+    public static final CIDFontType CIDTYPE0 = new CIDFontType("CIDFontType0", 0);
+
+    /**
+     * CID Font Type 2
+     */
+    public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1);
+
+
+    /**
+     * @see org.apache.avalon.framework.Enum#Enum(String)
+     */
+    protected CIDFontType(String name, int value) {
+        super(name, value);
+    }
+
+
+    /**
+     * Returns the CIDFontType by name.
+     * @param name Name of the CID font type to look up
+     * @return FontType the CID font type
+     */
+    public static CIDFontType byName(String name) {
+        if (name.equalsIgnoreCase(CIDFontType.CIDTYPE0.getName())) {
+            return CIDFontType.CIDTYPE0;
+        } else if (name.equalsIgnoreCase(CIDFontType.CIDTYPE2.getName())) {
+            return CIDFontType.CIDTYPE2;
+        } else {
+            throw new IllegalArgumentException("Invalid CID font type: " + name);
+        }
+    }
+    
+    
+    /**
+     * Returns the CID FontType by value.
+     * @param value Value of the CID font type to look up
+     * @return FontType the CID font type
+     */
+    public static CIDFontType byValue(int value) {
+        if (value == CIDFontType.CIDTYPE0.getValue()) {
+            return CIDFontType.CIDTYPE0;
+        } else if (value == CIDFontType.CIDTYPE2.getValue()) {
+            return CIDFontType.CIDTYPE2;
+        } else {
+            throw new IllegalArgumentException("Invalid CID font type: " + value);
+        }
+    }
+    
+}
diff --git a/src/org/apache/fop/fonts/CustomFont.java b/src/org/apache/fop/fonts/CustomFont.java
new file mode 100644 (file)
index 0000000..02e4cd6
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * Abstract base class for custom fonts loaded from files, for example.
+ */
+public abstract class CustomFont extends Font 
+            implements FontDescriptor, MutableFont {
+
+    private String fontName = null;
+    private String embedFileName = null;
+    private String embedResourceName = null;
+
+    private int capHeight = 0;
+    private int xHeight = 0;
+    private int ascender = 0;
+    private int descender = 0;
+    private int[] fontBBox = {0, 0, 0, 0};
+    private int flags = 4;
+    private int stemV = 0;
+    private int italicAngle = 0;
+    private int missingWidth = 0;
+    private FontType fontType = FontType.TYPE1;
+    private int firstChar = 0;
+    private int lastChar = 255;
+    
+    private Map kerning = new java.util.HashMap();
+   
+
+    private boolean useKerning = true;
+
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getFontName()
+     */
+    public String getFontName() {
+        return fontName;
+    }
+
+    /**
+     * Returns an URI representing an embeddable font file. The URI will often
+     * be a filename or an URL.
+     * @return URI to an embeddable font file or null if not available.
+     */
+    public String getEmbedFileName() {
+        return embedFileName;
+    }
+
+    /**
+     * Returns the lookup name to an embeddable font file available as a 
+     * resource.
+     * @todo Remove this method, this should be done using a resource: URI.
+     * @return the lookup name
+     */
+    public String getEmbedResourceName() {
+        return embedResourceName;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getAscender()
+     */
+    public int getAscender() {
+        return ascender;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getDescender()
+     */
+    public int getDescender() {
+        return descender;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getCapHeight()
+     */
+    public int getCapHeight() {
+        return capHeight;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getAscender(int)
+     */
+    public int getAscender(int size) {
+        return size * ascender;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getDescender(int)
+     */
+    public int getDescender(int size) {
+        return size * descender;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getCapHeight(int)
+     */
+    public int getCapHeight(int size) {
+        return size * capHeight;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getXHeight(int)
+     */
+    public int getXHeight(int size) {
+        return size * xHeight;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFontBBox()
+     */
+    public int[] getFontBBox() {
+        return fontBBox;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFlags()
+     */
+    public int getFlags() {
+        return flags;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getStemV()
+     */
+    public int getStemV() {
+        return stemV;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getItalicAngle()
+     */
+    public int getItalicAngle() {
+        return italicAngle;
+    }
+
+    /**
+     * Returns the width to be used when no width is available.
+     * @return a character width 
+     */
+    public int getMissingWidth() {
+        return missingWidth;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFontType()
+     */
+    public FontType getFontType() {
+        return fontType;
+    }
+    
+    /**
+     * Returns the index of the first character defined in this font.
+     * @return the index of the first character
+     */
+    public int getFirstChar() {
+        return 0;
+        // return firstChar;
+        /**@todo Why is this hardcoded??? This code was in SingleByteFont.java */
+    }
+
+    /**
+     * Returns the index of the last character defined in this font.
+     * @return the index of the last character
+     */
+    public int getLastChar() {
+        return lastChar;
+    }
+
+    /**
+     * Used to determine if kerning is enabled.
+     * @return True if kerning is enabled.
+     */
+    public boolean isKerningEnabled() {
+        return useKerning;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#hasKerningInfo()
+     */
+    public final boolean hasKerningInfo() {
+        return (isKerningEnabled() & kerning.isEmpty());
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getKerningInfo()
+     */
+    public final Map getKerningInfo() {
+        if (isKerningEnabled()) {
+            return kerning;
+        } else {
+            return java.util.Collections.EMPTY_MAP;
+        }
+    }
+
+
+    /* ---- MutableFont interface ---- */
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setFontName(String)
+     */
+    public void setFontName(String name) {
+        this.fontName = name;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setEmbedFileName(String)
+     */
+    public void setEmbedFileName(String path) {
+        this.embedFileName = path;
+    }
+        
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setEmbedResourceName(String)
+     */
+    public void setEmbedResourceName(String name) {
+        this.embedResourceName = name;
+    }
+        
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setCapHeight(int)
+     */
+    public void setCapHeight(int capHeight) {
+        this.capHeight = capHeight;
+    }
+    
+    /**
+     * Returns the XHeight value of the font.
+     * @param xHeight the XHeight value
+     */
+    public void setXHeight(int xHeight) {
+        this.xHeight = xHeight;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setAscender(int)
+     */
+    public void setAscender(int ascender) {
+        this.ascender = ascender;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setDescender(int)
+     */
+    public void setDescender(int descender) {
+        this.descender = descender;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setFontBBox(int[])
+     */
+    public void setFontBBox(int[] bbox) {
+        this.fontBBox = bbox;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setFlags(int)
+     */
+    public void setFlags(int flags) {
+        this.flags = flags;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setStemV(int)
+     */
+    public void setStemV(int stemV) {
+        this.stemV = stemV;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setItalicAngle(int)
+     */
+    public void setItalicAngle(int italicAngle) {
+        this.italicAngle = italicAngle;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setMissingWidth(int)
+     */
+    public void setMissingWidth(int width) {
+        this.missingWidth = width;
+    }
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setFontType(FontType)
+     */
+    public void setFontType(FontType fontType) {
+        this.fontType = fontType;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setFirstChar(int)
+     */
+    public void setFirstChar(int index) {
+        this.firstChar = index;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setLastChar(int)
+     */
+    public void setLastChar(int index) {
+        this.lastChar = index;
+    }
+    
+    /**
+     * @see org.apache.fop.fonts.MutableFont#setKerningEnabled(boolean)
+     */
+    public void setKerningEnabled(boolean enabled) {
+        this.useKerning = enabled;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.MutableFont#putKerningEntry(Integer, Map)
+     */
+    public void putKerningEntry(Integer key, Map value) {
+        this.kerning.put(key, value);
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/Font.java b/src/org/apache/fop/fonts/Font.java
new file mode 100644 (file)
index 0000000..a51a545
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+// FOP
+
+
+/**
+ * Base class for PDF font classes
+ */
+public abstract class Font implements FontMetrics {
+
+    /**
+     * Get the encoding of the font.
+     * @return the encoding
+     */
+    public abstract String getEncoding();
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public abstract char mapChar(char c);
+
+    /**
+     * Determines whether the font is a multibyte font.
+     * @return True if it is multibyte
+     */
+    public boolean isMultiByte() {
+        return false;
+    }
+
+}
+
diff --git a/src/org/apache/fop/fonts/FontDescriptor.java b/src/org/apache/fop/fonts/FontDescriptor.java
new file mode 100644 (file)
index 0000000..0f929ff
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * This interface enhances the font metrics interface with access methods to
+ * value needed to register fonts in various target formats like PDF or 
+ * PostScript.
+ */
+public interface FontDescriptor extends FontMetrics {
+
+    /**
+     * Returns the ascender value of the font. (Ascent in pdf spec)
+     * @return the ascender
+     */
+    int getAscender();
+    
+    
+    /**
+     * Returns the capital height of the font.
+     * @return the capiptal height
+     */
+    int getCapHeight();
+    
+    
+    /**
+     * Returns the descender value of the font. (Descent in pdf spec)
+     * @return the descender value
+     */
+    int getDescender();
+    
+    
+    /**
+     * Returns the flags for the font. (See pdf spec)
+     * @return the flags
+     */
+    int getFlags();
+    
+    
+    /**
+     * Returns the font's bounding box.
+     * @return the bounding box
+     */
+    int[] getFontBBox();
+    
+    
+    /**
+     * Returns the italic angle for the font.
+     * @return the italic angle
+     */
+    int getItalicAngle();
+    
+    
+    /**
+     * Returns the vertical stem width for the font.
+     * @return the vertical stem width
+     */
+    int getStemV();
+
+    
+    /**
+     * Indicates if this font may be embedded.
+     * @return True, if embedding is possible/permitted
+     */
+    boolean isEmbeddable();
+    
+    
+}
diff --git a/src/org/apache/fop/fonts/FontFileReader.java b/src/org/apache/fop/fonts/FontFileReader.java
deleted file mode 100644 (file)
index ff205a7..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Reads a file into an array and
- * provides file like functions for array access.
- */
-public class FontFileReader {
-
-    private int fsize;      // file size
-    private int current;    // current position in file
-    private byte[] file;
-
-    /**
-     * Initializes class and reads stream. Init does not close stream.
-     *
-     * @param in InputStream to read from new array with size + inc
-     * @throws IOException In case of an I/O problem
-     */
-    private void init(InputStream in) throws java.io.IOException {
-        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
-        try {
-            copyStream(in, bout);
-            this.file = bout.toByteArray();
-            this.fsize = this.file.length;
-            this.current = 0;
-        } finally {
-            bout.close();
-        }
-    }
-
-    /**@todo Use method from Avalon Excalibur IO or Jakarta Commons IO*/
-    private void copyStream(InputStream in, OutputStream out) throws IOException {
-        final int bufferSize = 2048;
-        final byte[] buf = new byte[bufferSize];
-        int bytesRead;
-        while ((bytesRead = in.read(buf)) != -1) {
-            out.write(buf, 0, bytesRead);
-        }
-    }
-
-    /**
-     * Constructor
-     *
-     * @param fileName filename to read
-     * @throws IOException In case of an I/O problem
-     */
-    public FontFileReader(String fileName) throws IOException {
-        final File f = new File(fileName);
-        InputStream in = new java.io.FileInputStream(f);
-        try {
-            init(in);
-        } finally {
-            in.close();
-        }
-    }
-
-
-    /**
-     * Constructor
-     *
-     * @param in InputStream to read from
-     * @throws IOException In case of an I/O problem
-     */
-    public FontFileReader(InputStream in) throws IOException {
-        init(in);
-    }
-
-
-    /**
-     * Set current file position to offset
-     *
-     * @param offset The new offset to set
-     * @throws IOException In case of an I/O problem
-     */
-    public void seekSet(long offset) throws IOException {
-        if (offset > fsize || offset < 0) {
-            throw new java.io.EOFException("Reached EOF, file size=" + fsize
-                                           + " offset=" + offset);
-        }
-        current = (int)offset;
-    }
-
-    /**
-     * Set current file position to offset
-     *
-     * @param add The number of bytes to advance
-     * @throws IOException In case of an I/O problem
-     */
-    public void seekAdd(long add) throws IOException {
-        seekSet(current + add);
-    }
-
-    /**
-     * Skip a given number of bytes.
-     *
-     * @param add The number of bytes to advance
-     * @throws IOException In case of an I/O problem
-     */
-    public void skip(long add) throws IOException {
-        seekAdd(add);
-    }
-
-    /**
-     * Returns current file position.
-     *
-     * @return int The current position.
-     */
-    public int getCurrentPos() {
-        return current;
-    }
-
-    /**
-     * Returns the size of the file.
-     *
-     * @return int The filesize
-     */
-    public int getFileSize() {
-        return fsize;
-    }
-
-    /**
-     * Read 1 byte.
-     *
-     * @return One byte
-     * @throws IOException If EOF is reached
-     */
-    public byte read() throws IOException {
-        if (current > fsize) {
-            throw new java.io.EOFException("Reached EOF, file size=" + fsize);
-        }
-
-        final byte ret = file[current++];
-        return ret;
-    }
-
-    /**
-     * Read 1 signed byte.
-     *
-     * @return One byte
-     * @throws IOException If EOF is reached
-     */
-    public final byte readTTFByte() throws IOException {
-        return read();
-    }
-
-    /**
-     * Read 1 unsigned byte.
-     *
-     * @return One unsigned byte
-     * @throws IOException If EOF is reached
-     */
-    public final int readTTFUByte() throws IOException {
-        final byte buf = read();
-
-        if (buf < 0) {
-            return (int)(256 + buf);
-        } else {
-            return (int)buf;
-        }
-    }
-
-    /**
-     * Read 2 bytes signed.
-     *
-     * @return One signed short
-     * @throws IOException If EOF is reached
-     */
-    public final short readTTFShort() throws IOException {
-        final int ret = (readTTFUByte() << 8) + readTTFUByte();
-        final short sret = (short)ret;
-        return sret;
-    }
-
-    /**
-     * Read 2 bytes unsigned.
-     *
-     * @return One unsigned short
-     * @throws IOException If EOF is reached
-     */
-    public final int readTTFUShort() throws IOException {
-        final int ret = (readTTFUByte() << 8) + readTTFUByte();
-        return (int)ret;
-    }
-
-    /**
-     * Write a USHort at a given position.
-     *
-     * @param pos The absolute position to write to
-     * @param val The value to write
-     * @throws IOException If EOF is reached
-     */
-    public final void writeTTFUShort(int pos, int val) throws IOException {
-        if ((pos + 2) > fsize) {
-            throw new java.io.EOFException("Reached EOF");
-        }
-        final byte b1 = (byte)((val >> 8) & 0xff);
-        final byte b2 = (byte)(val & 0xff);
-        file[pos] = b1;
-        file[pos + 1] = b2;
-    }
-
-    /**
-     * Read 2 bytes signed at position pos without changing current position.
-     *
-     * @param pos The absolute position to read from
-     * @return One signed short
-     * @throws IOException If EOF is reached
-     */
-    public final short readTTFShort(long pos) throws IOException {
-        final long cp = getCurrentPos();
-        seekSet(pos);
-        final short ret = readTTFShort();
-        seekSet(cp);
-        return ret;
-    }
-
-    /**
-     * Read 2 bytes unsigned at position pos without changing current position.
-     *
-     * @param pos The absolute position to read from
-     * @return One unsigned short
-     * @throws IOException If EOF is reached
-     */
-    public final int readTTFUShort(long pos) throws IOException {
-        long cp = getCurrentPos();
-        seekSet(pos);
-        int ret = readTTFUShort();
-        seekSet(cp);
-        return ret;
-    }
-
-    /**
-     * Read 4 bytes.
-     *
-     * @return One signed integer
-     * @throws IOException If EOF is reached
-     */
-    public final int readTTFLong() throws IOException {
-        long ret = readTTFUByte();    // << 8;
-        ret = (ret << 8) + readTTFUByte();
-        ret = (ret << 8) + readTTFUByte();
-        ret = (ret << 8) + readTTFUByte();
-
-        return (int)ret;
-    }
-
-    /**
-     * Read 4 bytes.
-     *
-     * @return One unsigned integer
-     * @throws IOException If EOF is reached
-     */
-    public final long readTTFULong() throws IOException {
-        long ret = readTTFUByte();
-        ret = (ret << 8) + readTTFUByte();
-        ret = (ret << 8) + readTTFUByte();
-        ret = (ret << 8) + readTTFUByte();
-
-        return ret;
-    }
-
-    /**
-     * Read a NUL terminated ISO-8859-1 string.
-     *
-     * @return A String
-     * @throws IOException If EOF is reached
-     */
-    public final String readTTFString() throws IOException {
-        int i = current;
-        while (file[i++] != 0) {
-            if (i > fsize) {
-                throw new java.io.EOFException("Reached EOF, file size="
-                                               + fsize);
-            }
-        }
-
-        byte[] tmp = new byte[i - current];
-        System.arraycopy(file, current, tmp, 0, i - current);
-        return new String(tmp, "ISO-8859-1");
-    }
-
-
-    /**
-     * Read an ISO-8859-1 string of len bytes.
-     *
-     * @param len The length of the string to read
-     * @return A String
-     * @throws IOException If EOF is reached
-     */
-    public final String readTTFString(int len) throws IOException {
-        if ((len + current) > fsize) {
-            throw new java.io.EOFException("Reached EOF, file size=" + fsize);
-        }
-
-        byte[] tmp = new byte[len];
-        System.arraycopy(file, current, tmp, 0, len);
-        current += len;
-        return new String(tmp, "ISO-8859-1");
-    }
-
-    /**
-     * Return a copy of the internal array
-     *
-     * @param offset The absolute offset to start reading from
-     * @param length The number of bytes to read
-     * @return An array of bytes
-     * @throws IOException if out of bounds
-     */
-    public byte[] getBytes(int offset,
-                           int length) throws IOException {
-        if ((offset + length) > fsize) {
-            throw new java.io.IOException("Reached EOF");
-        }
-
-        byte[] ret = new byte[length];
-        System.arraycopy(file, offset, ret, 0, length);
-        return ret;
-    }
-
-
-}
\ No newline at end of file
diff --git a/src/org/apache/fop/fonts/FontMetrics.java b/src/org/apache/fop/fonts/FontMetrics.java
new file mode 100644 (file)
index 0000000..4d79051
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * Main interface for access to font metrics.
+ */
+public interface FontMetrics {
+
+    /**
+     * Returns the font name.
+     * @return the font name
+     */
+    String getFontName();
+    
+    
+    /**
+     * Returns the type of the font.
+     * @return the font type
+     */
+    FontType getFontType();
+    
+
+    /**
+     * Returns the ascent of the font described by this
+     * FontMetrics object.
+     * @param size font size
+     * @return ascent in milliponts
+     */
+    int getAscender(int size);
+    
+    /**
+     * Returns the size of a capital letter measured from the font's baseline.
+     * @param size font size
+     * @return height of capital characters
+     */
+    int getCapHeight(int size);
+    
+    
+    /**
+     * Returns the descent of the font described by this
+     * FontMetrics object.
+     * @param size font size
+     * @return descent in milliponts
+     */
+    int getDescender(int size);
+    
+    
+    /**
+     * Determines the typical font height of this
+     * FontMetrics object
+     * @param size font size
+     * @return font height in millipoints
+     */
+    int getXHeight(int size);
+
+    /**
+     * Return the width (in 1/1000ths of point size) of the character at
+     * code point i.
+     * @param i code point index
+     * @param size font size
+     * @return the width of the character
+     */
+    int getWidth(int i, int size);
+
+    /**
+     * Return the array of widths.
+     * <p>
+     * This is used to get an array for inserting in an output format.
+     * It should not be used for lookup.
+     * @return an array of widths
+     */
+    int[] getWidths();
+    
+    /**
+     * Indicates if the font has kering information.
+     * @return True, if kerning is available.
+     */
+    boolean hasKerningInfo();
+        
+    /**
+     * Returns the kerning map for the font.
+     * @return the kerning map
+     */
+    Map getKerningInfo();
+    
+}
diff --git a/src/org/apache/fop/fonts/FontType.java b/src/org/apache/fop/fonts/FontType.java
new file mode 100644 (file)
index 0000000..cfaaa43
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import org.apache.avalon.framework.ValuedEnum;
+
+/**
+ * This class enumerates all supported font types.
+ */
+public class FontType extends ValuedEnum {
+
+    /**
+     * Collective identifier for "other" font types
+     */
+    public static final FontType OTHER       = new FontType("Other", 0);
+    /**
+     * Adobe Type 0 fonts
+     */
+    public static final FontType TYPE0       = new FontType("Type0", 1);
+    /**
+     * Adobe Type 1 fonts
+     */
+    public static final FontType TYPE1       = new FontType("Type1", 2);
+    /**
+     * Adobe Multiple Master Type 1 fonts
+     */
+    public static final FontType MMTYPE1     = new FontType("MMType1", 3);
+    /**
+     * Adobe Type 3 fonts ("user-defined" fonts)
+     */
+    public static final FontType TYPE3       = new FontType("Type3", 4);
+    /**
+     * TrueType fonts
+     */
+    public static final FontType TRUETYPE    = new FontType("TrueType", 5);
+
+
+    /**
+     * @see org.apache.avalon.framework.Enum#Enum(String)
+     */
+    protected FontType(String name, int value) {
+        super(name, value);
+    }
+
+
+    /**
+     * Returns the FontType by name.
+     * @param name Name of the font type to look up
+     * @return the font type
+     */
+    public static FontType byName(String name) {
+        if (name.equalsIgnoreCase(FontType.OTHER.getName())) {
+            return FontType.OTHER;
+        } else if (name.equalsIgnoreCase(FontType.TYPE0.getName())) {
+            return FontType.TYPE0;
+        } else if (name.equalsIgnoreCase(FontType.TYPE1.getName())) {
+            return FontType.TYPE1;
+        } else if (name.equalsIgnoreCase(FontType.MMTYPE1.getName())) {
+            return FontType.MMTYPE1;
+        } else if (name.equalsIgnoreCase(FontType.TYPE3.getName())) {
+            return FontType.TYPE3;
+        } else if (name.equalsIgnoreCase(FontType.TRUETYPE.getName())) {
+            return FontType.TRUETYPE;
+        } else {
+            throw new IllegalArgumentException("Invalid font type: " + name);
+        }
+    }
+    
+    
+    /**
+     * Returns the FontType by value.
+     * @param value Value of the font type to look up
+     * @return the font type
+     */
+    public static FontType byValue(int value) {
+        if (value == FontType.OTHER.getValue()) {
+            return FontType.OTHER;
+        } else if (value == FontType.TYPE0.getValue()) {
+            return FontType.TYPE0;
+        } else if (value == FontType.TYPE1.getValue()) {
+            return FontType.TYPE1;
+        } else if (value == FontType.MMTYPE1.getValue()) {
+            return FontType.MMTYPE1;
+        } else if (value == FontType.TYPE3.getValue()) {
+            return FontType.TYPE3;
+        } else if (value == FontType.TRUETYPE.getValue()) {
+            return FontType.TRUETYPE;
+        } else {
+            throw new IllegalArgumentException("Invalid font type: " + value);
+        }
+    }
+    
+}
diff --git a/src/org/apache/fop/fonts/LazyFont.java b/src/org/apache/fop/fonts/LazyFont.java
new file mode 100644 (file)
index 0000000..7f90f92
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+//Java
+import java.util.Map;
+
+//FOP
+import org.apache.fop.render.pdf.FontReader;
+
+/**
+ * This class is used to defer the loading of a font until it is really used.
+ */
+public class LazyFont extends Font implements FontDescriptor {
+
+    private String metricsFileName = null;
+    private String fontEmbedPath = null;
+    private boolean useKerning = false;
+
+    private boolean isMetricsLoaded = false;
+    private Font realFont = null;
+    private FontDescriptor realFontDescriptor = null;
+
+    /**
+     * Main constructor
+     * @param fontEmbedPath path to embeddable file (may be null)
+     * @param metricsFileName path to the metrics XML file
+     * @param useKerning True, if kerning should be enabled
+     */
+    public LazyFont(String fontEmbedPath, String metricsFileName, boolean useKerning) {
+        this.metricsFileName = metricsFileName;
+        this.fontEmbedPath = fontEmbedPath;
+        this.useKerning = useKerning;
+    }
+
+    private void load() {
+        if (!isMetricsLoaded) {
+            isMetricsLoaded = true;
+            try {
+                /**@todo Possible thread problem here */
+
+                FontReader reader = new FontReader(metricsFileName);
+                reader.setKerningEnabled(useKerning);
+                reader.setFontEmbedPath(fontEmbedPath);
+                realFont = reader.getFont();
+                if (realFont instanceof FontDescriptor) {
+                    realFontDescriptor = (FontDescriptor) realFont;
+                }
+                // System.out.println("Metrics " + metricsFileName + " loaded.");
+            } catch (Exception ex) {
+                /**@todo Log this exception */
+                //log.error("Failed to read font metrics file "
+                //                     + metricsFileName
+                //                     + " : " + ex.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Gets the real font.
+     * @return the real font
+     */
+    public Font getRealFont() {
+        load();
+        return realFont;
+    }
+
+    // ---- Font ----
+    /**
+     * @see org.apache.fop.fonts.Font#getEncoding()
+     */
+    public String getEncoding() {
+        load();
+        return realFont.getEncoding();
+    }
+
+    /**
+     * @see org.apache.fonts.pdf.Font#mapChar(char)
+     */
+    public char mapChar(char c) {
+        load();
+        return realFont.mapChar(c);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.Font#isMultiByte()
+     */
+    public boolean isMultiByte() {
+        return realFont.isMultiByte();
+    }
+
+    // ---- FontMetrics interface ----
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getFontName()
+     */
+    public String getFontName() {
+        load();
+        return realFont.getFontName();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getAscender(int)
+     */
+    public int getAscender(int size) {
+        load();
+        return realFont.getAscender(size);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getCapHeight(int)
+     */
+    public int getCapHeight(int size) {
+        load();
+        return realFont.getCapHeight(size);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getDescender(int)
+     */
+    public int getDescender(int size) {
+        load();
+        return realFont.getDescender(size);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getXHeight(int)
+     */
+    public int getXHeight(int size) {
+        load();
+        return realFont.getXHeight(size);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+     */
+    public int getWidth(int i, int size) {
+        load();
+        return realFont.getWidth(i, size);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidths()
+     */
+    public int[] getWidths() {
+        load();
+        return realFont.getWidths();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#hasKerningInfo()
+     */
+    public boolean hasKerningInfo() {
+        load();
+        return realFont.hasKerningInfo();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getKerningInfo()
+     */
+    public Map getKerningInfo() {
+        load();
+        return realFont.getKerningInfo();
+    }
+
+    // ---- FontDescriptor interface ----
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getCapHeight()
+     */
+    public int getCapHeight() {
+        load();
+        return realFontDescriptor.getCapHeight();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getDescender()
+     */
+    public int getDescender() {
+        load();
+        return realFontDescriptor.getDescender();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getAscender()
+     */
+    public int getAscender() {
+        load();
+        return realFontDescriptor.getAscender();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFlags()
+     */
+    public int getFlags() {
+        load();
+        return realFontDescriptor.getFlags();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFontBBox()
+     */
+    public int[] getFontBBox() {
+        load();
+        return realFontDescriptor.getFontBBox();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getItalicAngle()
+     */
+    public int getItalicAngle() {
+        load();
+        return realFontDescriptor.getItalicAngle();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getStemV()
+     */
+    public int getStemV() {
+        load();
+        return realFontDescriptor.getStemV();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#getFontType()
+     */
+    public FontType getFontType() {
+        load();
+        return realFontDescriptor.getFontType();
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+     */
+    public boolean isEmbeddable() {
+        load();
+        return realFontDescriptor.isEmbeddable();
+    }
+
+}
+
diff --git a/src/org/apache/fop/fonts/MultiByteFont.java b/src/org/apache/fop/fonts/MultiByteFont.java
new file mode 100644 (file)
index 0000000..1384d34
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+//Java
+import java.util.Map;
+
+//FOP
+import org.apache.fop.pdf.PDFWArray;
+
+/**
+ * Generic MultiByte (CID) font
+ */
+public class MultiByteFont extends CIDFont {
+
+    private static int uniqueCounter = 1;
+
+
+    private String ttcName = null;
+    private String encoding = "Identity-H";
+
+    private String embedResourceName = null;
+
+    private int defaultWidth = 0;
+    private CIDFontType cidType = CIDFontType.CIDTYPE2;
+
+    private String namePrefix = null;    // Quasi unique prefix
+    private PDFWArray warray = new PDFWArray();
+    private int width[] = null;
+
+    private BFEntry[] bfentries = null;
+
+    /**
+     * usedGlyphs contains orginal, new glyph index
+     */
+    private Map usedGlyphs = new java.util.HashMap();
+
+    /**
+     * usedGlyphsIndex contains new glyph, original index
+     */
+    private Map usedGlyphsIndex = new java.util.HashMap();
+    private int usedGlyphsCount = 0;
+
+
+    /**
+     * Default constructor
+     */
+    public MultiByteFont() {
+        // Make sure that the 3 first glyphs are included
+        usedGlyphs.put(new Integer(0), new Integer(0));
+        usedGlyphsIndex.put(new Integer(0), new Integer(0));
+        usedGlyphsCount++;
+        usedGlyphs.put(new Integer(1), new Integer(1));
+        usedGlyphsIndex.put(new Integer(1), new Integer(1));
+        usedGlyphsCount++;
+        usedGlyphs.put(new Integer(2), new Integer(2));
+        usedGlyphsIndex.put(new Integer(2), new Integer(2));
+        usedGlyphsCount++;
+
+        // Create a quasiunique prefix for fontname
+        int cnt = 0;
+        synchronized (this.getClass()) {
+            cnt = uniqueCounter++;
+        }
+        int ctm = (int)(System.currentTimeMillis() & 0xffff);
+        namePrefix = new String(cnt + "E" + Integer.toHexString(ctm));
+        
+        setFontType(FontType.TYPE0);
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getDefaultWidth()
+     */
+    public int getDefaultWidth() {
+        return defaultWidth;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getRegistry()
+     */
+    public String getRegistry() {
+        return "Adobe";
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getOrdering()
+     */
+    public String getOrdering() {
+        return "UCS";
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getSupplement()
+     */
+    public int getSupplement() {
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getCIDType()
+     */
+    public CIDFontType getCIDType() {
+        return cidType;
+    }
+    
+    /**
+     * Sets the CIDType.
+     * @param cidType The cidType to set
+     */
+    public void setCIDType(CIDFontType cidType) {
+        this.cidType = cidType;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.CIDFont#getCidBaseFont()
+     */
+    public String getCidBaseFont() {
+        if (isEmbeddable()) {
+            return namePrefix + super.getFontName();
+        } else {
+            return super.getFontName();
+        }
+    }
+
+/* unused
+    public PDFWArray getWidthsAsPDFWArray() {
+        if (isEmbeddable()) {
+            // Create widths for reencoded chars
+            warray = new PDFWArray();
+            int[] tmpWidth = new int[usedGlyphsCount];
+
+            for (int i = 0; i < usedGlyphsCount; i++) {
+                Integer nw = (Integer)usedGlyphsIndex.get(new Integer(i));
+                int nwx = (nw == null) ? 0 : nw.intValue();
+                tmpWidth[i] = width[nwx];
+            }
+            warray.addEntry(0, tmpWidth);
+        }
+        return warray;
+    }*/
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+     */
+    public boolean isEmbeddable() {
+        if (getEmbedFileName() == null 
+            && embedResourceName == null) {
+            return false;
+        } else {
+            return true;
+            }
+    }
+
+    /**
+     * @see org.apache.fop.fonts.Font#getEncoding()
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getFontName()
+     */
+    public String getFontName() {
+        if (isEmbeddable()) {
+            return namePrefix + super.getFontName();
+        } else {
+            return super.getFontName();
+        }
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+     */
+    public int getWidth(int i, int size) {
+        if (isEmbeddable()) {
+            Integer idx = (Integer)usedGlyphsIndex.get(new Integer(i));
+            return size * width[idx.intValue()];
+        } else {
+            return size * width[i];
+        }
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidths()
+     */
+    public int[] getWidths() {
+        int[] arr = new int[width.length];
+        System.arraycopy(width, 0, arr, 0, width.length - 1);
+        /*
+        for (int i = 0; i < arr.length; i++)
+            arr[i] *= size;
+        */
+        return arr;
+    }
+
+    /**
+     * Remaps a codepoint based.
+     * @param i codepoint to remap
+     * @return new codepoint
+     */
+/* unused
+    public Integer reMap(Integer i) {
+        if (isEmbeddable()) {
+            Integer ret = (Integer)usedGlyphsIndex.get(i);
+            if (ret == null) {
+                ret = i;
+            }
+            return ret;
+        } else {
+            return i;
+        }
+
+    }
+*/
+
+    /**
+     * @see org.apache.fop.fonts.Font#mapChar(char)
+     */
+    public char mapChar(char c) {
+        int idx = (int)c;
+        int retIdx = 0;
+
+        for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
+            if (bfentries[i].getUnicodeStart() <= idx
+                    && bfentries[i].getUnicodeEnd() >= idx) {
+                retIdx = bfentries[i].getGlyphStartIndex() + idx
+                         - bfentries[i].getUnicodeStart();
+            }
+        }
+
+        if (isEmbeddable()) {
+            // Reencode to a new subset font or get
+            // the reencoded value
+            Integer newIdx = (Integer)usedGlyphs.get(new Integer(retIdx));
+            if (newIdx == null) {
+                usedGlyphs.put(new Integer(retIdx),
+                               new Integer(usedGlyphsCount));
+                usedGlyphsIndex.put(new Integer(usedGlyphsCount),
+                                    new Integer(retIdx));
+                retIdx = usedGlyphsCount;
+                // System.out.println(c+"("+(int)c+") = "+retIdx);
+                usedGlyphsCount++;
+            } else {
+                retIdx = newIdx.intValue();
+            }
+        }
+
+        return (char)retIdx;
+    }
+
+    /**
+     * Sets the bfentries.
+     * @param bfentries The bfentries to set
+     */
+    public void setBFEntries(BFEntry[] bfentries) {
+        this.bfentries = bfentries;
+    }
+
+    /**
+     * Sets the defaultWidth.
+     * @param defaultWidth The defaultWidth to set
+     */
+    public void setDefaultWidth(int defaultWidth) {
+        this.defaultWidth = defaultWidth;
+    }
+
+    /**
+     * Returns the TrueType Collection Name.
+     * @return the TrueType Collection Name
+     */
+    public String getTTCName() {
+        return ttcName;
+    }
+
+    /**
+     * Sets the the TrueType Collection Name.
+     * @param ttcName the TrueType Collection Name
+     */
+    public void setTTCName(String ttcName) {
+        this.ttcName = ttcName;
+    }
+
+    /**
+     * Adds a new CID width entry to the font.
+     * @param cidWidthIndex index
+     * @param wds array of widths
+     */
+    public void addCIDWidthEntry(int cidWidthIndex, int[] wds) {
+        this.warray.addEntry(cidWidthIndex, wds);
+    }
+
+
+    /**
+     * Sets the width array.
+     * @param wds array of widths.
+     */
+    public void setWidthArray(int[] wds) {
+        this.width = wds;
+    }
+
+    /**
+     * Returns a Map of used Glyphs.
+     * @return Map Map of used Glyphs
+     */
+    public Map getUsedGlyphs() {
+        return usedGlyphs;
+    }
+
+}
+
diff --git a/src/org/apache/fop/fonts/MutableFont.java b/src/org/apache/fop/fonts/MutableFont.java
new file mode 100644 (file)
index 0000000..f13c568
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * This interface is used to set the values of a font during configuration time.
+ */
+public interface MutableFont {
+
+    /**
+     * Sets the font name.
+     * @param name font name
+     */
+    void setFontName(String name);
+    
+    /**
+     * Sets the path to the embeddable font file.
+     * @param path URI to the file
+     */
+    void setEmbedFileName(String path);
+
+    /**
+     * Sets the resource name of the embeddable font file.
+     * @param name resource name
+     */
+    void setEmbedResourceName(String name);
+
+    /**
+     * Sets the capital height value.
+     * @param capHeight capital height
+     */
+    void setCapHeight(int capHeight);
+    
+    /**
+     * Sets the ascent value.
+     * @param ascender ascent height
+     */
+    void setAscender(int ascender);
+    
+    /**
+     * Sets the descent value.
+     * @param descender descent value
+     */
+    void setDescender(int descender);
+
+    /**
+     * Sets the font's bounding box
+     * @param bbox bounding box
+     */
+    void setFontBBox(int[] bbox);
+
+    /**
+     * Sets the font's flags
+     * @param flags flags
+     */
+    void setFlags(int flags);
+    
+    /**
+     * Sets the font's StemV value.
+     * @param stemV StemV
+     */
+    void setStemV(int stemV);
+    
+    /**
+     * Sets the font's italic angle.
+     * @param italicAngle italic angle
+     */
+    void setItalicAngle(int italicAngle);
+    
+    /**
+     * Sets the font's default width
+     * @param width default width
+     */
+    void setMissingWidth(int width);
+    /**
+     * Sets the font type.
+     * @param fontType font type
+     */
+    void setFontType(FontType fontType);
+    /**
+     * Sets the index of the first character in the character table.
+     * @param index index of first character
+     */
+    void setFirstChar(int index);
+    
+    /**
+     * Sets the index of the last character in the character table.
+     * @param index index of the last character
+     */
+    void setLastChar(int index);
+    
+    /**
+     * Enables/disabled kerning.
+     * @param enabled True if kerning should be enabled if available
+     */
+    void setKerningEnabled(boolean enabled);
+    
+    /**
+     * Adds an entry to the kerning table.
+     * @param key Kerning key
+     * @param value Kerning value
+     */
+    void putKerningEntry(Integer key, Map value);
+       
+}
diff --git a/src/org/apache/fop/fonts/SingleByteFont.java b/src/org/apache/fop/fonts/SingleByteFont.java
new file mode 100644 (file)
index 0000000..9ad776f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * Generic SingleByte font
+ */
+public class SingleByteFont extends CustomFont {
+    
+    private final CodePointMapping mapping
+        = CodePointMapping.getMapping("WinAnsiEncoding");
+
+    private String encoding = "WinAnsiEncoding";
+
+    private int width[] = null;
+
+
+    /**
+     * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+     */
+    public boolean isEmbeddable() {
+        return (getEmbedFileName() == null && getEmbedResourceName() == null) ? false
+               : true;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.Font#getEncoding()
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+     */
+    public int getWidth(int i, int size) {
+        return size * width[i];
+    }
+
+    /**
+     * @see org.apache.fop.fonts.FontMetrics#getWidths()
+     */
+    public int[] getWidths() {
+        int[] arr = new int[width.length];
+        System.arraycopy(width, 0, arr, 0, width.length - 1);
+        /*
+        for (int i = 0; i < arr.length; i++)
+            arr[i] *= size;
+        */
+        return arr;
+    }
+
+    /**
+     * @see org.apache.fop.fonts.Font#mapChar(char)
+     */
+    public char mapChar(char c) {
+        char d = mapping.mapChar(c);
+        if (d != 0) {
+            return d;
+        } else {
+            return '#';
+        }
+    }
+    
+    /* ---- single byte font specific setters --- */
+
+    /**
+     * Sets a width for a character.
+     * @param index index of the character
+     * @param width the width of the character
+     */
+    public void setWidth(int index, int width) {
+        if (this.width == null) {
+            this.width = new int[256];
+        }
+        this.width[index] = width;
+    }
+    
+}
+
diff --git a/src/org/apache/fop/fonts/TTFCmapEntry.java b/src/org/apache/fop/fonts/TTFCmapEntry.java
deleted file mode 100644 (file)
index d3e900d..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-/**
- * The CMap entry contains information of a Unicode range and the
- * the glyph indexes related to the range
- */
-public class TTFCmapEntry {
-
-    private int unicodeStart;
-    private int unicodeEnd;
-    private int glyphStartIndex;
-
-    TTFCmapEntry() {
-        unicodeStart = 0;
-        unicodeEnd = 0;
-        glyphStartIndex = 0;
-    }
-
-    TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
-        this.unicodeStart = unicodeStart;
-        this.unicodeEnd = unicodeEnd;
-        this.glyphStartIndex = glyphStartIndex;
-    }
-
-    /**
-     * @see java.lang.Object#equals(Object)
-     */
-    public boolean equals(Object o) {
-        if (o instanceof TTFCmapEntry) {
-            TTFCmapEntry ce = (TTFCmapEntry)o;
-            if (ce.unicodeStart == this.unicodeStart
-                    && ce.unicodeEnd == this.unicodeEnd
-                    && ce.glyphStartIndex == this.glyphStartIndex) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns the glyphStartIndex.
-     * @return int
-     */
-    public int getGlyphStartIndex() {
-        return glyphStartIndex;
-    }
-
-    /**
-     * Returns the unicodeEnd.
-     * @return int
-     */
-    public int getUnicodeEnd() {
-        return unicodeEnd;
-    }
-
-    /**
-     * Returns the unicodeStart.
-     * @return int
-     */
-    public int getUnicodeStart() {
-        return unicodeStart;
-    }
-
-    /**
-     * Sets the glyphStartIndex.
-     * @param glyphStartIndex The glyphStartIndex to set
-     */
-    public void setGlyphStartIndex(int glyphStartIndex) {
-        this.glyphStartIndex = glyphStartIndex;
-    }
-
-    /**
-     * Sets the unicodeEnd.
-     * @param unicodeEnd The unicodeEnd to set
-     */
-    public void setUnicodeEnd(int unicodeEnd) {
-        this.unicodeEnd = unicodeEnd;
-    }
-
-    /**
-     * Sets the unicodeStart.
-     * @param unicodeStart The unicodeStart to set
-     */
-    public void setUnicodeStart(int unicodeStart) {
-        this.unicodeStart = unicodeStart;
-    }
-
-}
diff --git a/src/org/apache/fop/fonts/TTFDirTabEntry.java b/src/org/apache/fop/fonts/TTFDirTabEntry.java
deleted file mode 100644 (file)
index 2c62133..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-
-class TTFDirTabEntry {
-
-    private byte[] tag = new byte[4];
-    private int checksum;
-    private long offset;
-    private long length;
-
-    /**
-     * Read Dir Tab, return tag name
-     */
-    public String read(FontFileReader in) throws IOException {
-        tag[0] = in.readTTFByte();
-        tag[1] = in.readTTFByte();
-        tag[2] = in.readTTFByte();
-        tag[3] = in.readTTFByte();
-
-        in.skip(4);    // Skip checksum
-
-        offset = in.readTTFULong();
-        length = in.readTTFULong();
-
-        //System.out.println(this.toString());
-        return new String(tag, "ISO-8859-1");
-    }
-
-
-    public String toString() {
-        return "Read dir tab ["
-            + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]"
-            + " offset: " + offset
-            + " length: " + length
-            + " name: " + tag;
-    }
-
-    /**
-     * Returns the checksum.
-     * @return int
-     */
-    public int getChecksum() {
-        return checksum;
-    }
-
-    /**
-     * Returns the length.
-     * @return long
-     */
-    public long getLength() {
-        return length;
-    }
-
-    /**
-     * Returns the offset.
-     * @return long
-     */
-    public long getOffset() {
-        return offset;
-    }
-
-    /**
-     * Returns the tag.
-     * @return byte[]
-     */
-    public byte[] getTag() {
-        return tag;
-    }
-
-}
diff --git a/src/org/apache/fop/fonts/TTFFile.java b/src/org/apache/fop/fonts/TTFFile.java
deleted file mode 100644 (file)
index cef0b41..0000000
+++ /dev/null
@@ -1,1248 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-
-import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.framework.logger.ConsoleLogger;
-import org.apache.avalon.framework.logger.Logger;
-
-/**
- * Reads a TrueType file or a TrueType Collection.
- * The TrueType spec can be found at the Microsoft.
- * Typography site: http://www.microsoft.com/truetype/
- */
-public class TTFFile extends AbstractLogEnabled {
-
-    static final byte NTABS = 24;
-    static final int NMACGLYPHS = 258;
-    static final int MAX_CHAR_CODE = 255;
-    static final int ENC_BUF_SIZE = 1024;
-
-    private String encoding = "WinAnsiEncoding";    // Default encoding
-
-    private short firstChar = 0;
-    private boolean isEmbeddable = true;
-    private boolean hasSerifs = true;
-    /**
-     * Table directory
-     */
-    protected Map dirTabs;
-    private Map kerningTab;                          // for CIDs
-    private Map ansiKerningTab;                      // For winAnsiEncoding
-    private List cmaps;
-    private List unicodeMapping;
-
-    private int upem;                                // unitsPerEm from "head" table
-    private int nhmtx;                               // Number of horizontal metrics
-    private int postFormat;
-    private int locaFormat;
-    /**
-     * Offset to last loca
-     */
-    protected long lastLoca = 0;
-    private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
-    private int nmGlyphs;                            // Used in fixWidths - remove?
-
-    /**
-     * Contains glyph data
-     */
-    protected TTFMtxEntry mtxTab[];                  // Contains glyph data
-    private int[] mtxEncoded = null;
-
-    private String fontName = "";
-    private String fullName = "";
-    private String notice = "";
-    private String familyName = "";
-    private String subFamilyName = "";
-
-    private long italicAngle = 0;
-    private long isFixedPitch = 0;
-    private int fontBBox1 = 0;
-    private int fontBBox2 = 0;
-    private int fontBBox3 = 0;
-    private int fontBBox4 = 0;
-    private int capHeight = 0;
-    private int underlinePosition = 0;
-    private int underlineThickness = 0;
-    private int xHeight = 0;
-    private int ascender = 0;
-    private int descender = 0;
-
-    private short lastChar = 0;
-
-    private int ansiWidth[];
-    private Map ansiIndex;
-
-    /**
-     * Position inputstream to position indicated
-     * in the dirtab offset + offset
-     */
-    void seekTab(FontFileReader in, String name,
-                  long offset) throws IOException {
-        TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
-        if (dt == null) {
-            getLogger().error("Dirtab " + name + " not found.");
-            return;
-        }
-
-        in.seekSet(dt.getOffset() + offset);
-    }
-
-    /**
-     * Convert from truetype unit to pdf unit based on the
-     * unitsPerEm field in the "head" table
-     * @param n truetype unit
-     * @return pdf unit
-     */
-    public int convertTTFUnit2PDFUnit(int n) {
-        int ret;
-        if (n < 0) {
-            long rest1 = n % upem;
-            long storrest = 1000 * rest1;
-            long ledd2 = rest1 / storrest;
-            ret = -((-1000 * n) / upem - (int)ledd2);
-        } else {
-            ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
-        }
-
-        return ret;
-    }
-
-    /**
-     * Read the cmap table,
-     * return false if the table is not present or only unsupported
-     * tables are present. Currently only unicode cmaps are supported.
-     * Set the unicodeIndex in the TTFMtxEntries and fills in the
-     * cmaps vector.
-     */
-    private boolean readCMAP(FontFileReader in) throws IOException {
-
-        unicodeMapping = new java.util.ArrayList();
-
-        //Read CMAP table and correct mtxTab.index
-        int mtxPtr = 0;
-
-        seekTab(in, "cmap", 2);
-        int numCMap = in.readTTFUShort();    // Number of cmap subtables
-        long cmapUniOffset = 0;
-
-        getLogger().info(numCMap + " cmap tables");
-
-        //Read offset for all tables. We are only interested in the unicode table
-        for (int i = 0; i < numCMap; i++) {
-            int cmapPID = in.readTTFUShort();
-            int cmapEID = in.readTTFUShort();
-            long cmapOffset = in.readTTFULong();
-
-            getLogger().debug("Platform ID: " + cmapPID
-                + " Encoding: " + cmapEID);
-
-            if (cmapPID == 3 && cmapEID == 1) {
-                cmapUniOffset = cmapOffset;
-            }
-        }
-
-        if (cmapUniOffset <= 0) {
-            getLogger().fatalError("Unicode cmap table not present");
-            getLogger().fatalError("Unsupported format: Aborting");
-            return false;
-        }
-
-        // Read unicode cmap
-        seekTab(in, "cmap", cmapUniOffset);
-        int cmapFormat = in.readTTFUShort();
-        /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
-
-        getLogger().info("CMAP format: " + cmapFormat);
-        if (cmapFormat == 4) {
-            in.skip(2);    // Skip version number
-            int cmapSegCountX2 = in.readTTFUShort();
-            int cmapSearchRange = in.readTTFUShort();
-            int cmapEntrySelector = in.readTTFUShort();
-            int cmapRangeShift = in.readTTFUShort();
-
-            getLogger().debug("segCountX2   : " + cmapSegCountX2);
-            getLogger().debug("searchRange  : " + cmapSearchRange);
-            getLogger().debug("entrySelector: " + cmapEntrySelector);
-            getLogger().debug("rangeShift   : " + cmapRangeShift);
-
-
-            int cmapEndCounts[] = new int[cmapSegCountX2 / 2];
-            int cmapStartCounts[] = new int[cmapSegCountX2 / 2];
-            int cmapDeltas[] = new int[cmapSegCountX2 / 2];
-            int cmapRangeOffsets[] = new int[cmapSegCountX2 / 2];
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapEndCounts[i] = in.readTTFUShort();
-            }
-
-            in.skip(2);    // Skip reservedPad
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapStartCounts[i] = in.readTTFUShort();
-            }
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapDeltas[i] = in.readTTFShort();
-            }
-
-            //int startRangeOffset = in.getCurrentPos();
-
-            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
-                cmapRangeOffsets[i] = in.readTTFUShort();
-            }
-
-            int glyphIdArrayOffset = in.getCurrentPos();
-
-            // Insert the unicode id for the glyphs in mtxTab
-            // and fill in the cmaps ArrayList
-
-            for (int i = 0; i < cmapStartCounts.length; i++) {
-
-                getLogger().debug(i + ": " + cmapStartCounts[i]
-                    + " - " + cmapEndCounts[i]);
-
-                for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
-
-                    // Update lastChar
-                    if (j < 256 && j > lastChar) {
-                        lastChar = (short)j;
-                    }
-
-                    if (mtxPtr < mtxTab.length) {
-                        int glyphIdx;
-                        // the last character 65535 = .notdef
-                        // may have a range offset
-                        if (cmapRangeOffsets[i] != 0 && j != 65535) {
-                            int glyphOffset = glyphIdArrayOffset
-                                + ((cmapRangeOffsets[i] / 2)
-                                    + (j - cmapStartCounts[i])
-                                    + (i)
-                                    - cmapSegCountX2 / 2) * 2;
-                            in.seekSet(glyphOffset);
-                            glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
-                                       & 0xffff;
-
-                            unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
-                            mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-
-
-                            // Also add winAnsiWidth
-                            List v = (List)ansiIndex.get(new Integer(j));
-                            if (v != null) {
-                                Iterator e = v.listIterator();
-                                while (e.hasNext()) {
-                                    Integer aIdx = (Integer)e.next();
-                                    ansiWidth[aIdx.intValue()] =
-                                        mtxTab[glyphIdx].getWx();
-
-                                    getLogger().debug("Added width "
-                                        + mtxTab[glyphIdx].getWx()
-                                        + " uni: " + j
-                                        + " ansi: " + aIdx.intValue());
-                                }
-                            }
-
-                            getLogger().debug("Idx: "
-                                + glyphIdx
-                                + " Delta: " + cmapDeltas[i]
-                                + " Unicode: " + j
-                                + " name: " + mtxTab[glyphIdx].getName());
-                        } else {
-                            glyphIdx = (j + cmapDeltas[i]) & 0xffff;
-
-                            if (glyphIdx < mtxTab.length) {
-                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-                            } else {
-                                getLogger().debug("Glyph " + glyphIdx
-                                                   + " out of range: "
-                                                   + mtxTab.length);
-                            }
-
-                            unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
-                            if (glyphIdx < mtxTab.length) {
-                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-                            } else {
-                                getLogger().debug("Glyph " + glyphIdx
-                                                   + " out of range: "
-                                                   + mtxTab.length);
-                            }
-
-                            // Also add winAnsiWidth
-                            List v = (List)ansiIndex.get(new Integer(j));
-                            if (v != null) {
-                                Iterator e = v.listIterator();
-                                while (e.hasNext()) {
-                                    Integer aIdx = (Integer)e.next();
-                                    ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
-                                }
-                            }
-
-                            //getLogger().debug("IIdx: " +
-                            //    mtxPtr +
-                            //    " Delta: " + cmap_deltas[i] +
-                            //    " Unicode: " + j +
-                            //    " name: " +
-                            //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
-
-                        }
-                        if (glyphIdx < mtxTab.length) {
-                            if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
-                                mtxPtr++;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Print first char/last char
-     */
-    private void printMaxMin() {
-        int min = 255;
-        int max = 0;
-        for (int i = 0; i < mtxTab.length; i++) {
-            if (mtxTab[i].getIndex() < min) {
-                min = mtxTab[i].getIndex();
-            }
-            if (mtxTab[i].getIndex() > max) {
-                max = mtxTab[i].getIndex();
-            }
-        }
-        getLogger().info("Min: " + min);
-        getLogger().info("Max: " + max);
-    }
-
-
-    /**
-     * Reads the font using a FontFileReader.
-     *
-     * @param in The FontFileReader to use
-     * @throws IOException In case of an I/O problem
-     */
-    public void readFont(FontFileReader in) throws IOException {
-        readFont(in, (String)null);
-    }
-
-    /**
-     * initialize the ansiWidths array (for winAnsiEncoding)
-     * and fill with the missingwidth
-     */
-    private void initAnsiWidths() {
-        ansiWidth = new int[256];
-        for (int i = 0; i < 256; i++) {
-            ansiWidth[i] = mtxTab[0].getWx();
-        }
-
-        // Create an index hash to the ansiWidth
-        // Can't just index the winAnsiEncoding when inserting widths
-        // same char (eg bullet) is repeated more than one place
-        ansiIndex = new java.util.HashMap();
-        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
-            Integer ansi = new Integer(i);
-            Integer uni = new Integer((int)Glyphs.WINANSI_ENCODING[i]);
-
-            List v = (List)ansiIndex.get(uni);
-            if (v == null) {
-                v = new java.util.ArrayList();
-                ansiIndex.put(uni, v);
-            }
-            v.add(ansi);
-        }
-    }
-
-    /**
-     * Read the font data.
-     * If the fontfile is a TrueType Collection (.ttc file)
-     * the name of the font to read data for must be supplied,
-     * else the name is ignored.
-     *
-     * @param in The FontFileReader to use
-     * @param name The name of the font
-     * @return boolean Returns true if the font is valid
-     * @throws IOException In case of an I/O problem
-     */
-    public boolean readFont(FontFileReader in, String name) throws IOException {
-
-        /*
-         * Check if TrueType collection, and that the name
-         * exists in the collection
-         */
-        if (!checkTTC(in, name)) {
-            throw new IOException("Failed to read font");
-        }
-
-        readDirTabs(in);
-        readFontHeader(in);
-        getNumGlyphs(in);
-        getLogger().info("Number of glyphs in font: " + numberOfGlyphs);
-        readHorizontalHeader(in);
-        readHorizontalMetrics(in);
-        initAnsiWidths();
-        readPostscript(in);
-        readOS2(in);
-        readIndexToLocation(in);
-        readGlyf(in);
-        readName(in);
-        readPCLT(in);
-        // Read cmap table and fill in ansiwidths
-        boolean valid = readCMAP(in);
-        if (!valid) {
-            return false;
-        }
-        // Create cmaps for bfentries
-        createCMaps();
-        // print_max_min();
-
-        readKerning(in);
-        return true;
-    }
-
-    private void createCMaps() {
-        cmaps = new java.util.ArrayList();
-        TTFCmapEntry tce = new TTFCmapEntry();
-
-        Iterator e = unicodeMapping.listIterator();
-        UnicodeMapping um = (UnicodeMapping)e.next();
-        UnicodeMapping lastMapping = um;
-
-        tce.setUnicodeStart(um.getUIdx());
-        tce.setGlyphStartIndex(um.getGIdx());
-
-        while (e.hasNext()) {
-            um = (UnicodeMapping)e.next();
-            if (((lastMapping.getUIdx() + 1) != um.getUIdx())
-                    || ((lastMapping.getGIdx() + 1) != um.getGIdx())) {
-                tce.setUnicodeEnd(lastMapping.getUIdx());
-                cmaps.add(tce);
-
-                tce = new TTFCmapEntry();
-                tce.setUnicodeStart(um.getUIdx());
-                tce.setGlyphStartIndex(um.getGIdx());
-            }
-            lastMapping = um;
-        }
-
-        tce.setUnicodeEnd(um.getUIdx());
-        cmaps.add(tce);
-    }
-
-    /**
-     * Returns the Windows name of the font.
-     * @return String The Windows name
-     */
-    public String getWindowsName() {
-        return familyName + "," + subFamilyName;
-    }
-
-    /**
-     * Returns the PostScript name of the font.
-     * @return String The PostScript name
-     */
-    public String getPostscriptName() {
-        if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
-            return familyName;
-        } else {
-            return familyName + "," + subFamilyName;
-        }
-    }
-
-    /**
-     * Returns the font family name of the font.
-     * @return String The family name
-     */
-    public String getFamilyName() {
-        return familyName;
-    }
-
-    /**
-     * Returns the name of the character set used.
-     * @return String The caracter set
-     */
-    public String getCharSetName() {
-        return encoding;
-    }
-
-    /**
-     * Returns the CapHeight attribute of the font.
-     * @return int The CapHeight
-     */
-    public int getCapHeight() {
-        return (int)convertTTFUnit2PDFUnit(capHeight);
-    }
-
-    /**
-     * Returns the XHeight attribute of the font.
-     * @return int The XHeight
-     */
-    public int getXHeight() {
-        return (int)convertTTFUnit2PDFUnit(xHeight);
-    }
-
-    /**
-     * Returns the Flags attribute of the font.
-     * @return int The Flags
-     */
-    public int getFlags() {
-        int flags = 32;    // Use Adobe Standard charset
-        if (italicAngle != 0) {
-            flags = flags | 64;
-        }
-        if (isFixedPitch != 0) {
-            flags = flags | 2;
-        }
-        if (hasSerifs) {
-            flags = flags | 1;
-        }
-        return flags;
-    }
-
-
-    /**
-     * Returns the StemV attribute of the font.
-     * @return String The StemV
-     */
-    public String getStemV() {
-        return "0";
-    }
-
-    /**
-     * Returns the ItalicAngle attribute of the font.
-     * @return String The ItalicAngle
-     */
-    public String getItalicAngle() {
-        String ia = Short.toString((short)(italicAngle / 0x10000));
-
-        // This is the correct italic angle, however only int italic
-        // angles are supported at the moment so this is commented out.
-        /*
-         * if ((italicAngle % 0x10000) > 0 )
-         * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
-         */
-        return ia;
-    }
-
-    /**
-     * Returns the font bounding box.
-     * @return int[] The font bbox
-     */
-    public int[] getFontBBox() {
-        final int[] fbb = new int[4];
-        fbb[0] = (int)convertTTFUnit2PDFUnit(fontBBox1);
-        fbb[1] = (int)convertTTFUnit2PDFUnit(fontBBox2);
-        fbb[2] = (int)convertTTFUnit2PDFUnit(fontBBox3);
-        fbb[3] = (int)convertTTFUnit2PDFUnit(fontBBox4);
-
-        return fbb;
-    }
-
-    /**
-     * Returns the LowerCaseAscent attribute of the font.
-     * @return int The LowerCaseAscent
-     */
-    public int getLowerCaseAscent() {
-        return (int)convertTTFUnit2PDFUnit(ascender);
-    }
-
-    /**
-     * Returns the LowerCaseDescent attribute of the font.
-     * @return int The LowerCaseDescent
-     */
-    public int getLowerCaseDescent() {
-        return (int)convertTTFUnit2PDFUnit(descender);
-    }
-
-    /**
-     * Returns the index of the last character, but this is for WinAnsiEncoding
-     * only, so the last char is < 256.
-     * @return short Index of the last character (<256)
-     */
-    public short getLastChar() {
-        return lastChar;
-    }
-
-    /**
-     * Returns the index of the first character.
-     * @return short Index of the first character
-     */
-    public short getFirstChar() {
-        return firstChar;
-    }
-
-    /**
-     * Returns an array of character widths.
-     * @return int[] The character widths
-     */
-    public int[] getWidths() {
-        int[] wx = new int[mtxTab.length];
-        for (int i = 0; i < wx.length; i++) {
-            wx[i] = (int)convertTTFUnit2PDFUnit(mtxTab[i].getWx());
-        }
-
-        return wx;
-    }
-
-    /**
-     * Returns the width of a given character.
-     * @param idx Index of the character
-     * @return int Standard width
-     */
-    public int getCharWidth(int idx) {
-        return (int)convertTTFUnit2PDFUnit(ansiWidth[idx]);
-    }
-
-    /**
-     * Returns the kerning table.
-     * @return Map The kerning table
-     */
-    public Map getKerning() {
-        return kerningTab;
-    }
-
-    /**
-     * Returns the ANSI kerning table.
-     * @return Map The ANSI kerning table
-     */
-    public Map getAnsiKerning() {
-        return ansiKerningTab;
-    }
-
-    /**
-     * Indicates if the font may be embedded.
-     * @return boolean True if it may be embedded
-     */
-    public boolean isEmbeddable() {
-        return isEmbeddable;
-    }
-
-
-    /**
-     * Read Table Directory from the current position in the
-     * FontFileReader and fill the global HashMap dirTabs
-     * with the table name (String) as key and a TTFDirTabEntry
-     * as value.
-     * @param in FontFileReader to read the table directory from
-     * @throws IOException in case of an I/O problem
-     */
-    protected void readDirTabs(FontFileReader in) throws IOException {
-        in.skip(4);    // TTF_FIXED_SIZE
-        int ntabs = in.readTTFUShort();
-        in.skip(6);    // 3xTTF_USHORT_SIZE
-
-        dirTabs = new java.util.HashMap();
-        TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
-        getLogger().debug("Reading " + ntabs + " dir tables");
-        for (int i = 0; i < ntabs; i++) {
-            pd[i] = new TTFDirTabEntry();
-            dirTabs.put(pd[i].read(in), pd[i]);
-        }
-    }
-
-    /**
-     * Read the "head" table, this reads the bounding box and
-     * sets the upem (unitsPerEM) variable
-     * @param in FontFileReader to read the header from
-     * @throws IOException in case of an I/O problem
-     */
-    protected void readFontHeader(FontFileReader in) throws IOException {
-        seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
-        upem = in.readTTFUShort();
-
-        in.skip(16);
-
-        fontBBox1 = in.readTTFShort();
-        fontBBox2 = in.readTTFShort();
-        fontBBox3 = in.readTTFShort();
-        fontBBox4 = in.readTTFShort();
-
-        in.skip(2 + 2 + 2);
-
-        locaFormat = in.readTTFShort();
-    }
-
-    /**
-     * Read the number of glyphs from the "maxp" table
-     * @param in FontFileReader to read the number of glyphs from
-     * @throws IOException in case of an I/O problem
-     */
-    protected void getNumGlyphs(FontFileReader in) throws IOException {
-        seekTab(in, "maxp", 4);
-        numberOfGlyphs = in.readTTFUShort();
-    }
-
-
-    /**
-     * Read the "hhea" table to find the ascender and descender and
-     * size of "hmtx" table, i.e. a fixed size font might have only
-     * one width
-     * @param in FontFileReader to read the hhea table from
-     * @throws IOException in case of an I/O problem
-     */
-    protected void readHorizontalHeader(FontFileReader in)
-            throws IOException {
-        seekTab(in, "hhea", 4);
-        ascender = in.readTTFShort();    // Use sTypoAscender in "OS/2" table?
-        descender = in.readTTFShort();    // Use sTypoDescender in "OS/2" table?
-
-        in.skip(2 + 2 + 3 * 2 + 8 * 2);
-        nhmtx = in.readTTFUShort();
-        getLogger().debug("Number of horizontal metrics: " + nhmtx);
-    }
-
-    /**
-     * Read "hmtx" table and put the horizontal metrics
-     * in the mtxTab array. If the number of metrics is less
-     * than the number of glyphs (eg fixed size fonts), extend
-     * the mtxTab array and fill in the missing widths
-     * @param in FontFileReader to read the hmtx table from
-     * @throws IOException in case of an I/O problem
-     */
-    protected void readHorizontalMetrics(FontFileReader in)
-            throws IOException {
-        seekTab(in, "hmtx", 0);
-
-        int mtxSize = (numberOfGlyphs > nhmtx) ? numberOfGlyphs : nhmtx;
-        mtxTab = new TTFMtxEntry[mtxSize];
-
-        getLogger().debug("*** Widths array: \n");
-        for (int i = 0; i < mtxSize; i++) {
-            mtxTab[i] = new TTFMtxEntry();
-        }
-        for (int i = 0; i < nhmtx; i++) {
-            mtxTab[i].setWx(in.readTTFUShort());
-            mtxTab[i].setLsb(in.readTTFUShort());
-
-            getLogger().debug("   width[" + i + "] = "
-                + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
-        }
-
-        if (nhmtx < mtxSize) {
-            // Fill in the missing widths
-            int lastWidth = mtxTab[nhmtx - 1].getWx();
-            for (int i = nhmtx; i < mtxSize; i++) {
-                mtxTab[i].setWx(lastWidth);
-                mtxTab[i].setLsb(in.readTTFUShort());
-            }
-        }
-    }
-
-
-    /**
-     * Read the "post" table
-     * containing the postscript names of the glyphs.
-     */
-    private final void readPostscript(FontFileReader in) throws IOException {
-        String[] psGlyphsBuffer;
-        int i, k, l;
-
-        seekTab(in, "post", 0);
-        postFormat = in.readTTFLong();
-        italicAngle = in.readTTFULong();
-        underlinePosition = in.readTTFShort();
-        underlineThickness = in.readTTFShort();
-        isFixedPitch = in.readTTFULong();
-
-        in.skip(4 * 4);
-
-        getLogger().debug("Post format: " + postFormat);
-        switch (postFormat) {
-        case 0x00010000:
-            getLogger().debug("Postscript format 1");
-            for (i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
-                mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
-            }
-            break;
-        case 0x00020000:
-            getLogger().debug("Postscript format 2");
-            int numGlyphStrings = 0;
-            l = in.readTTFUShort();      // Num Glyphs
-            // short minIndex=256;
-            for (i = 0; i < l; i++) {    // Read indexes
-                mtxTab[i].setIndex(in.readTTFUShort());
-                // if (minIndex > mtxTab[i].index)
-                // minIndex=(short)mtxTab[i].index;
-
-                if (mtxTab[i].getIndex() > 257) {
-                    numGlyphStrings++;
-                }
-
-                getLogger().debug("Post index: " + mtxTab[i].getIndex());
-            }
-            // firstChar=minIndex;
-            psGlyphsBuffer = new String[numGlyphStrings];
-            getLogger().debug("Reading " + numGlyphStrings
-                + " glyphnames" + ", was n num glyphs=" + l);
-            for (i = 0; i < psGlyphsBuffer.length; i++) {
-                psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
-            }
-
-            for (i = 0; i < l; i++) {
-                if (mtxTab[i].getIndex() < NMACGLYPHS) {
-                    mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
-                } else {
-                    k = mtxTab[i].getIndex() - NMACGLYPHS;
-
-                    getLogger().debug(k + " i=" + i + " mtx=" + mtxTab.length
-                        + " ps=" + psGlyphsBuffer.length);
-
-                    mtxTab[i].setName(psGlyphsBuffer[k]);
-                }
-            }
-
-            break;
-        case 0x00030000:
-            // Postscript format 3 contains no glyph names
-            getLogger().debug("Postscript format 3");
-            break;
-        default:
-            getLogger().error("Unknown Postscript format: " + postFormat);
-        }
-    }
-
-
-    /**
-     * Read the "OS/2" table
-     */
-    private final void readOS2(FontFileReader in) throws IOException {
-        // Check if font is embeddable
-        if (dirTabs.get("OS/2") != null) {
-            seekTab(in, "OS/2", 2 * 4);
-            int fsType = in.readTTFUShort();
-            if (fsType == 2) {
-                isEmbeddable = false;
-            } else {
-                isEmbeddable = true;
-            }
-        } else {
-            isEmbeddable = true;
-        }
-    }
-
-    /**
-     * Read the "loca" table.
-     * @param in FontFileReader to read from
-     * @throws IOException In case of a I/O problem
-     */
-    protected final void readIndexToLocation(FontFileReader in)
-            throws IOException {
-        seekTab(in, "loca", 0);
-        for (int i = 0; i < numberOfGlyphs; i++) {
-            mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
-                                 : (in.readTTFUShort() << 1));
-        }
-        lastLoca = (locaFormat == 1 ? in.readTTFULong()
-                    : (in.readTTFUShort() << 1));
-    }
-
-    /**
-     * Read the "glyf" table to find the bounding boxes.
-     * @param in FontFileReader to read from
-     * @throws IOException In case of a I/O problem
-     */
-    private final void readGlyf(FontFileReader in) throws IOException {
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
-        for (int i = 0; i < (numberOfGlyphs - 1); i++) {
-            if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
-                in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
-                in.skip(2);
-                final int[] bbox = {
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort()};
-                mtxTab[i].setBoundingBox(bbox);
-            } else {
-                mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
-            }
-        }
-
-
-        long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
-        for (int i = 0; i < numberOfGlyphs; i++) {
-            if ((i + 1) >= mtxTab.length
-                    || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
-                in.seekSet(n + mtxTab[i].getOffset());
-                in.skip(2);
-                final int[] bbox = {
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort(),
-                    in.readTTFShort()};
-                mtxTab[i].setBoundingBox(bbox);
-            } else {
-                /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
-                final int bbox0 = mtxTab[0].getBoundingBox()[0];
-                final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
-                mtxTab[i].setBoundingBox(bbox);
-                /* Original code
-                mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
-                mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
-                mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
-                mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
-            }
-            getLogger().debug(mtxTab[i].toString(this));
-        }
-    }
-
-    /**
-     * Read the "name" table.
-     * @param in FontFileReader to read from
-     * @throws IOException In case of a I/O problem
-     */
-    private final void readName(FontFileReader in) throws IOException {
-        seekTab(in, "name", 2);
-        int i = in.getCurrentPos();
-        int n = in.readTTFUShort();
-        int j = in.readTTFUShort() + i - 2;
-        i += 2 * 2;
-
-        while (n-- > 0) {
-            // getLogger().debug("Iteration: " + n);
-            in.seekSet(i);
-            final int platformID = in.readTTFUShort();
-            final int encodingID = in.readTTFUShort();
-            /*final int language_id =*/ in.readTTFUShort(); //Skip language id
-
-            int k = in.readTTFUShort();
-            int l = in.readTTFUShort();
-
-            if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))
-                    && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
-                // if (k==1 || k==2 || k==0 || k==4 || k==6) {
-                in.seekSet(j + in.readTTFUShort());
-                String txt = in.readTTFString(l);
-                // getLogger().debug(platform_id + " " + encoding_id
-                //     + " " + k + " " + txt);
-                switch (k) {
-                case 0:
-                    notice = txt;
-                    break;
-                case 1:
-                    familyName = txt;
-                    break;
-                case 2:
-                    subFamilyName = txt;
-                    break;
-                case 4:
-                    fullName = txt;
-                    break;
-                case 6:
-                    fontName = txt;
-                    break;
-                }
-                if (!notice.equals("")
-                        && !fullName.equals("")
-                        && !fontName.equals("")
-                        && !familyName.equals("")
-                        && !subFamilyName.equals("")) {
-                    break;
-                }
-            }
-            i += 6 * 2;
-        }
-    }
-
-    /**
-     * Read the "PCLT" table to find xHeight and capHeight.
-     * @param in FontFileReader to read from
-     * @throws IOException In case of a I/O problem
-     */
-    private final void readPCLT(FontFileReader in) throws IOException {
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
-        if (dirTab != null) {
-            in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
-            xHeight = in.readTTFUShort();
-            in.skip(2 * 2);
-            capHeight = in.readTTFUShort();
-            in.skip(2 + 16 + 8 + 6 + 1 + 1);
-
-            int serifStyle = in.readTTFUByte();
-            serifStyle = serifStyle >> 6;
-            serifStyle = serifStyle & 3;
-            if (serifStyle == 1) {
-                hasSerifs = false;
-            } else {
-                hasSerifs = true;
-            }
-        } else {
-            // Approximate capHeight from height of "H"
-            // It's most unlikly that a font misses the PCLT table
-            // This also assumes that psocriptnames exists ("H")
-            // Should look it up int the cmap (that wouldn't help
-            // for charsets without H anyway...)
-            for (int i = 0; i < mtxTab.length; i++) {
-                if ("H".equals(mtxTab[i].getName())) {
-                    capHeight = mtxTab[i].getBoundingBox()[3] - mtxTab[i].getBoundingBox()[1];
-                }
-            }
-        }
-    }
-
-    /**
-     * Read the kerning table, create a table for both CIDs and
-     * winAnsiEncoding.
-     * @param in FontFileReader to read from
-     * @throws IOException In case of a I/O problem
-     */
-    private final void readKerning(FontFileReader in) throws IOException {
-        // Read kerning
-        kerningTab = new java.util.HashMap();
-        ansiKerningTab = new java.util.HashMap();
-        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
-        if (dirTab != null) {
-            seekTab(in, "kern", 2);
-            for (int n = in.readTTFUShort(); n > 0; n--) {
-                in.skip(2 * 2);
-                int k = in.readTTFUShort();
-                if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
-                    return;
-                }
-                if ((k >> 8) != 0) {
-                    continue;
-                }
-
-                k = in.readTTFUShort();
-                in.skip(3 * 2);
-                while (k-- > 0) {
-                    int i = in.readTTFUShort();
-                    int j = in.readTTFUShort();
-                    int kpx = in.readTTFShort();
-                    if (kpx != 0) {
-                        // CID table
-                        Integer iObj = new Integer(i);
-                        Map adjTab = (Map)kerningTab.get(iObj);
-                        if (adjTab == null) {
-                            adjTab = new java.util.HashMap();
-                        }
-                        adjTab.put(new Integer(j),
-                                   new Integer((int)convertTTFUnit2PDFUnit(kpx)));
-                        kerningTab.put(iObj, adjTab);
-                    }
-                }
-            }
-            // getLogger().debug(kerningTab.toString());
-
-            // Create winAnsiEncoded kerning table
-            Iterator ae = kerningTab.keySet().iterator();
-            while (ae.hasNext()) {
-                Integer cidKey = (Integer)ae.next();
-                Map akpx = new java.util.HashMap();
-                Map ckpx = (Map)kerningTab.get(cidKey);
-
-                Iterator aee = ckpx.keySet().iterator();
-                while (aee.hasNext()) {
-                    Integer cidKey2 = (Integer)aee.next();
-                    Integer kern = (Integer)ckpx.get(cidKey2);
-
-                    Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
-                    while (uniMap.hasNext()) {
-                        Integer unicodeKey = (Integer)uniMap.next();
-                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
-                        for (int u = 0; u < ansiKeys.length; u++) {
-                            akpx.put(ansiKeys[u], kern);
-                        }
-                    }
-                }
-
-                if (akpx.size() > 0) {
-                    Iterator uniMap = mtxTab[cidKey.intValue()].getUnicodeIndex().listIterator();
-                    while (uniMap.hasNext()) {
-                        Integer unicodeKey = (Integer)uniMap.next();
-                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
-                        for (int u = 0; u < ansiKeys.length; u++) {
-                            ansiKerningTab.put(ansiKeys[u], akpx);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Return a List with TTFCmapEntry.
-     * @return A list of TTFCmapEntry objects
-     */
-    public List getCMaps() {
-        return cmaps;
-    }
-
-    /**
-     * Check if this is a TrueType collection and that the given
-     * name exists in the collection.
-     * If it does, set offset in fontfile to the beginning of
-     * the Table Directory for that font.
-     * @param in FontFileReader to read from
-     * @param name The name to check
-     * @return True if not collection or font name present, false otherwise
-     * @throws IOException In case of an I/O problem
-     */
-    protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
-        String tag = in.readTTFString(4);
-
-        if ("ttcf".equals(tag)) {
-            // This is a TrueType Collection
-            in.skip(4);
-
-            // Read directory offsets
-            int numDirectories = (int)in.readTTFULong();
-            // int numDirectories=in.readTTFUShort();
-            long[] dirOffsets = new long[numDirectories];
-            for (int i = 0; i < numDirectories; i++) {
-                dirOffsets[i] = in.readTTFULong();
-            }
-
-            getLogger().debug("This is a TrueType collection file with"
-                                   + numDirectories + " fonts");
-            getLogger().debug("Containing the following fonts: ");
-            // Read all the directories and name tables to check
-            // If the font exists - this is a bit ugly, but...
-            boolean found = false;
-
-            // Iterate through all name tables even if font
-            // Is found, just to show all the names
-            long dirTabOffset = 0;
-            for (int i = 0; (i < numDirectories); i++) {
-                in.seekSet(dirOffsets[i]);
-                readDirTabs(in);
-
-                readName(in);
-
-                if (fullName.equals(name)) {
-                    found = true;
-                    dirTabOffset = dirOffsets[i];
-                    getLogger().debug("* " + fullName);
-                } else {
-                    getLogger().debug(fullName);
-                }
-
-                // Reset names
-                notice = "";
-                fullName = "";
-                familyName = "";
-                fontName = "";
-                subFamilyName = "";
-            }
-
-            in.seekSet(dirTabOffset);
-            return found;
-        } else {
-            in.seekSet(0);
-            return true;
-        }
-    }
-
-    /*
-     * Helper classes, they are not very efficient, but that really
-     * doesn't matter...
-     */
-    private Integer[] unicodeToWinAnsi(int unicode) {
-        List ret = new java.util.ArrayList();
-        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
-            if (unicode == Glyphs.WINANSI_ENCODING[i]) {
-                ret.add(new Integer(i));
-            }
-        }
-        return (Integer[])ret.toArray(new Integer[0]);
-    }
-
-    /**
-     * Dumps a few informational values to System.out.
-     */
-    public void printStuff() {
-        System.out.println("Font name:   " + fontName);
-        System.out.println("Full name:   " + fullName);
-        System.out.println("Family name: " + familyName);
-        System.out.println("Subfamily name: " + subFamilyName);
-        System.out.println("Notice:      " + notice);
-        System.out.println("xHeight:     " + (int)convertTTFUnit2PDFUnit(xHeight));
-        System.out.println("capheight:   " + (int)convertTTFUnit2PDFUnit(capHeight));
-
-        int italic = (int)(italicAngle >> 16);
-        System.out.println("Italic:      " + italic);
-        System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
-        if ((italicAngle % 0x10000) > 0) {
-            System.out.print("."
-                             + (short)((italicAngle % 0x10000) * 1000)
-                               / 0x10000);
-        }
-        System.out.println();
-        System.out.println("Ascender:    " + convertTTFUnit2PDFUnit(ascender));
-        System.out.println("Descender:   " + convertTTFUnit2PDFUnit(descender));
-        System.out.println("FontBBox:    [" + (int)convertTTFUnit2PDFUnit(fontBBox1)
-                           + " " + (int)convertTTFUnit2PDFUnit(fontBBox2) + " "
-                           + (int)convertTTFUnit2PDFUnit(fontBBox3) + " "
-                           + (int)convertTTFUnit2PDFUnit(fontBBox4) + "]");
-    }
-
-    /**
-     * Static main method to get info about a TrueType font.
-     * @param args The command line arguments
-     */
-    public static void main(String[] args) {
-        int level = ConsoleLogger.LEVEL_WARN;
-        Logger log = new ConsoleLogger(level);
-        try {
-            TTFFile ttfFile = new TTFFile();
-            ttfFile.enableLogging(log);
-
-            FontFileReader reader = new FontFileReader(args[0]);
-
-            String name = null;
-            if (args.length >= 2) {
-                name = args[1];
-            }
-
-            ttfFile.readFont(reader, name);
-            ttfFile.printStuff();
-
-        } catch (IOException ioe) {
-            log.error("Problem reading font: " + ioe.toString(), ioe);
-        }
-    }
-
-}
-
-
-/**
- * Key-value helper class
- */
-class UnicodeMapping {
-
-    private int uIdx;
-    private int gIdx;
-
-    UnicodeMapping(int gIdx, int uIdx) {
-        this.uIdx = uIdx;
-        this.gIdx = gIdx;
-    }
-
-    /**
-     * Returns the gIdx.
-     * @return int
-     */
-    public int getGIdx() {
-        return gIdx;
-    }
-
-    /**
-     * Returns the uIdx.
-     * @return int
-     */
-    public int getUIdx() {
-        return uIdx;
-    }
-
-}
diff --git a/src/org/apache/fop/fonts/TTFMtxEntry.java b/src/org/apache/fop/fonts/TTFMtxEntry.java
deleted file mode 100644 (file)
index 747be55..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.util.List;
-
-class TTFMtxEntry {
-
-    private int wx;
-    private int lsb;
-    private String name = "";
-    private int index;
-    private List unicodeIndex = new java.util.ArrayList();
-    private int[] boundingBox = new int[4];
-    private long offset;
-    private byte found = 0;
-
-    public String toString(TTFFile t) {
-        return "Glyph " + name + " index: " + index + " bbox [ "
-             + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " "
-             + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " "
-             + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " "
-             + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: "
-             + t.convertTTFUnit2PDFUnit(wx);
-    }
-
-    /**
-     * Returns the boundingBox.
-     * @return int[]
-     */
-    public int[] getBoundingBox() {
-        return boundingBox;
-    }
-
-    /**
-     * Sets the boundingBox.
-     * @param boundingBox The boundingBox to set
-     */
-    public void setBoundingBox(int[] boundingBox) {
-        this.boundingBox = boundingBox;
-    }
-
-    /**
-     * Returns the found.
-     * @return byte
-     */
-    public byte getFound() {
-        return found;
-    }
-
-    /**
-     * Returns the index.
-     * @return int
-     */
-    public int getIndex() {
-        return index;
-    }
-
-    /**
-     * Returns the lsb.
-     * @return int
-     */
-    public int getLsb() {
-        return lsb;
-    }
-
-    /**
-     * Returns the name.
-     * @return String
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * Returns the offset.
-     * @return long
-     */
-    public long getOffset() {
-        return offset;
-    }
-
-    /**
-     * Returns the unicodeIndex.
-     * @return List
-     */
-    public List getUnicodeIndex() {
-        return unicodeIndex;
-    }
-
-    /**
-     * Returns the wx.
-     * @return int
-     */
-    public int getWx() {
-        return wx;
-    }
-
-    /**
-     * Sets the found.
-     * @param found The found to set
-     */
-    public void setFound(byte found) {
-        this.found = found;
-    }
-
-    /**
-     * Sets the index.
-     * @param index The index to set
-     */
-    public void setIndex(int index) {
-        this.index = index;
-    }
-
-    /**
-     * Sets the lsb.
-     * @param lsb The lsb to set
-     */
-    public void setLsb(int lsb) {
-        this.lsb = lsb;
-    }
-
-    /**
-     * Sets the name.
-     * @param name The name to set
-     */
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    /**
-     * Sets the offset.
-     * @param offset The offset to set
-     */
-    public void setOffset(long offset) {
-        this.offset = offset;
-    }
-
-    /**
-     * Sets the wx.
-     * @param wx The wx to set
-     */
-    public void setWx(int wx) {
-        this.wx = wx;
-    }
-
-
-}
diff --git a/src/org/apache/fop/fonts/TTFSubSetFile.java b/src/org/apache/fop/fonts/TTFSubSetFile.java
deleted file mode 100644 (file)
index 7a1a98e..0000000
+++ /dev/null
@@ -1,835 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-
-/**
- * Reads a TrueType file and generates a subset
- * that can be used to embed a TrueType CID font
- * TrueType tables needed for embedded CID fonts are:
- * "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm".
- * The TrueType spec can be found at the Microsoft
- * Typography site: http://www.microsoft.com/truetype/
- */
-public class TTFSubSetFile extends TTFFile {
-
-    private byte[] output = null;
-    private int realSize = 0;
-    private int currentPos = 0;
-
-    /*
-     * Offsets in name table to be filled out by table.
-     * The offsets are to the checkSum field
-     */
-    private int cvtDirOffset = 0;
-    private int fpgmDirOffset = 0;
-    private int glyfDirOffset = 0;
-    private int headDirOffset = 0;
-    private int hheaDirOffset = 0;
-    private int hmtxDirOffset = 0;
-    private int locaDirOffset = 0;
-    private int maxpDirOffset = 0;
-    private int prepDirOffset = 0;
-
-    private int checkSumAdjustmentOffset = 0;
-    private int locaOffset = 0;
-
-    /**
-     * Initalize the output array
-     */
-    private void init(int size) {
-        output = new byte[size];
-        realSize = 0;
-        currentPos = 0;
-
-        // createDirectory()
-    }
-
-    /**
-     * Create the directory table
-     */
-    private void createDirectory() {
-        int numTables = 9;
-        // Create the TrueType header
-        writeByte((byte)0);
-        writeByte((byte)1);
-        writeByte((byte)0);
-        writeByte((byte)0);
-        realSize += 4;
-
-        writeUShort(numTables);
-        realSize += 2;
-
-        // Create searchRange, entrySelector and rangeShift
-        int maxPow = maxPow2(numTables);
-        int searchRange = maxPow * 16;
-        writeUShort(searchRange);
-        realSize += 2;
-
-        writeUShort(maxPow);
-        realSize += 2;
-
-        writeUShort((numTables * 16) - searchRange);
-        realSize += 2;
-
-        // Create space for the table entries
-        writeString("cvt ");
-        cvtDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("fpgm");
-        fpgmDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("glyf");
-        glyfDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("head");
-        headDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("hhea");
-        hheaDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("hmtx");
-        hmtxDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("loca");
-        locaDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("maxp");
-        maxpDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
-        writeString("prep");
-        prepDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-    }
-
-
-    /**
-     * Copy the cvt table as is from original font to subset font
-     */
-    private void createCvt(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "cvt ", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(cvtDirOffset, checksum);
-            writeULong(cvtDirOffset + 4, currentPos);
-            writeULong(cvtDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find cvt table");
-        }
-    }
-
-
-
-    /**
-     * Copy the fpgm table as is from original font to subset font
-     */
-    private void createFpgm(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "fpgm", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(fpgmDirOffset, checksum);
-            writeULong(fpgmDirOffset + 4, currentPos);
-            writeULong(fpgmDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find fpgm table");
-        }
-    }
-
-
-
-    /**
-     * Create an empty loca table without updating checksum
-     */
-    private void createLoca(int size) throws IOException {
-        pad4();
-        locaOffset = currentPos;
-        writeULong(locaDirOffset + 4, currentPos);
-        writeULong(locaDirOffset + 8, size * 4 + 4);
-        currentPos += size * 4 + 4;
-        realSize += size * 4 + 4;
-    }
-
-
-    /**
-     * Copy the maxp table as is from original font to subset font
-     * and set num glyphs to size
-     */
-    private void createMaxp(FontFileReader in, int size) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "maxp", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-            writeUShort(currentPos + 4, size);
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(maxpDirOffset, checksum);
-            writeULong(maxpDirOffset + 4, currentPos);
-            writeULong(maxpDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find maxp table");
-        }
-    }
-
-
-    /**
-     * Copy the prep table as is from original font to subset font
-     */
-    private void createPrep(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "prep", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(prepDirOffset, checksum);
-            writeULong(prepDirOffset + 4, currentPos);
-            writeULong(prepDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find prep table");
-        }
-    }
-
-
-    /**
-     * Copy the hhea table as is from original font to subset font
-     * and fill in size of hmtx table
-     */
-    private void createHhea(FontFileReader in, int size) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "hhea", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-            writeUShort((int)entry.getLength() + currentPos - 2, size);
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(hheaDirOffset, checksum);
-            writeULong(hheaDirOffset + 4, currentPos);
-            writeULong(hheaDirOffset + 8, (int)entry.getLength());
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find hhea table");
-        }
-    }
-
-
-    /**
-     * Copy the head table as is from original font to subset font
-     * and set indexToLocaFormat to long and set
-     * checkSumAdjustment to 0, store offset to checkSumAdjustment
-     * in checkSumAdjustmentOffset
-     */
-    private void createHead(FontFileReader in) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
-        if (entry != null) {
-            pad4();
-            seekTab(in, "head", 0);
-            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
-                             0, output, currentPos, (int)entry.getLength());
-
-            checkSumAdjustmentOffset = currentPos + 8;
-            output[currentPos + 8] = 0;     // Set checkSumAdjustment to 0
-            output[currentPos + 9] = 0;
-            output[currentPos + 10] = 0;
-            output[currentPos + 11] = 0;
-            output[currentPos + 50] = 0;    // long locaformat
-            output[currentPos + 51] = 1;    // long locaformat
-
-            int checksum = getCheckSum(currentPos, (int)entry.getLength());
-            writeULong(headDirOffset, checksum);
-            writeULong(headDirOffset + 4, currentPos);
-            writeULong(headDirOffset + 8, (int)entry.getLength());
-
-            currentPos += (int)entry.getLength();
-            realSize += (int)entry.getLength();
-        } else {
-            throw new IOException("Can't find head table");
-        }
-    }
-
-
-    /**
-     * Create the glyf table and fill in loca table
-     */
-    private void createGlyf(FontFileReader in,
-                            Map glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
-        int size = 0;
-        int start = 0;
-        int endOffset = 0;    // Store this as the last loca
-        if (entry != null) {
-            pad4();
-            start = currentPos;
-
-            /* Loca table must be in order by glyph index, so build
-             * an array first and then write the glyph info and
-             * location offset.
-             */
-            int[] origIndexes = new int[glyphs.size()];
-
-            Iterator e = glyphs.keySet().iterator();
-            while (e.hasNext()) {
-                Integer origIndex = (Integer)e.next();
-                Integer subsetIndex = (Integer)glyphs.get(origIndex);
-                origIndexes[subsetIndex.intValue()] = origIndex.intValue();
-            }
-
-            for (int i = 0; i < origIndexes.length; i++) {
-                int glyphLength = 0;
-                int nextOffset = 0;
-                int origGlyphIndex = origIndexes[i];
-                if (origGlyphIndex >= (mtxTab.length - 1)) {
-                    nextOffset = (int)lastLoca;
-                } else {
-                    nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
-                }
-                glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
-
-                // Copy glyph
-                System.arraycopy(
-                    in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
-                        glyphLength), 0,
-                    output, currentPos,
-                    glyphLength);
-
-
-                // Update loca table
-                writeULong(locaOffset + i * 4, currentPos - start);
-                if ((currentPos - start + glyphLength) > endOffset) {
-                    endOffset = (currentPos - start + glyphLength);
-                }
-
-                currentPos += glyphLength;
-                realSize += glyphLength;
-
-            }
-
-            size = currentPos - start;
-
-            int checksum = getCheckSum(start, size);
-            writeULong(glyfDirOffset, checksum);
-            writeULong(glyfDirOffset + 4, start);
-            writeULong(glyfDirOffset + 8, size);
-            currentPos += 12;
-            realSize += 12;
-
-            // Update loca checksum and last loca index
-            writeULong(locaOffset + glyphs.size() * 4, endOffset);
-
-            checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
-            writeULong(locaDirOffset, checksum);
-        } else {
-            throw new IOException("Can't find glyf table");
-        }
-    }
-
-
-    /**
-     * Create the hmtx table by copying metrics from original
-     * font to subset font. The glyphs Map contains an
-     * Integer key and Integer value that maps the original
-     * metric (key) to the subset metric (value)
-     */
-    private void createHmtx(FontFileReader in,
-                            Map glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
-
-        int longHorMetricSize = glyphs.size() * 2;
-        int leftSideBearingSize = glyphs.size() * 2;
-        int hmtxSize = longHorMetricSize + leftSideBearingSize;
-
-        if (entry != null) {
-            pad4();
-            //int offset = (int)entry.offset;
-            Iterator e = glyphs.keySet().iterator();
-            while (e.hasNext()) {
-                Integer origIndex = (Integer)e.next();
-                Integer subsetIndex = (Integer)glyphs.get(origIndex);
-
-                writeUShort(currentPos + subsetIndex.intValue() * 4,
-                            mtxTab[origIndex.intValue()].getWx());
-                writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
-                            mtxTab[origIndex.intValue()].getLsb());
-            }
-
-            int checksum = getCheckSum(currentPos, hmtxSize);
-            writeULong(hmtxDirOffset, checksum);
-            writeULong(hmtxDirOffset + 4, currentPos);
-            writeULong(hmtxDirOffset + 8, hmtxSize);
-            currentPos += hmtxSize;
-            realSize += hmtxSize;
-        } else {
-            throw new IOException("Can't find hmtx table");
-        }
-    }
-
-    /**
-     * Returns a List containing the glyph itself plus all glyphs
-     * that this composite glyph uses
-     */
-    private List getIncludedGlyphs(FontFileReader in, int glyphOffset,
-                                     Integer glyphIdx) throws IOException {
-        List ret = new java.util.ArrayList();
-        ret.add(glyphIdx);
-        int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() + 10;
-        Integer compositeIdx = null;
-        int flags = 0;
-        boolean moreComposites = true;
-        while (moreComposites) {
-            flags = in.readTTFUShort(offset);
-            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
-            ret.add(compositeIdx);
-
-            offset += 4;
-            if ((flags & 1) > 0) {
-                // ARG_1_AND_ARG_2_ARE_WORDS
-                offset += 4;
-            } else {
-                offset += 2;
-            }
-
-            if ((flags & 8) > 0) {
-                offset += 2;    // WE_HAVE_A_SCALE
-            } else if ((flags & 64) > 0) {
-                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
-            } else if ((flags & 128) > 0) {
-                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
-            }
-
-            if ((flags & 32) > 0) {
-                moreComposites = true;
-            } else {
-                moreComposites = false;
-            }
-        }
-
-        return ret;
-    }
-
-
-    /**
-     * Rewrite all compositepointers in glyphindex glyphIdx
-     *
-     */
-    private void remapComposite(FontFileReader in, Map glyphs,
-                                int glyphOffset,
-                                Integer glyphIdx) throws IOException {
-        int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset()
-                     + 10;
-
-        Integer compositeIdx = null;
-        int flags = 0;
-        boolean moreComposites = true;
-
-        while (moreComposites) {
-            flags = in.readTTFUShort(offset);
-            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
-            Integer newIdx = (Integer)glyphs.get(compositeIdx);
-            if (newIdx == null) {
-                // This errormessage would look much better
-                // if the fontname was printed to
-                //log.error("An embedded font "
-                //                     + "contains bad glyph data. "
-                //                     + "Characters might not display "
-                //                     + "correctly.");
-                moreComposites = false;
-                continue;
-            }
-
-            in.writeTTFUShort(offset + 2, newIdx.intValue());
-
-            offset += 4;
-
-            if ((flags & 1) > 0) {
-                // ARG_1_AND_ARG_2_ARE_WORDS
-                offset += 4;
-            } else {
-                offset += 2;
-            }
-
-            if ((flags & 8) > 0) {
-                offset += 2;    // WE_HAVE_A_SCALE
-            } else if ((flags & 64) > 0) {
-                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
-            } else if ((flags & 128) > 0) {
-                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
-            }
-
-            if ((flags & 32) > 0) {
-                moreComposites = true;
-            } else {
-                moreComposites = false;
-            }
-        }
-    }
-
-
-    /**
-     * Scan all the original glyphs for composite glyphs and add those glyphs
-     * to the glyphmapping also rewrite the composite glyph pointers to the new
-     * mapping
-     */
-    private void scanGlyphs(FontFileReader in,
-                            Map glyphs) throws IOException {
-        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
-        Map newComposites = null;
-        Map allComposites = new java.util.HashMap();
-
-        int newIndex = glyphs.size();
-
-        if (entry != null) {
-            while (newComposites == null || newComposites.size() > 0) {
-                // Inefficient to iterate through all glyphs
-                newComposites = new java.util.HashMap();
-
-                Iterator e = glyphs.keySet().iterator();
-                while (e.hasNext()) {
-                    Integer origIndex = (Integer)e.next();
-
-                    if (in.readTTFShort(entry.getOffset()
-                                        + mtxTab[origIndex.intValue()].getOffset()) < 0) {
-                        // origIndex is a composite glyph
-                        allComposites.put(origIndex, glyphs.get(origIndex));
-                        List composites =
-                            getIncludedGlyphs(in, (int)entry.getOffset(),
-                                              origIndex);
-
-                        // Iterate through all composites pointed to
-                        // by this composite and check if they exists
-                        // in the glyphs map, add them if not.
-                        Iterator cps = composites.iterator();
-                        while (cps.hasNext()) {
-                            Integer cIdx = (Integer)cps.next();
-                            if (glyphs.get(cIdx) == null
-                                    && newComposites.get(cIdx) == null) {
-                                newComposites.put(cIdx,
-                                                  new Integer(newIndex));
-                                newIndex++;
-                            }
-                        }
-                    }
-                }
-
-                // Add composites to glyphs
-                Iterator m = newComposites.keySet().iterator();
-                while (m.hasNext()) {
-                    Integer im = (Integer)m.next();
-                    glyphs.put(im, newComposites.get(im));
-                }
-            }
-
-            // Iterate through all composites to remap their composite index
-            Iterator ce = allComposites.keySet().iterator();
-            while (ce.hasNext()) {
-                remapComposite(in, glyphs, (int)entry.getOffset(),
-                               (Integer)ce.next());
-            }
-
-        } else {
-            throw new IOException("Can't find glyf table");
-        }
-    }
-
-
-
-    /**
-     * Returns a subset of the original font.
-     *
-     * @param in FontFileReader to read from
-     * @param name Name to be checked for in the font file
-     * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
-     * new index as (Integer) value)
-     * @return A subset of the original font
-     * @throws IOException in case of an I/O problem
-     */
-    public byte[] readFont(FontFileReader in, String name,
-                           Map glyphs) throws IOException {
-
-        //Check if TrueType collection, and that the name exists in the collection
-        if (!checkTTC(in, name)) {
-            throw new IOException("Failed to read font");
-        }
-
-        output = new byte[in.getFileSize()];
-
-        readDirTabs(in);
-        readFontHeader(in);
-        getNumGlyphs(in);
-        readHorizontalHeader(in);
-        readHorizontalMetrics(in);
-        readIndexToLocation(in);
-
-        scanGlyphs(in, glyphs);
-
-        createDirectory();                // Create the TrueType header and directory
-
-        createHead(in);
-        createHhea(in, glyphs.size());    // Create the hhea table
-        createHmtx(in, glyphs);           // Create hmtx table
-        createMaxp(in, glyphs.size());    // copy the maxp table
-
-        try {
-            createCvt(in);    // copy the cvt table
-        } catch (IOException ex) {
-            // Cvt is optional (only required for OpenType (MS) fonts)
-            //log.error("TrueType warning: " + ex.getMessage());
-        }
-
-        try {
-            createFpgm(in);    // copy fpgm table
-        } catch (IOException ex) {
-            // Fpgm is optional (only required for OpenType (MS) fonts)
-            //log.error("TrueType warning: " + ex.getMessage());
-        }
-
-        try {
-            createPrep(in);    // copy prep table
-        } catch (IOException ex) {
-            // Prep is optional (only required for OpenType (MS) fonts)
-            //log.error("TrueType warning: " + ex.getMessage());
-        }
-
-        try {
-            createLoca(glyphs.size());    // create empty loca table
-        } catch (IOException ex) {
-            // Loca is optional (only required for OpenType (MS) fonts)
-            //log.error("TrueType warning: " + ex.getMessage());
-        }
-
-        try {
-            createGlyf(in, glyphs);
-        } catch (IOException ex) {
-            // Glyf is optional (only required for OpenType (MS) fonts)
-            //log.error("TrueType warning: " + ex.getMessage());
-        }
-
-        pad4();
-        createCheckSumAdjustment();
-
-        byte[] ret = new byte[realSize];
-        System.arraycopy(output, 0, ret, 0, realSize);
-
-        return ret;
-    }
-
-    /**
-     * writes a ISO-8859-1 string at the currentPosition
-     * updates currentPosition but not realSize
-     * @return number of bytes written
-     */
-    private int writeString(String str) {
-        int length = 0;
-        try {
-            byte[] buf = str.getBytes("ISO-8859-1");
-            System.arraycopy(buf, 0, output, currentPos, buf.length);
-            length = buf.length;
-            currentPos += length;
-        } catch (java.io.UnsupportedEncodingException e) {
-            // This should never happen!
-        }
-
-        return length;
-    }
-
-    /**
-     * Appends a byte to the output array,
-     * updates currentPost but not realSize
-     */
-    private void writeByte(byte b) {
-        output[currentPos++] = b;
-    }
-
-    /**
-     * Appends a USHORT to the output array,
-     * updates currentPost but not realSize
-     */
-    private void writeUShort(int s) {
-        byte b1 = (byte)((s >> 8) & 0xff);
-        byte b2 = (byte)(s & 0xff);
-        writeByte(b1);
-        writeByte(b2);
-    }
-
-    /**
-     * Appends a USHORT to the output array,
-     * at the given position without changing currentPos
-     */
-    private void writeUShort(int pos, int s) {
-        byte b1 = (byte)((s >> 8) & 0xff);
-        byte b2 = (byte)(s & 0xff);
-        output[pos] = b1;
-        output[pos + 1] = b2;
-    }
-
-    /**
-     * Appends a ULONG to the output array,
-     * updates currentPos but not realSize
-     */
-    private void writeULong(int s) {
-        byte b1 = (byte)((s >> 24) & 0xff);
-        byte b2 = (byte)((s >> 16) & 0xff);
-        byte b3 = (byte)((s >> 8) & 0xff);
-        byte b4 = (byte)(s & 0xff);
-        writeByte(b1);
-        writeByte(b2);
-        writeByte(b3);
-        writeByte(b4);
-    }
-
-    /**
-     * Appends a ULONG to the output array,
-     * at the given position without changing currentPos
-     */
-    private void writeULong(int pos, int s) {
-        byte b1 = (byte)((s >> 24) & 0xff);
-        byte b2 = (byte)((s >> 16) & 0xff);
-        byte b3 = (byte)((s >> 8) & 0xff);
-        byte b4 = (byte)(s & 0xff);
-        output[pos] = b1;
-        output[pos + 1] = b2;
-        output[pos + 2] = b3;
-        output[pos + 3] = b4;
-    }
-
-    /**
-     * Read a signed short value at given position
-     */
-    private short readShort(int pos) {
-        int ret = readUShort(pos);
-        return (short)ret;
-    }
-
-    /**
-     * Read a unsigned short value at given position
-     */
-    private int readUShort(int pos) {
-        int ret = (int)output[pos];
-        if (ret < 0) {
-            ret += 256;
-        }
-        ret = ret << 8;
-        if ((int)output[pos + 1] < 0) {
-            ret |= (int)output[pos + 1] + 256;
-        } else {
-            ret |= (int)output[pos + 1];
-        }
-
-        return ret;
-    }
-
-    /**
-     * Create a padding in the fontfile to align
-     * on a 4-byte boundary
-     */
-    private void pad4() {
-        int padSize = currentPos % 4;
-        for (int i = 0; i < padSize; i++) {
-            output[currentPos++] = 0;
-            realSize++;
-        }
-    }
-
-    /**
-     * Returns the maximum power of 2 <= max
-     */
-    private int maxPow2(int max) {
-        int i = 0;
-        while (Math.pow(2, (double)i) < max) {
-            i++;
-        }
-
-        return (i - 1);
-    }
-
-    private int log2(int num) {
-        return (int)(Math.log((double)num) / Math.log(2));
-    }
-
-
-    private int getCheckSum(int start, int size) {
-        return (int)getLongCheckSum(start, size);
-    }
-
-    private long getLongCheckSum(int start, int size) {
-        // All the tables here are aligned on four byte boundaries
-        // Add remainder to size if it's not a multiple of 4
-        int remainder = size % 4;
-        if (remainder != 0) {
-            size += remainder;
-        }
-
-        long sum = 0;
-
-        for (int i = 0; i < size; i += 4) {
-            int l = (int)(output[start + i] << 24);
-            l += (int)(output[start + i + 1] << 16);
-            l += (int)(output[start + i + 2] << 16);
-            l += (int)(output[start + i + 3] << 16);
-            sum += l;
-            if (sum > 0xffffffff) {
-                sum = sum - 0xffffffff;
-            }
-        }
-
-        return sum;
-    }
-
-    private void createCheckSumAdjustment() {
-        long sum = getLongCheckSum(0, realSize);
-        int checksum = (int)(0xb1b0afba - sum);
-        writeULong(checkSumAdjustmentOffset, checksum);
-    }
-
-}
-
-
-
index 239221492fd43bbc7974feaaa76e038894cf2320..424104bd9e11b7f5c5c9f8daacc3f7cbb3f5d14c 100644 (file)
@@ -25,9 +25,9 @@ import org.apache.avalon.framework.logger.ConsoleLogger;
 import org.apache.avalon.framework.logger.Logger;
 
 //FOP
-import org.apache.fop.fonts.FontFileReader;
-import org.apache.fop.fonts.TTFCmapEntry;
-import org.apache.fop.fonts.TTFFile;
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.TTFCmapEntry;
+import org.apache.fop.fonts.truetype.TTFFile;
 
 /**
  * A tool which reads TTF files and generates
diff --git a/src/org/apache/fop/fonts/package.html b/src/org/apache/fop/fonts/package.html
new file mode 100644 (file)
index 0000000..33e1e2c
--- /dev/null
@@ -0,0 +1,6 @@
+<HTML>
+<TITLE>org.apache.fop.fonts Package</TITLE>
+<BODY>
+<P>Classes for font handling. Subpackages contain command line applications for font metrics generation, font parsing classes etc.</P>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/src/org/apache/fop/fonts/truetype/FontFileReader.java b/src/org/apache/fop/fonts/truetype/FontFileReader.java
new file mode 100644 (file)
index 0000000..a1c8ea5
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.fop.util.StreamUtilities;
+
+/**
+ * Reads a TrueType font file into a byte array and
+ * provides file like functions for array access.
+ */
+public class FontFileReader {
+
+    private int fsize;      // file size
+    private int current;    // current position in file
+    private byte[] file;
+
+    /**
+     * Initializes class and reads stream. Init does not close stream.
+     *
+     * @param in InputStream to read from new array with size + inc
+     * @throws IOException In case of an I/O problem
+     */
+    private void init(InputStream in) throws java.io.IOException {
+        java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+        try {
+            StreamUtilities.streamCopy(in, bout);
+            this.file = bout.toByteArray();
+            this.fsize = this.file.length;
+            this.current = 0;
+        } finally {
+            bout.close();
+        }
+    }
+
+    /**
+     * Constructor
+     *
+     * @param fileName filename to read
+     * @throws IOException In case of an I/O problem
+     */
+    public FontFileReader(String fileName) throws IOException {
+        final File f = new File(fileName);
+        InputStream in = new java.io.FileInputStream(f);
+        try {
+            init(in);
+        } finally {
+            in.close();
+        }
+    }
+
+
+    /**
+     * Constructor
+     *
+     * @param in InputStream to read from
+     * @throws IOException In case of an I/O problem
+     */
+    public FontFileReader(InputStream in) throws IOException {
+        init(in);
+    }
+
+
+    /**
+     * Set current file position to offset
+     *
+     * @param offset The new offset to set
+     * @throws IOException In case of an I/O problem
+     */
+    public void seekSet(long offset) throws IOException {
+        if (offset > fsize || offset < 0) {
+            throw new java.io.EOFException("Reached EOF, file size=" + fsize
+                                           + " offset=" + offset);
+        }
+        current = (int)offset;
+    }
+
+    /**
+     * Set current file position to offset
+     *
+     * @param add The number of bytes to advance
+     * @throws IOException In case of an I/O problem
+     */
+    public void seekAdd(long add) throws IOException {
+        seekSet(current + add);
+    }
+
+    /**
+     * Skip a given number of bytes.
+     *
+     * @param add The number of bytes to advance
+     * @throws IOException In case of an I/O problem
+     */
+    public void skip(long add) throws IOException {
+        seekAdd(add);
+    }
+
+    /**
+     * Returns current file position.
+     *
+     * @return int The current position.
+     */
+    public int getCurrentPos() {
+        return current;
+    }
+
+    /**
+     * Returns the size of the file.
+     *
+     * @return int The filesize
+     */
+    public int getFileSize() {
+        return fsize;
+    }
+
+    /**
+     * Read 1 byte.
+     *
+     * @return One byte
+     * @throws IOException If EOF is reached
+     */
+    public byte read() throws IOException {
+        if (current > fsize) {
+            throw new java.io.EOFException("Reached EOF, file size=" + fsize);
+        }
+
+        final byte ret = file[current++];
+        return ret;
+    }
+
+    /**
+     * Read 1 signed byte.
+     *
+     * @return One byte
+     * @throws IOException If EOF is reached
+     */
+    public final byte readTTFByte() throws IOException {
+        return read();
+    }
+
+    /**
+     * Read 1 unsigned byte.
+     *
+     * @return One unsigned byte
+     * @throws IOException If EOF is reached
+     */
+    public final int readTTFUByte() throws IOException {
+        final byte buf = read();
+
+        if (buf < 0) {
+            return (int)(256 + buf);
+        } else {
+            return (int)buf;
+        }
+    }
+
+    /**
+     * Read 2 bytes signed.
+     *
+     * @return One signed short
+     * @throws IOException If EOF is reached
+     */
+    public final short readTTFShort() throws IOException {
+        final int ret = (readTTFUByte() << 8) + readTTFUByte();
+        final short sret = (short)ret;
+        return sret;
+    }
+
+    /**
+     * Read 2 bytes unsigned.
+     *
+     * @return One unsigned short
+     * @throws IOException If EOF is reached
+     */
+    public final int readTTFUShort() throws IOException {
+        final int ret = (readTTFUByte() << 8) + readTTFUByte();
+        return (int)ret;
+    }
+
+    /**
+     * Write a USHort at a given position.
+     *
+     * @param pos The absolute position to write to
+     * @param val The value to write
+     * @throws IOException If EOF is reached
+     */
+    public final void writeTTFUShort(int pos, int val) throws IOException {
+        if ((pos + 2) > fsize) {
+            throw new java.io.EOFException("Reached EOF");
+        }
+        final byte b1 = (byte)((val >> 8) & 0xff);
+        final byte b2 = (byte)(val & 0xff);
+        file[pos] = b1;
+        file[pos + 1] = b2;
+    }
+
+    /**
+     * Read 2 bytes signed at position pos without changing current position.
+     *
+     * @param pos The absolute position to read from
+     * @return One signed short
+     * @throws IOException If EOF is reached
+     */
+    public final short readTTFShort(long pos) throws IOException {
+        final long cp = getCurrentPos();
+        seekSet(pos);
+        final short ret = readTTFShort();
+        seekSet(cp);
+        return ret;
+    }
+
+    /**
+     * Read 2 bytes unsigned at position pos without changing current position.
+     *
+     * @param pos The absolute position to read from
+     * @return One unsigned short
+     * @throws IOException If EOF is reached
+     */
+    public final int readTTFUShort(long pos) throws IOException {
+        long cp = getCurrentPos();
+        seekSet(pos);
+        int ret = readTTFUShort();
+        seekSet(cp);
+        return ret;
+    }
+
+    /**
+     * Read 4 bytes.
+     *
+     * @return One signed integer
+     * @throws IOException If EOF is reached
+     */
+    public final int readTTFLong() throws IOException {
+        long ret = readTTFUByte();    // << 8;
+        ret = (ret << 8) + readTTFUByte();
+        ret = (ret << 8) + readTTFUByte();
+        ret = (ret << 8) + readTTFUByte();
+
+        return (int)ret;
+    }
+
+    /**
+     * Read 4 bytes.
+     *
+     * @return One unsigned integer
+     * @throws IOException If EOF is reached
+     */
+    public final long readTTFULong() throws IOException {
+        long ret = readTTFUByte();
+        ret = (ret << 8) + readTTFUByte();
+        ret = (ret << 8) + readTTFUByte();
+        ret = (ret << 8) + readTTFUByte();
+
+        return ret;
+    }
+
+    /**
+     * Read a NUL terminated ISO-8859-1 string.
+     *
+     * @return A String
+     * @throws IOException If EOF is reached
+     */
+    public final String readTTFString() throws IOException {
+        int i = current;
+        while (file[i++] != 0) {
+            if (i > fsize) {
+                throw new java.io.EOFException("Reached EOF, file size="
+                                               + fsize);
+            }
+        }
+
+        byte[] tmp = new byte[i - current];
+        System.arraycopy(file, current, tmp, 0, i - current);
+        return new String(tmp, "ISO-8859-1");
+    }
+
+
+    /**
+     * Read an ISO-8859-1 string of len bytes.
+     *
+     * @param len The length of the string to read
+     * @return A String
+     * @throws IOException If EOF is reached
+     */
+    public final String readTTFString(int len) throws IOException {
+        if ((len + current) > fsize) {
+            throw new java.io.EOFException("Reached EOF, file size=" + fsize);
+        }
+
+        byte[] tmp = new byte[len];
+        System.arraycopy(file, current, tmp, 0, len);
+        current += len;
+        return new String(tmp, "ISO-8859-1");
+    }
+
+    /**
+     * Return a copy of the internal array
+     *
+     * @param offset The absolute offset to start reading from
+     * @param length The number of bytes to read
+     * @return An array of bytes
+     * @throws IOException if out of bounds
+     */
+    public byte[] getBytes(int offset,
+                           int length) throws IOException {
+        if ((offset + length) > fsize) {
+            throw new java.io.IOException("Reached EOF");
+        }
+
+        byte[] ret = new byte[length];
+        System.arraycopy(file, offset, ret, 0, length);
+        return ret;
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/org/apache/fop/fonts/truetype/TTFCmapEntry.java b/src/org/apache/fop/fonts/truetype/TTFCmapEntry.java
new file mode 100644 (file)
index 0000000..cda19c1
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+/**
+ * The CMap entry contains information of a Unicode range and the
+ * the glyph indexes related to the range
+ */
+public class TTFCmapEntry {
+
+    private int unicodeStart;
+    private int unicodeEnd;
+    private int glyphStartIndex;
+
+    TTFCmapEntry() {
+        unicodeStart = 0;
+        unicodeEnd = 0;
+        glyphStartIndex = 0;
+    }
+
+    TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+        this.unicodeStart = unicodeStart;
+        this.unicodeEnd = unicodeEnd;
+        this.glyphStartIndex = glyphStartIndex;
+    }
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals(Object o) {
+        if (o instanceof TTFCmapEntry) {
+            TTFCmapEntry ce = (TTFCmapEntry)o;
+            if (ce.unicodeStart == this.unicodeStart
+                    && ce.unicodeEnd == this.unicodeEnd
+                    && ce.glyphStartIndex == this.glyphStartIndex) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the glyphStartIndex.
+     * @return int
+     */
+    public int getGlyphStartIndex() {
+        return glyphStartIndex;
+    }
+
+    /**
+     * Returns the unicodeEnd.
+     * @return int
+     */
+    public int getUnicodeEnd() {
+        return unicodeEnd;
+    }
+
+    /**
+     * Returns the unicodeStart.
+     * @return int
+     */
+    public int getUnicodeStart() {
+        return unicodeStart;
+    }
+
+    /**
+     * Sets the glyphStartIndex.
+     * @param glyphStartIndex The glyphStartIndex to set
+     */
+    public void setGlyphStartIndex(int glyphStartIndex) {
+        this.glyphStartIndex = glyphStartIndex;
+    }
+
+    /**
+     * Sets the unicodeEnd.
+     * @param unicodeEnd The unicodeEnd to set
+     */
+    public void setUnicodeEnd(int unicodeEnd) {
+        this.unicodeEnd = unicodeEnd;
+    }
+
+    /**
+     * Sets the unicodeStart.
+     * @param unicodeStart The unicodeStart to set
+     */
+    public void setUnicodeStart(int unicodeStart) {
+        this.unicodeStart = unicodeStart;
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/truetype/TTFDirTabEntry.java b/src/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
new file mode 100644 (file)
index 0000000..74db027
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+
+
+/**
+ * This class represents an entry to a TrueType font's Dir Tab.
+ */
+class TTFDirTabEntry {
+
+    private byte[] tag = new byte[4];
+    private int checksum;
+    private long offset;
+    private long length;
+
+    /**
+     * Read Dir Tab, return tag name
+     */
+    public String read(FontFileReader in) throws IOException {
+        tag[0] = in.readTTFByte();
+        tag[1] = in.readTTFByte();
+        tag[2] = in.readTTFByte();
+        tag[3] = in.readTTFByte();
+
+        in.skip(4);    // Skip checksum
+
+        offset = in.readTTFULong();
+        length = in.readTTFULong();
+
+        //System.out.println(this.toString());
+        return new String(tag, "ISO-8859-1");
+    }
+
+
+    public String toString() {
+        return "Read dir tab ["
+            + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]"
+            + " offset: " + offset
+            + " length: " + length
+            + " name: " + tag;
+    }
+
+    /**
+     * Returns the checksum.
+     * @return int
+     */
+    public int getChecksum() {
+        return checksum;
+    }
+
+    /**
+     * Returns the length.
+     * @return long
+     */
+    public long getLength() {
+        return length;
+    }
+
+    /**
+     * Returns the offset.
+     * @return long
+     */
+    public long getOffset() {
+        return offset;
+    }
+
+    /**
+     * Returns the tag.
+     * @return byte[]
+     */
+    public byte[] getTag() {
+        return tag;
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/truetype/TTFFile.java b/src/org/apache/fop/fonts/truetype/TTFFile.java
new file mode 100644 (file)
index 0000000..c62b52b
--- /dev/null
@@ -0,0 +1,1249 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.ConsoleLogger;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.fop.fonts.Glyphs;
+
+/**
+ * Reads a TrueType file or a TrueType Collection.
+ * The TrueType spec can be found at the Microsoft.
+ * Typography site: http://www.microsoft.com/truetype/
+ */
+public class TTFFile extends AbstractLogEnabled {
+
+    static final byte NTABS = 24;
+    static final int NMACGLYPHS = 258;
+    static final int MAX_CHAR_CODE = 255;
+    static final int ENC_BUF_SIZE = 1024;
+
+    private String encoding = "WinAnsiEncoding";    // Default encoding
+
+    private short firstChar = 0;
+    private boolean isEmbeddable = true;
+    private boolean hasSerifs = true;
+    /**
+     * Table directory
+     */
+    protected Map dirTabs;
+    private Map kerningTab;                          // for CIDs
+    private Map ansiKerningTab;                      // For winAnsiEncoding
+    private List cmaps;
+    private List unicodeMapping;
+
+    private int upem;                                // unitsPerEm from "head" table
+    private int nhmtx;                               // Number of horizontal metrics
+    private int postFormat;
+    private int locaFormat;
+    /**
+     * Offset to last loca
+     */
+    protected long lastLoca = 0;
+    private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
+    private int nmGlyphs;                            // Used in fixWidths - remove?
+
+    /**
+     * Contains glyph data
+     */
+    protected TTFMtxEntry mtxTab[];                  // Contains glyph data
+    private int[] mtxEncoded = null;
+
+    private String fontName = "";
+    private String fullName = "";
+    private String notice = "";
+    private String familyName = "";
+    private String subFamilyName = "";
+
+    private long italicAngle = 0;
+    private long isFixedPitch = 0;
+    private int fontBBox1 = 0;
+    private int fontBBox2 = 0;
+    private int fontBBox3 = 0;
+    private int fontBBox4 = 0;
+    private int capHeight = 0;
+    private int underlinePosition = 0;
+    private int underlineThickness = 0;
+    private int xHeight = 0;
+    private int ascender = 0;
+    private int descender = 0;
+
+    private short lastChar = 0;
+
+    private int ansiWidth[];
+    private Map ansiIndex;
+
+    /**
+     * Position inputstream to position indicated
+     * in the dirtab offset + offset
+     */
+    void seekTab(FontFileReader in, String name,
+                  long offset) throws IOException {
+        TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
+        if (dt == null) {
+            getLogger().error("Dirtab " + name + " not found.");
+            return;
+        }
+
+        in.seekSet(dt.getOffset() + offset);
+    }
+
+    /**
+     * Convert from truetype unit to pdf unit based on the
+     * unitsPerEm field in the "head" table
+     * @param n truetype unit
+     * @return pdf unit
+     */
+    public int convertTTFUnit2PDFUnit(int n) {
+        int ret;
+        if (n < 0) {
+            long rest1 = n % upem;
+            long storrest = 1000 * rest1;
+            long ledd2 = rest1 / storrest;
+            ret = -((-1000 * n) / upem - (int)ledd2);
+        } else {
+            ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
+        }
+
+        return ret;
+    }
+
+    /**
+     * Read the cmap table,
+     * return false if the table is not present or only unsupported
+     * tables are present. Currently only unicode cmaps are supported.
+     * Set the unicodeIndex in the TTFMtxEntries and fills in the
+     * cmaps vector.
+     */
+    private boolean readCMAP(FontFileReader in) throws IOException {
+
+        unicodeMapping = new java.util.ArrayList();
+
+        //Read CMAP table and correct mtxTab.index
+        int mtxPtr = 0;
+
+        seekTab(in, "cmap", 2);
+        int numCMap = in.readTTFUShort();    // Number of cmap subtables
+        long cmapUniOffset = 0;
+
+        getLogger().info(numCMap + " cmap tables");
+
+        //Read offset for all tables. We are only interested in the unicode table
+        for (int i = 0; i < numCMap; i++) {
+            int cmapPID = in.readTTFUShort();
+            int cmapEID = in.readTTFUShort();
+            long cmapOffset = in.readTTFULong();
+
+            getLogger().debug("Platform ID: " + cmapPID
+                + " Encoding: " + cmapEID);
+
+            if (cmapPID == 3 && cmapEID == 1) {
+                cmapUniOffset = cmapOffset;
+            }
+        }
+
+        if (cmapUniOffset <= 0) {
+            getLogger().fatalError("Unicode cmap table not present");
+            getLogger().fatalError("Unsupported format: Aborting");
+            return false;
+        }
+
+        // Read unicode cmap
+        seekTab(in, "cmap", cmapUniOffset);
+        int cmapFormat = in.readTTFUShort();
+        /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
+
+        getLogger().info("CMAP format: " + cmapFormat);
+        if (cmapFormat == 4) {
+            in.skip(2);    // Skip version number
+            int cmapSegCountX2 = in.readTTFUShort();
+            int cmapSearchRange = in.readTTFUShort();
+            int cmapEntrySelector = in.readTTFUShort();
+            int cmapRangeShift = in.readTTFUShort();
+
+            getLogger().debug("segCountX2   : " + cmapSegCountX2);
+            getLogger().debug("searchRange  : " + cmapSearchRange);
+            getLogger().debug("entrySelector: " + cmapEntrySelector);
+            getLogger().debug("rangeShift   : " + cmapRangeShift);
+
+
+            int cmapEndCounts[] = new int[cmapSegCountX2 / 2];
+            int cmapStartCounts[] = new int[cmapSegCountX2 / 2];
+            int cmapDeltas[] = new int[cmapSegCountX2 / 2];
+            int cmapRangeOffsets[] = new int[cmapSegCountX2 / 2];
+
+            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+                cmapEndCounts[i] = in.readTTFUShort();
+            }
+
+            in.skip(2);    // Skip reservedPad
+
+            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+                cmapStartCounts[i] = in.readTTFUShort();
+            }
+
+            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+                cmapDeltas[i] = in.readTTFShort();
+            }
+
+            //int startRangeOffset = in.getCurrentPos();
+
+            for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+                cmapRangeOffsets[i] = in.readTTFUShort();
+            }
+
+            int glyphIdArrayOffset = in.getCurrentPos();
+
+            // Insert the unicode id for the glyphs in mtxTab
+            // and fill in the cmaps ArrayList
+
+            for (int i = 0; i < cmapStartCounts.length; i++) {
+
+                getLogger().debug(i + ": " + cmapStartCounts[i]
+                    + " - " + cmapEndCounts[i]);
+
+                for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
+
+                    // Update lastChar
+                    if (j < 256 && j > lastChar) {
+                        lastChar = (short)j;
+                    }
+
+                    if (mtxPtr < mtxTab.length) {
+                        int glyphIdx;
+                        // the last character 65535 = .notdef
+                        // may have a range offset
+                        if (cmapRangeOffsets[i] != 0 && j != 65535) {
+                            int glyphOffset = glyphIdArrayOffset
+                                + ((cmapRangeOffsets[i] / 2)
+                                    + (j - cmapStartCounts[i])
+                                    + (i)
+                                    - cmapSegCountX2 / 2) * 2;
+                            in.seekSet(glyphOffset);
+                            glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+                                       & 0xffff;
+
+                            unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
+                            mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+
+
+                            // Also add winAnsiWidth
+                            List v = (List)ansiIndex.get(new Integer(j));
+                            if (v != null) {
+                                Iterator e = v.listIterator();
+                                while (e.hasNext()) {
+                                    Integer aIdx = (Integer)e.next();
+                                    ansiWidth[aIdx.intValue()] =
+                                        mtxTab[glyphIdx].getWx();
+
+                                    getLogger().debug("Added width "
+                                        + mtxTab[glyphIdx].getWx()
+                                        + " uni: " + j
+                                        + " ansi: " + aIdx.intValue());
+                                }
+                            }
+
+                            getLogger().debug("Idx: "
+                                + glyphIdx
+                                + " Delta: " + cmapDeltas[i]
+                                + " Unicode: " + j
+                                + " name: " + mtxTab[glyphIdx].getName());
+                        } else {
+                            glyphIdx = (j + cmapDeltas[i]) & 0xffff;
+
+                            if (glyphIdx < mtxTab.length) {
+                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+                            } else {
+                                getLogger().debug("Glyph " + glyphIdx
+                                                   + " out of range: "
+                                                   + mtxTab.length);
+                            }
+
+                            unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
+                            if (glyphIdx < mtxTab.length) {
+                                mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+                            } else {
+                                getLogger().debug("Glyph " + glyphIdx
+                                                   + " out of range: "
+                                                   + mtxTab.length);
+                            }
+
+                            // Also add winAnsiWidth
+                            List v = (List)ansiIndex.get(new Integer(j));
+                            if (v != null) {
+                                Iterator e = v.listIterator();
+                                while (e.hasNext()) {
+                                    Integer aIdx = (Integer)e.next();
+                                    ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
+                                }
+                            }
+
+                            //getLogger().debug("IIdx: " +
+                            //    mtxPtr +
+                            //    " Delta: " + cmap_deltas[i] +
+                            //    " Unicode: " + j +
+                            //    " name: " +
+                            //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
+
+                        }
+                        if (glyphIdx < mtxTab.length) {
+                            if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
+                                mtxPtr++;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Print first char/last char
+     */
+    private void printMaxMin() {
+        int min = 255;
+        int max = 0;
+        for (int i = 0; i < mtxTab.length; i++) {
+            if (mtxTab[i].getIndex() < min) {
+                min = mtxTab[i].getIndex();
+            }
+            if (mtxTab[i].getIndex() > max) {
+                max = mtxTab[i].getIndex();
+            }
+        }
+        getLogger().info("Min: " + min);
+        getLogger().info("Max: " + max);
+    }
+
+
+    /**
+     * Reads the font using a FontFileReader.
+     *
+     * @param in The FontFileReader to use
+     * @throws IOException In case of an I/O problem
+     */
+    public void readFont(FontFileReader in) throws IOException {
+        readFont(in, (String)null);
+    }
+
+    /**
+     * initialize the ansiWidths array (for winAnsiEncoding)
+     * and fill with the missingwidth
+     */
+    private void initAnsiWidths() {
+        ansiWidth = new int[256];
+        for (int i = 0; i < 256; i++) {
+            ansiWidth[i] = mtxTab[0].getWx();
+        }
+
+        // Create an index hash to the ansiWidth
+        // Can't just index the winAnsiEncoding when inserting widths
+        // same char (eg bullet) is repeated more than one place
+        ansiIndex = new java.util.HashMap();
+        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
+            Integer ansi = new Integer(i);
+            Integer uni = new Integer((int)Glyphs.WINANSI_ENCODING[i]);
+
+            List v = (List)ansiIndex.get(uni);
+            if (v == null) {
+                v = new java.util.ArrayList();
+                ansiIndex.put(uni, v);
+            }
+            v.add(ansi);
+        }
+    }
+
+    /**
+     * Read the font data.
+     * If the fontfile is a TrueType Collection (.ttc file)
+     * the name of the font to read data for must be supplied,
+     * else the name is ignored.
+     *
+     * @param in The FontFileReader to use
+     * @param name The name of the font
+     * @return boolean Returns true if the font is valid
+     * @throws IOException In case of an I/O problem
+     */
+    public boolean readFont(FontFileReader in, String name) throws IOException {
+
+        /*
+         * Check if TrueType collection, and that the name
+         * exists in the collection
+         */
+        if (!checkTTC(in, name)) {
+            throw new IOException("Failed to read font");
+        }
+
+        readDirTabs(in);
+        readFontHeader(in);
+        getNumGlyphs(in);
+        getLogger().info("Number of glyphs in font: " + numberOfGlyphs);
+        readHorizontalHeader(in);
+        readHorizontalMetrics(in);
+        initAnsiWidths();
+        readPostscript(in);
+        readOS2(in);
+        readIndexToLocation(in);
+        readGlyf(in);
+        readName(in);
+        readPCLT(in);
+        // Read cmap table and fill in ansiwidths
+        boolean valid = readCMAP(in);
+        if (!valid) {
+            return false;
+        }
+        // Create cmaps for bfentries
+        createCMaps();
+        // print_max_min();
+
+        readKerning(in);
+        return true;
+    }
+
+    private void createCMaps() {
+        cmaps = new java.util.ArrayList();
+        TTFCmapEntry tce = new TTFCmapEntry();
+
+        Iterator e = unicodeMapping.listIterator();
+        UnicodeMapping um = (UnicodeMapping)e.next();
+        UnicodeMapping lastMapping = um;
+
+        tce.setUnicodeStart(um.getUIdx());
+        tce.setGlyphStartIndex(um.getGIdx());
+
+        while (e.hasNext()) {
+            um = (UnicodeMapping)e.next();
+            if (((lastMapping.getUIdx() + 1) != um.getUIdx())
+                    || ((lastMapping.getGIdx() + 1) != um.getGIdx())) {
+                tce.setUnicodeEnd(lastMapping.getUIdx());
+                cmaps.add(tce);
+
+                tce = new TTFCmapEntry();
+                tce.setUnicodeStart(um.getUIdx());
+                tce.setGlyphStartIndex(um.getGIdx());
+            }
+            lastMapping = um;
+        }
+
+        tce.setUnicodeEnd(um.getUIdx());
+        cmaps.add(tce);
+    }
+
+    /**
+     * Returns the Windows name of the font.
+     * @return String The Windows name
+     */
+    public String getWindowsName() {
+        return familyName + "," + subFamilyName;
+    }
+
+    /**
+     * Returns the PostScript name of the font.
+     * @return String The PostScript name
+     */
+    public String getPostscriptName() {
+        if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
+            return familyName;
+        } else {
+            return familyName + "," + subFamilyName;
+        }
+    }
+
+    /**
+     * Returns the font family name of the font.
+     * @return String The family name
+     */
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    /**
+     * Returns the name of the character set used.
+     * @return String The caracter set
+     */
+    public String getCharSetName() {
+        return encoding;
+    }
+
+    /**
+     * Returns the CapHeight attribute of the font.
+     * @return int The CapHeight
+     */
+    public int getCapHeight() {
+        return (int)convertTTFUnit2PDFUnit(capHeight);
+    }
+
+    /**
+     * Returns the XHeight attribute of the font.
+     * @return int The XHeight
+     */
+    public int getXHeight() {
+        return (int)convertTTFUnit2PDFUnit(xHeight);
+    }
+
+    /**
+     * Returns the Flags attribute of the font.
+     * @return int The Flags
+     */
+    public int getFlags() {
+        int flags = 32;    // Use Adobe Standard charset
+        if (italicAngle != 0) {
+            flags = flags | 64;
+        }
+        if (isFixedPitch != 0) {
+            flags = flags | 2;
+        }
+        if (hasSerifs) {
+            flags = flags | 1;
+        }
+        return flags;
+    }
+
+
+    /**
+     * Returns the StemV attribute of the font.
+     * @return String The StemV
+     */
+    public String getStemV() {
+        return "0";
+    }
+
+    /**
+     * Returns the ItalicAngle attribute of the font.
+     * @return String The ItalicAngle
+     */
+    public String getItalicAngle() {
+        String ia = Short.toString((short)(italicAngle / 0x10000));
+
+        // This is the correct italic angle, however only int italic
+        // angles are supported at the moment so this is commented out.
+        /*
+         * if ((italicAngle % 0x10000) > 0 )
+         * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
+         */
+        return ia;
+    }
+
+    /**
+     * Returns the font bounding box.
+     * @return int[] The font bbox
+     */
+    public int[] getFontBBox() {
+        final int[] fbb = new int[4];
+        fbb[0] = (int)convertTTFUnit2PDFUnit(fontBBox1);
+        fbb[1] = (int)convertTTFUnit2PDFUnit(fontBBox2);
+        fbb[2] = (int)convertTTFUnit2PDFUnit(fontBBox3);
+        fbb[3] = (int)convertTTFUnit2PDFUnit(fontBBox4);
+
+        return fbb;
+    }
+
+    /**
+     * Returns the LowerCaseAscent attribute of the font.
+     * @return int The LowerCaseAscent
+     */
+    public int getLowerCaseAscent() {
+        return (int)convertTTFUnit2PDFUnit(ascender);
+    }
+
+    /**
+     * Returns the LowerCaseDescent attribute of the font.
+     * @return int The LowerCaseDescent
+     */
+    public int getLowerCaseDescent() {
+        return (int)convertTTFUnit2PDFUnit(descender);
+    }
+
+    /**
+     * Returns the index of the last character, but this is for WinAnsiEncoding
+     * only, so the last char is < 256.
+     * @return short Index of the last character (<256)
+     */
+    public short getLastChar() {
+        return lastChar;
+    }
+
+    /**
+     * Returns the index of the first character.
+     * @return short Index of the first character
+     */
+    public short getFirstChar() {
+        return firstChar;
+    }
+
+    /**
+     * Returns an array of character widths.
+     * @return int[] The character widths
+     */
+    public int[] getWidths() {
+        int[] wx = new int[mtxTab.length];
+        for (int i = 0; i < wx.length; i++) {
+            wx[i] = (int)convertTTFUnit2PDFUnit(mtxTab[i].getWx());
+        }
+
+        return wx;
+    }
+
+    /**
+     * Returns the width of a given character.
+     * @param idx Index of the character
+     * @return int Standard width
+     */
+    public int getCharWidth(int idx) {
+        return (int)convertTTFUnit2PDFUnit(ansiWidth[idx]);
+    }
+
+    /**
+     * Returns the kerning table.
+     * @return Map The kerning table
+     */
+    public Map getKerning() {
+        return kerningTab;
+    }
+
+    /**
+     * Returns the ANSI kerning table.
+     * @return Map The ANSI kerning table
+     */
+    public Map getAnsiKerning() {
+        return ansiKerningTab;
+    }
+
+    /**
+     * Indicates if the font may be embedded.
+     * @return boolean True if it may be embedded
+     */
+    public boolean isEmbeddable() {
+        return isEmbeddable;
+    }
+
+
+    /**
+     * Read Table Directory from the current position in the
+     * FontFileReader and fill the global HashMap dirTabs
+     * with the table name (String) as key and a TTFDirTabEntry
+     * as value.
+     * @param in FontFileReader to read the table directory from
+     * @throws IOException in case of an I/O problem
+     */
+    protected void readDirTabs(FontFileReader in) throws IOException {
+        in.skip(4);    // TTF_FIXED_SIZE
+        int ntabs = in.readTTFUShort();
+        in.skip(6);    // 3xTTF_USHORT_SIZE
+
+        dirTabs = new java.util.HashMap();
+        TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
+        getLogger().debug("Reading " + ntabs + " dir tables");
+        for (int i = 0; i < ntabs; i++) {
+            pd[i] = new TTFDirTabEntry();
+            dirTabs.put(pd[i].read(in), pd[i]);
+        }
+    }
+
+    /**
+     * Read the "head" table, this reads the bounding box and
+     * sets the upem (unitsPerEM) variable
+     * @param in FontFileReader to read the header from
+     * @throws IOException in case of an I/O problem
+     */
+    protected void readFontHeader(FontFileReader in) throws IOException {
+        seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
+        upem = in.readTTFUShort();
+
+        in.skip(16);
+
+        fontBBox1 = in.readTTFShort();
+        fontBBox2 = in.readTTFShort();
+        fontBBox3 = in.readTTFShort();
+        fontBBox4 = in.readTTFShort();
+
+        in.skip(2 + 2 + 2);
+
+        locaFormat = in.readTTFShort();
+    }
+
+    /**
+     * Read the number of glyphs from the "maxp" table
+     * @param in FontFileReader to read the number of glyphs from
+     * @throws IOException in case of an I/O problem
+     */
+    protected void getNumGlyphs(FontFileReader in) throws IOException {
+        seekTab(in, "maxp", 4);
+        numberOfGlyphs = in.readTTFUShort();
+    }
+
+
+    /**
+     * Read the "hhea" table to find the ascender and descender and
+     * size of "hmtx" table, i.e. a fixed size font might have only
+     * one width
+     * @param in FontFileReader to read the hhea table from
+     * @throws IOException in case of an I/O problem
+     */
+    protected void readHorizontalHeader(FontFileReader in)
+            throws IOException {
+        seekTab(in, "hhea", 4);
+        ascender = in.readTTFShort();    // Use sTypoAscender in "OS/2" table?
+        descender = in.readTTFShort();    // Use sTypoDescender in "OS/2" table?
+
+        in.skip(2 + 2 + 3 * 2 + 8 * 2);
+        nhmtx = in.readTTFUShort();
+        getLogger().debug("Number of horizontal metrics: " + nhmtx);
+    }
+
+    /**
+     * Read "hmtx" table and put the horizontal metrics
+     * in the mtxTab array. If the number of metrics is less
+     * than the number of glyphs (eg fixed size fonts), extend
+     * the mtxTab array and fill in the missing widths
+     * @param in FontFileReader to read the hmtx table from
+     * @throws IOException in case of an I/O problem
+     */
+    protected void readHorizontalMetrics(FontFileReader in)
+            throws IOException {
+        seekTab(in, "hmtx", 0);
+
+        int mtxSize = (numberOfGlyphs > nhmtx) ? numberOfGlyphs : nhmtx;
+        mtxTab = new TTFMtxEntry[mtxSize];
+
+        getLogger().debug("*** Widths array: \n");
+        for (int i = 0; i < mtxSize; i++) {
+            mtxTab[i] = new TTFMtxEntry();
+        }
+        for (int i = 0; i < nhmtx; i++) {
+            mtxTab[i].setWx(in.readTTFUShort());
+            mtxTab[i].setLsb(in.readTTFUShort());
+
+            getLogger().debug("   width[" + i + "] = "
+                + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
+        }
+
+        if (nhmtx < mtxSize) {
+            // Fill in the missing widths
+            int lastWidth = mtxTab[nhmtx - 1].getWx();
+            for (int i = nhmtx; i < mtxSize; i++) {
+                mtxTab[i].setWx(lastWidth);
+                mtxTab[i].setLsb(in.readTTFUShort());
+            }
+        }
+    }
+
+
+    /**
+     * Read the "post" table
+     * containing the postscript names of the glyphs.
+     */
+    private final void readPostscript(FontFileReader in) throws IOException {
+        String[] psGlyphsBuffer;
+        int i, k, l;
+
+        seekTab(in, "post", 0);
+        postFormat = in.readTTFLong();
+        italicAngle = in.readTTFULong();
+        underlinePosition = in.readTTFShort();
+        underlineThickness = in.readTTFShort();
+        isFixedPitch = in.readTTFULong();
+
+        in.skip(4 * 4);
+
+        getLogger().debug("Post format: " + postFormat);
+        switch (postFormat) {
+        case 0x00010000:
+            getLogger().debug("Postscript format 1");
+            for (i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
+                mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
+            }
+            break;
+        case 0x00020000:
+            getLogger().debug("Postscript format 2");
+            int numGlyphStrings = 0;
+            l = in.readTTFUShort();      // Num Glyphs
+            // short minIndex=256;
+            for (i = 0; i < l; i++) {    // Read indexes
+                mtxTab[i].setIndex(in.readTTFUShort());
+                // if (minIndex > mtxTab[i].index)
+                // minIndex=(short)mtxTab[i].index;
+
+                if (mtxTab[i].getIndex() > 257) {
+                    numGlyphStrings++;
+                }
+
+                getLogger().debug("Post index: " + mtxTab[i].getIndex());
+            }
+            // firstChar=minIndex;
+            psGlyphsBuffer = new String[numGlyphStrings];
+            getLogger().debug("Reading " + numGlyphStrings
+                + " glyphnames" + ", was n num glyphs=" + l);
+            for (i = 0; i < psGlyphsBuffer.length; i++) {
+                psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
+            }
+
+            for (i = 0; i < l; i++) {
+                if (mtxTab[i].getIndex() < NMACGLYPHS) {
+                    mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
+                } else {
+                    k = mtxTab[i].getIndex() - NMACGLYPHS;
+
+                    getLogger().debug(k + " i=" + i + " mtx=" + mtxTab.length
+                        + " ps=" + psGlyphsBuffer.length);
+
+                    mtxTab[i].setName(psGlyphsBuffer[k]);
+                }
+            }
+
+            break;
+        case 0x00030000:
+            // Postscript format 3 contains no glyph names
+            getLogger().debug("Postscript format 3");
+            break;
+        default:
+            getLogger().error("Unknown Postscript format: " + postFormat);
+        }
+    }
+
+
+    /**
+     * Read the "OS/2" table
+     */
+    private final void readOS2(FontFileReader in) throws IOException {
+        // Check if font is embeddable
+        if (dirTabs.get("OS/2") != null) {
+            seekTab(in, "OS/2", 2 * 4);
+            int fsType = in.readTTFUShort();
+            if (fsType == 2) {
+                isEmbeddable = false;
+            } else {
+                isEmbeddable = true;
+            }
+        } else {
+            isEmbeddable = true;
+        }
+    }
+
+    /**
+     * Read the "loca" table.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    protected final void readIndexToLocation(FontFileReader in)
+            throws IOException {
+        seekTab(in, "loca", 0);
+        for (int i = 0; i < numberOfGlyphs; i++) {
+            mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
+                                 : (in.readTTFUShort() << 1));
+        }
+        lastLoca = (locaFormat == 1 ? in.readTTFULong()
+                    : (in.readTTFUShort() << 1));
+    }
+
+    /**
+     * Read the "glyf" table to find the bounding boxes.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private final void readGlyf(FontFileReader in) throws IOException {
+        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
+        for (int i = 0; i < (numberOfGlyphs - 1); i++) {
+            if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+                in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+                in.skip(2);
+                final int[] bbox = {
+                    in.readTTFShort(),
+                    in.readTTFShort(),
+                    in.readTTFShort(),
+                    in.readTTFShort()};
+                mtxTab[i].setBoundingBox(bbox);
+            } else {
+                mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
+            }
+        }
+
+
+        long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
+        for (int i = 0; i < numberOfGlyphs; i++) {
+            if ((i + 1) >= mtxTab.length
+                    || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+                in.seekSet(n + mtxTab[i].getOffset());
+                in.skip(2);
+                final int[] bbox = {
+                    in.readTTFShort(),
+                    in.readTTFShort(),
+                    in.readTTFShort(),
+                    in.readTTFShort()};
+                mtxTab[i].setBoundingBox(bbox);
+            } else {
+                /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
+                final int bbox0 = mtxTab[0].getBoundingBox()[0];
+                final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
+                mtxTab[i].setBoundingBox(bbox);
+                /* Original code
+                mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
+                mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
+                mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
+                mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
+            }
+            getLogger().debug(mtxTab[i].toString(this));
+        }
+    }
+
+    /**
+     * Read the "name" table.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private final void readName(FontFileReader in) throws IOException {
+        seekTab(in, "name", 2);
+        int i = in.getCurrentPos();
+        int n = in.readTTFUShort();
+        int j = in.readTTFUShort() + i - 2;
+        i += 2 * 2;
+
+        while (n-- > 0) {
+            // getLogger().debug("Iteration: " + n);
+            in.seekSet(i);
+            final int platformID = in.readTTFUShort();
+            final int encodingID = in.readTTFUShort();
+            /*final int language_id =*/ in.readTTFUShort(); //Skip language id
+
+            int k = in.readTTFUShort();
+            int l = in.readTTFUShort();
+
+            if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))
+                    && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
+                // if (k==1 || k==2 || k==0 || k==4 || k==6) {
+                in.seekSet(j + in.readTTFUShort());
+                String txt = in.readTTFString(l);
+                // getLogger().debug(platform_id + " " + encoding_id
+                //     + " " + k + " " + txt);
+                switch (k) {
+                case 0:
+                    notice = txt;
+                    break;
+                case 1:
+                    familyName = txt;
+                    break;
+                case 2:
+                    subFamilyName = txt;
+                    break;
+                case 4:
+                    fullName = txt;
+                    break;
+                case 6:
+                    fontName = txt;
+                    break;
+                }
+                if (!notice.equals("")
+                        && !fullName.equals("")
+                        && !fontName.equals("")
+                        && !familyName.equals("")
+                        && !subFamilyName.equals("")) {
+                    break;
+                }
+            }
+            i += 6 * 2;
+        }
+    }
+
+    /**
+     * Read the "PCLT" table to find xHeight and capHeight.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private final void readPCLT(FontFileReader in) throws IOException {
+        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
+        if (dirTab != null) {
+            in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+            xHeight = in.readTTFUShort();
+            in.skip(2 * 2);
+            capHeight = in.readTTFUShort();
+            in.skip(2 + 16 + 8 + 6 + 1 + 1);
+
+            int serifStyle = in.readTTFUByte();
+            serifStyle = serifStyle >> 6;
+            serifStyle = serifStyle & 3;
+            if (serifStyle == 1) {
+                hasSerifs = false;
+            } else {
+                hasSerifs = true;
+            }
+        } else {
+            // Approximate capHeight from height of "H"
+            // It's most unlikly that a font misses the PCLT table
+            // This also assumes that psocriptnames exists ("H")
+            // Should look it up int the cmap (that wouldn't help
+            // for charsets without H anyway...)
+            for (int i = 0; i < mtxTab.length; i++) {
+                if ("H".equals(mtxTab[i].getName())) {
+                    capHeight = mtxTab[i].getBoundingBox()[3] - mtxTab[i].getBoundingBox()[1];
+                }
+            }
+        }
+    }
+
+    /**
+     * Read the kerning table, create a table for both CIDs and
+     * winAnsiEncoding.
+     * @param in FontFileReader to read from
+     * @throws IOException In case of a I/O problem
+     */
+    private final void readKerning(FontFileReader in) throws IOException {
+        // Read kerning
+        kerningTab = new java.util.HashMap();
+        ansiKerningTab = new java.util.HashMap();
+        TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
+        if (dirTab != null) {
+            seekTab(in, "kern", 2);
+            for (int n = in.readTTFUShort(); n > 0; n--) {
+                in.skip(2 * 2);
+                int k = in.readTTFUShort();
+                if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
+                    return;
+                }
+                if ((k >> 8) != 0) {
+                    continue;
+                }
+
+                k = in.readTTFUShort();
+                in.skip(3 * 2);
+                while (k-- > 0) {
+                    int i = in.readTTFUShort();
+                    int j = in.readTTFUShort();
+                    int kpx = in.readTTFShort();
+                    if (kpx != 0) {
+                        // CID table
+                        Integer iObj = new Integer(i);
+                        Map adjTab = (Map)kerningTab.get(iObj);
+                        if (adjTab == null) {
+                            adjTab = new java.util.HashMap();
+                        }
+                        adjTab.put(new Integer(j),
+                                   new Integer((int)convertTTFUnit2PDFUnit(kpx)));
+                        kerningTab.put(iObj, adjTab);
+                    }
+                }
+            }
+            // getLogger().debug(kerningTab.toString());
+
+            // Create winAnsiEncoded kerning table
+            Iterator ae = kerningTab.keySet().iterator();
+            while (ae.hasNext()) {
+                Integer cidKey = (Integer)ae.next();
+                Map akpx = new java.util.HashMap();
+                Map ckpx = (Map)kerningTab.get(cidKey);
+
+                Iterator aee = ckpx.keySet().iterator();
+                while (aee.hasNext()) {
+                    Integer cidKey2 = (Integer)aee.next();
+                    Integer kern = (Integer)ckpx.get(cidKey2);
+
+                    Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
+                    while (uniMap.hasNext()) {
+                        Integer unicodeKey = (Integer)uniMap.next();
+                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+                        for (int u = 0; u < ansiKeys.length; u++) {
+                            akpx.put(ansiKeys[u], kern);
+                        }
+                    }
+                }
+
+                if (akpx.size() > 0) {
+                    Iterator uniMap = mtxTab[cidKey.intValue()].getUnicodeIndex().listIterator();
+                    while (uniMap.hasNext()) {
+                        Integer unicodeKey = (Integer)uniMap.next();
+                        Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+                        for (int u = 0; u < ansiKeys.length; u++) {
+                            ansiKerningTab.put(ansiKeys[u], akpx);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Return a List with TTFCmapEntry.
+     * @return A list of TTFCmapEntry objects
+     */
+    public List getCMaps() {
+        return cmaps;
+    }
+
+    /**
+     * Check if this is a TrueType collection and that the given
+     * name exists in the collection.
+     * If it does, set offset in fontfile to the beginning of
+     * the Table Directory for that font.
+     * @param in FontFileReader to read from
+     * @param name The name to check
+     * @return True if not collection or font name present, false otherwise
+     * @throws IOException In case of an I/O problem
+     */
+    protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
+        String tag = in.readTTFString(4);
+
+        if ("ttcf".equals(tag)) {
+            // This is a TrueType Collection
+            in.skip(4);
+
+            // Read directory offsets
+            int numDirectories = (int)in.readTTFULong();
+            // int numDirectories=in.readTTFUShort();
+            long[] dirOffsets = new long[numDirectories];
+            for (int i = 0; i < numDirectories; i++) {
+                dirOffsets[i] = in.readTTFULong();
+            }
+
+            getLogger().debug("This is a TrueType collection file with"
+                                   + numDirectories + " fonts");
+            getLogger().debug("Containing the following fonts: ");
+            // Read all the directories and name tables to check
+            // If the font exists - this is a bit ugly, but...
+            boolean found = false;
+
+            // Iterate through all name tables even if font
+            // Is found, just to show all the names
+            long dirTabOffset = 0;
+            for (int i = 0; (i < numDirectories); i++) {
+                in.seekSet(dirOffsets[i]);
+                readDirTabs(in);
+
+                readName(in);
+
+                if (fullName.equals(name)) {
+                    found = true;
+                    dirTabOffset = dirOffsets[i];
+                    getLogger().debug("* " + fullName);
+                } else {
+                    getLogger().debug(fullName);
+                }
+
+                // Reset names
+                notice = "";
+                fullName = "";
+                familyName = "";
+                fontName = "";
+                subFamilyName = "";
+            }
+
+            in.seekSet(dirTabOffset);
+            return found;
+        } else {
+            in.seekSet(0);
+            return true;
+        }
+    }
+
+    /*
+     * Helper classes, they are not very efficient, but that really
+     * doesn't matter...
+     */
+    private Integer[] unicodeToWinAnsi(int unicode) {
+        List ret = new java.util.ArrayList();
+        for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
+            if (unicode == Glyphs.WINANSI_ENCODING[i]) {
+                ret.add(new Integer(i));
+            }
+        }
+        return (Integer[])ret.toArray(new Integer[0]);
+    }
+
+    /**
+     * Dumps a few informational values to System.out.
+     */
+    public void printStuff() {
+        System.out.println("Font name:   " + fontName);
+        System.out.println("Full name:   " + fullName);
+        System.out.println("Family name: " + familyName);
+        System.out.println("Subfamily name: " + subFamilyName);
+        System.out.println("Notice:      " + notice);
+        System.out.println("xHeight:     " + (int)convertTTFUnit2PDFUnit(xHeight));
+        System.out.println("capheight:   " + (int)convertTTFUnit2PDFUnit(capHeight));
+
+        int italic = (int)(italicAngle >> 16);
+        System.out.println("Italic:      " + italic);
+        System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
+        if ((italicAngle % 0x10000) > 0) {
+            System.out.print("."
+                             + (short)((italicAngle % 0x10000) * 1000)
+                               / 0x10000);
+        }
+        System.out.println();
+        System.out.println("Ascender:    " + convertTTFUnit2PDFUnit(ascender));
+        System.out.println("Descender:   " + convertTTFUnit2PDFUnit(descender));
+        System.out.println("FontBBox:    [" + (int)convertTTFUnit2PDFUnit(fontBBox1)
+                           + " " + (int)convertTTFUnit2PDFUnit(fontBBox2) + " "
+                           + (int)convertTTFUnit2PDFUnit(fontBBox3) + " "
+                           + (int)convertTTFUnit2PDFUnit(fontBBox4) + "]");
+    }
+
+    /**
+     * Static main method to get info about a TrueType font.
+     * @param args The command line arguments
+     */
+    public static void main(String[] args) {
+        int level = ConsoleLogger.LEVEL_WARN;
+        Logger log = new ConsoleLogger(level);
+        try {
+            TTFFile ttfFile = new TTFFile();
+            ttfFile.enableLogging(log);
+
+            FontFileReader reader = new FontFileReader(args[0]);
+
+            String name = null;
+            if (args.length >= 2) {
+                name = args[1];
+            }
+
+            ttfFile.readFont(reader, name);
+            ttfFile.printStuff();
+
+        } catch (IOException ioe) {
+            log.error("Problem reading font: " + ioe.toString(), ioe);
+        }
+    }
+
+}
+
+
+/**
+ * Key-value helper class
+ */
+class UnicodeMapping {
+
+    private int uIdx;
+    private int gIdx;
+
+    UnicodeMapping(int gIdx, int uIdx) {
+        this.uIdx = uIdx;
+        this.gIdx = gIdx;
+    }
+
+    /**
+     * Returns the gIdx.
+     * @return int
+     */
+    public int getGIdx() {
+        return gIdx;
+    }
+
+    /**
+     * Returns the uIdx.
+     * @return int
+     */
+    public int getUIdx() {
+        return uIdx;
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/truetype/TTFMtxEntry.java b/src/org/apache/fop/fonts/truetype/TTFMtxEntry.java
new file mode 100644 (file)
index 0000000..965844c
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.util.List;
+
+/**
+ * This class represents a TrueType Mtx Entry.
+ */
+class TTFMtxEntry {
+
+    private int wx;
+    private int lsb;
+    private String name = "";
+    private int index;
+    private List unicodeIndex = new java.util.ArrayList();
+    private int[] boundingBox = new int[4];
+    private long offset;
+    private byte found = 0;
+
+    /**
+     * Returns a String representation of this object.
+     * 
+     * @param t TTFFile to use for unit conversion
+     * @return String String representation
+     */
+    public String toString(TTFFile t) {
+        return "Glyph " + name + " index: " + index + " bbox [ "
+             + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " "
+             + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " "
+             + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " "
+             + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: "
+             + t.convertTTFUnit2PDFUnit(wx);
+    }
+
+    /**
+     * Returns the boundingBox.
+     * @return int[]
+     */
+    public int[] getBoundingBox() {
+        return boundingBox;
+    }
+
+    /**
+     * Sets the boundingBox.
+     * @param boundingBox The boundingBox to set
+     */
+    public void setBoundingBox(int[] boundingBox) {
+        this.boundingBox = boundingBox;
+    }
+
+    /**
+     * Returns the found.
+     * @return byte
+     */
+    public byte getFound() {
+        return found;
+    }
+
+    /**
+     * Returns the index.
+     * @return int
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * Returns the lsb.
+     * @return int
+     */
+    public int getLsb() {
+        return lsb;
+    }
+
+    /**
+     * Returns the name.
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the offset.
+     * @return long
+     */
+    public long getOffset() {
+        return offset;
+    }
+
+    /**
+     * Returns the unicodeIndex.
+     * @return List
+     */
+    public List getUnicodeIndex() {
+        return unicodeIndex;
+    }
+
+    /**
+     * Returns the wx.
+     * @return int
+     */
+    public int getWx() {
+        return wx;
+    }
+
+    /**
+     * Sets the found.
+     * @param found The found to set
+     */
+    public void setFound(byte found) {
+        this.found = found;
+    }
+
+    /**
+     * Sets the index.
+     * @param index The index to set
+     */
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    /**
+     * Sets the lsb.
+     * @param lsb The lsb to set
+     */
+    public void setLsb(int lsb) {
+        this.lsb = lsb;
+    }
+
+    /**
+     * Sets the name.
+     * @param name The name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Sets the offset.
+     * @param offset The offset to set
+     */
+    public void setOffset(long offset) {
+        this.offset = offset;
+    }
+
+    /**
+     * Sets the wx.
+     * @param wx The wx to set
+     */
+    public void setWx(int wx) {
+        this.wx = wx;
+    }
+
+
+}
diff --git a/src/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/org/apache/fop/fonts/truetype/TTFSubSetFile.java
new file mode 100644 (file)
index 0000000..8014cf4
--- /dev/null
@@ -0,0 +1,836 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+
+
+/**
+ * Reads a TrueType file and generates a subset
+ * that can be used to embed a TrueType CID font.
+ * TrueType tables needed for embedded CID fonts are:
+ * "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm".
+ * The TrueType spec can be found at the Microsoft
+ * Typography site: http://www.microsoft.com/truetype/
+ */
+public class TTFSubSetFile extends TTFFile {
+
+    private byte[] output = null;
+    private int realSize = 0;
+    private int currentPos = 0;
+
+    /*
+     * Offsets in name table to be filled out by table.
+     * The offsets are to the checkSum field
+     */
+    private int cvtDirOffset = 0;
+    private int fpgmDirOffset = 0;
+    private int glyfDirOffset = 0;
+    private int headDirOffset = 0;
+    private int hheaDirOffset = 0;
+    private int hmtxDirOffset = 0;
+    private int locaDirOffset = 0;
+    private int maxpDirOffset = 0;
+    private int prepDirOffset = 0;
+
+    private int checkSumAdjustmentOffset = 0;
+    private int locaOffset = 0;
+
+    /**
+     * Initalize the output array
+     */
+    private void init(int size) {
+        output = new byte[size];
+        realSize = 0;
+        currentPos = 0;
+
+        // createDirectory()
+    }
+
+    /**
+     * Create the directory table
+     */
+    private void createDirectory() {
+        int numTables = 9;
+        // Create the TrueType header
+        writeByte((byte)0);
+        writeByte((byte)1);
+        writeByte((byte)0);
+        writeByte((byte)0);
+        realSize += 4;
+
+        writeUShort(numTables);
+        realSize += 2;
+
+        // Create searchRange, entrySelector and rangeShift
+        int maxPow = maxPow2(numTables);
+        int searchRange = maxPow * 16;
+        writeUShort(searchRange);
+        realSize += 2;
+
+        writeUShort(maxPow);
+        realSize += 2;
+
+        writeUShort((numTables * 16) - searchRange);
+        realSize += 2;
+
+        // Create space for the table entries
+        writeString("cvt ");
+        cvtDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("fpgm");
+        fpgmDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("glyf");
+        glyfDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("head");
+        headDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("hhea");
+        hheaDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("hmtx");
+        hmtxDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("loca");
+        locaDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("maxp");
+        maxpDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+
+        writeString("prep");
+        prepDirOffset = currentPos;
+        currentPos += 12;
+        realSize += 16;
+    }
+
+
+    /**
+     * Copy the cvt table as is from original font to subset font
+     */
+    private void createCvt(FontFileReader in) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "cvt ", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(cvtDirOffset, checksum);
+            writeULong(cvtDirOffset + 4, currentPos);
+            writeULong(cvtDirOffset + 8, (int)entry.getLength());
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find cvt table");
+        }
+    }
+
+
+
+    /**
+     * Copy the fpgm table as is from original font to subset font
+     */
+    private void createFpgm(FontFileReader in) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "fpgm", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(fpgmDirOffset, checksum);
+            writeULong(fpgmDirOffset + 4, currentPos);
+            writeULong(fpgmDirOffset + 8, (int)entry.getLength());
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find fpgm table");
+        }
+    }
+
+
+
+    /**
+     * Create an empty loca table without updating checksum
+     */
+    private void createLoca(int size) throws IOException {
+        pad4();
+        locaOffset = currentPos;
+        writeULong(locaDirOffset + 4, currentPos);
+        writeULong(locaDirOffset + 8, size * 4 + 4);
+        currentPos += size * 4 + 4;
+        realSize += size * 4 + 4;
+    }
+
+
+    /**
+     * Copy the maxp table as is from original font to subset font
+     * and set num glyphs to size
+     */
+    private void createMaxp(FontFileReader in, int size) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "maxp", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+            writeUShort(currentPos + 4, size);
+
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(maxpDirOffset, checksum);
+            writeULong(maxpDirOffset + 4, currentPos);
+            writeULong(maxpDirOffset + 8, (int)entry.getLength());
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find maxp table");
+        }
+    }
+
+
+    /**
+     * Copy the prep table as is from original font to subset font
+     */
+    private void createPrep(FontFileReader in) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "prep", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(prepDirOffset, checksum);
+            writeULong(prepDirOffset + 4, currentPos);
+            writeULong(prepDirOffset + 8, (int)entry.getLength());
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find prep table");
+        }
+    }
+
+
+    /**
+     * Copy the hhea table as is from original font to subset font
+     * and fill in size of hmtx table
+     */
+    private void createHhea(FontFileReader in, int size) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "hhea", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+            writeUShort((int)entry.getLength() + currentPos - 2, size);
+
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(hheaDirOffset, checksum);
+            writeULong(hheaDirOffset + 4, currentPos);
+            writeULong(hheaDirOffset + 8, (int)entry.getLength());
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find hhea table");
+        }
+    }
+
+
+    /**
+     * Copy the head table as is from original font to subset font
+     * and set indexToLocaFormat to long and set
+     * checkSumAdjustment to 0, store offset to checkSumAdjustment
+     * in checkSumAdjustmentOffset
+     */
+    private void createHead(FontFileReader in) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+        if (entry != null) {
+            pad4();
+            seekTab(in, "head", 0);
+            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+                             0, output, currentPos, (int)entry.getLength());
+
+            checkSumAdjustmentOffset = currentPos + 8;
+            output[currentPos + 8] = 0;     // Set checkSumAdjustment to 0
+            output[currentPos + 9] = 0;
+            output[currentPos + 10] = 0;
+            output[currentPos + 11] = 0;
+            output[currentPos + 50] = 0;    // long locaformat
+            output[currentPos + 51] = 1;    // long locaformat
+
+            int checksum = getCheckSum(currentPos, (int)entry.getLength());
+            writeULong(headDirOffset, checksum);
+            writeULong(headDirOffset + 4, currentPos);
+            writeULong(headDirOffset + 8, (int)entry.getLength());
+
+            currentPos += (int)entry.getLength();
+            realSize += (int)entry.getLength();
+        } else {
+            throw new IOException("Can't find head table");
+        }
+    }
+
+
+    /**
+     * Create the glyf table and fill in loca table
+     */
+    private void createGlyf(FontFileReader in,
+                            Map glyphs) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+        int size = 0;
+        int start = 0;
+        int endOffset = 0;    // Store this as the last loca
+        if (entry != null) {
+            pad4();
+            start = currentPos;
+
+            /* Loca table must be in order by glyph index, so build
+             * an array first and then write the glyph info and
+             * location offset.
+             */
+            int[] origIndexes = new int[glyphs.size()];
+
+            Iterator e = glyphs.keySet().iterator();
+            while (e.hasNext()) {
+                Integer origIndex = (Integer)e.next();
+                Integer subsetIndex = (Integer)glyphs.get(origIndex);
+                origIndexes[subsetIndex.intValue()] = origIndex.intValue();
+            }
+
+            for (int i = 0; i < origIndexes.length; i++) {
+                int glyphLength = 0;
+                int nextOffset = 0;
+                int origGlyphIndex = origIndexes[i];
+                if (origGlyphIndex >= (mtxTab.length - 1)) {
+                    nextOffset = (int)lastLoca;
+                } else {
+                    nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
+                }
+                glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
+
+                // Copy glyph
+                System.arraycopy(
+                    in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
+                        glyphLength), 0,
+                    output, currentPos,
+                    glyphLength);
+
+
+                // Update loca table
+                writeULong(locaOffset + i * 4, currentPos - start);
+                if ((currentPos - start + glyphLength) > endOffset) {
+                    endOffset = (currentPos - start + glyphLength);
+                }
+
+                currentPos += glyphLength;
+                realSize += glyphLength;
+
+            }
+
+            size = currentPos - start;
+
+            int checksum = getCheckSum(start, size);
+            writeULong(glyfDirOffset, checksum);
+            writeULong(glyfDirOffset + 4, start);
+            writeULong(glyfDirOffset + 8, size);
+            currentPos += 12;
+            realSize += 12;
+
+            // Update loca checksum and last loca index
+            writeULong(locaOffset + glyphs.size() * 4, endOffset);
+
+            checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
+            writeULong(locaDirOffset, checksum);
+        } else {
+            throw new IOException("Can't find glyf table");
+        }
+    }
+
+
+    /**
+     * Create the hmtx table by copying metrics from original
+     * font to subset font. The glyphs Map contains an
+     * Integer key and Integer value that maps the original
+     * metric (key) to the subset metric (value)
+     */
+    private void createHmtx(FontFileReader in,
+                            Map glyphs) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+
+        int longHorMetricSize = glyphs.size() * 2;
+        int leftSideBearingSize = glyphs.size() * 2;
+        int hmtxSize = longHorMetricSize + leftSideBearingSize;
+
+        if (entry != null) {
+            pad4();
+            //int offset = (int)entry.offset;
+            Iterator e = glyphs.keySet().iterator();
+            while (e.hasNext()) {
+                Integer origIndex = (Integer)e.next();
+                Integer subsetIndex = (Integer)glyphs.get(origIndex);
+
+                writeUShort(currentPos + subsetIndex.intValue() * 4,
+                            mtxTab[origIndex.intValue()].getWx());
+                writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
+                            mtxTab[origIndex.intValue()].getLsb());
+            }
+
+            int checksum = getCheckSum(currentPos, hmtxSize);
+            writeULong(hmtxDirOffset, checksum);
+            writeULong(hmtxDirOffset + 4, currentPos);
+            writeULong(hmtxDirOffset + 8, hmtxSize);
+            currentPos += hmtxSize;
+            realSize += hmtxSize;
+        } else {
+            throw new IOException("Can't find hmtx table");
+        }
+    }
+
+    /**
+     * Returns a List containing the glyph itself plus all glyphs
+     * that this composite glyph uses
+     */
+    private List getIncludedGlyphs(FontFileReader in, int glyphOffset,
+                                     Integer glyphIdx) throws IOException {
+        List ret = new java.util.ArrayList();
+        ret.add(glyphIdx);
+        int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() + 10;
+        Integer compositeIdx = null;
+        int flags = 0;
+        boolean moreComposites = true;
+        while (moreComposites) {
+            flags = in.readTTFUShort(offset);
+            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
+            ret.add(compositeIdx);
+
+            offset += 4;
+            if ((flags & 1) > 0) {
+                // ARG_1_AND_ARG_2_ARE_WORDS
+                offset += 4;
+            } else {
+                offset += 2;
+            }
+
+            if ((flags & 8) > 0) {
+                offset += 2;    // WE_HAVE_A_SCALE
+            } else if ((flags & 64) > 0) {
+                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
+            } else if ((flags & 128) > 0) {
+                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
+            }
+
+            if ((flags & 32) > 0) {
+                moreComposites = true;
+            } else {
+                moreComposites = false;
+            }
+        }
+
+        return ret;
+    }
+
+
+    /**
+     * Rewrite all compositepointers in glyphindex glyphIdx
+     *
+     */
+    private void remapComposite(FontFileReader in, Map glyphs,
+                                int glyphOffset,
+                                Integer glyphIdx) throws IOException {
+        int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset()
+                     + 10;
+
+        Integer compositeIdx = null;
+        int flags = 0;
+        boolean moreComposites = true;
+
+        while (moreComposites) {
+            flags = in.readTTFUShort(offset);
+            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
+            Integer newIdx = (Integer)glyphs.get(compositeIdx);
+            if (newIdx == null) {
+                // This errormessage would look much better
+                // if the fontname was printed to
+                //log.error("An embedded font "
+                //                     + "contains bad glyph data. "
+                //                     + "Characters might not display "
+                //                     + "correctly.");
+                moreComposites = false;
+                continue;
+            }
+
+            in.writeTTFUShort(offset + 2, newIdx.intValue());
+
+            offset += 4;
+
+            if ((flags & 1) > 0) {
+                // ARG_1_AND_ARG_2_ARE_WORDS
+                offset += 4;
+            } else {
+                offset += 2;
+            }
+
+            if ((flags & 8) > 0) {
+                offset += 2;    // WE_HAVE_A_SCALE
+            } else if ((flags & 64) > 0) {
+                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
+            } else if ((flags & 128) > 0) {
+                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
+            }
+
+            if ((flags & 32) > 0) {
+                moreComposites = true;
+            } else {
+                moreComposites = false;
+            }
+        }
+    }
+
+
+    /**
+     * Scan all the original glyphs for composite glyphs and add those glyphs
+     * to the glyphmapping also rewrite the composite glyph pointers to the new
+     * mapping
+     */
+    private void scanGlyphs(FontFileReader in,
+                            Map glyphs) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+        Map newComposites = null;
+        Map allComposites = new java.util.HashMap();
+
+        int newIndex = glyphs.size();
+
+        if (entry != null) {
+            while (newComposites == null || newComposites.size() > 0) {
+                // Inefficient to iterate through all glyphs
+                newComposites = new java.util.HashMap();
+
+                Iterator e = glyphs.keySet().iterator();
+                while (e.hasNext()) {
+                    Integer origIndex = (Integer)e.next();
+
+                    if (in.readTTFShort(entry.getOffset()
+                                        + mtxTab[origIndex.intValue()].getOffset()) < 0) {
+                        // origIndex is a composite glyph
+                        allComposites.put(origIndex, glyphs.get(origIndex));
+                        List composites =
+                            getIncludedGlyphs(in, (int)entry.getOffset(),
+                                              origIndex);
+
+                        // Iterate through all composites pointed to
+                        // by this composite and check if they exists
+                        // in the glyphs map, add them if not.
+                        Iterator cps = composites.iterator();
+                        while (cps.hasNext()) {
+                            Integer cIdx = (Integer)cps.next();
+                            if (glyphs.get(cIdx) == null
+                                    && newComposites.get(cIdx) == null) {
+                                newComposites.put(cIdx,
+                                                  new Integer(newIndex));
+                                newIndex++;
+                            }
+                        }
+                    }
+                }
+
+                // Add composites to glyphs
+                Iterator m = newComposites.keySet().iterator();
+                while (m.hasNext()) {
+                    Integer im = (Integer)m.next();
+                    glyphs.put(im, newComposites.get(im));
+                }
+            }
+
+            // Iterate through all composites to remap their composite index
+            Iterator ce = allComposites.keySet().iterator();
+            while (ce.hasNext()) {
+                remapComposite(in, glyphs, (int)entry.getOffset(),
+                               (Integer)ce.next());
+            }
+
+        } else {
+            throw new IOException("Can't find glyf table");
+        }
+    }
+
+
+
+    /**
+     * Returns a subset of the original font.
+     *
+     * @param in FontFileReader to read from
+     * @param name Name to be checked for in the font file
+     * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+     * new index as (Integer) value)
+     * @return A subset of the original font
+     * @throws IOException in case of an I/O problem
+     */
+    public byte[] readFont(FontFileReader in, String name,
+                           Map glyphs) throws IOException {
+
+        //Check if TrueType collection, and that the name exists in the collection
+        if (!checkTTC(in, name)) {
+            throw new IOException("Failed to read font");
+        }
+
+        output = new byte[in.getFileSize()];
+
+        readDirTabs(in);
+        readFontHeader(in);
+        getNumGlyphs(in);
+        readHorizontalHeader(in);
+        readHorizontalMetrics(in);
+        readIndexToLocation(in);
+
+        scanGlyphs(in, glyphs);
+
+        createDirectory();                // Create the TrueType header and directory
+
+        createHead(in);
+        createHhea(in, glyphs.size());    // Create the hhea table
+        createHmtx(in, glyphs);           // Create hmtx table
+        createMaxp(in, glyphs.size());    // copy the maxp table
+
+        try {
+            createCvt(in);    // copy the cvt table
+        } catch (IOException ex) {
+            // Cvt is optional (only required for OpenType (MS) fonts)
+            //log.error("TrueType warning: " + ex.getMessage());
+        }
+
+        try {
+            createFpgm(in);    // copy fpgm table
+        } catch (IOException ex) {
+            // Fpgm is optional (only required for OpenType (MS) fonts)
+            //log.error("TrueType warning: " + ex.getMessage());
+        }
+
+        try {
+            createPrep(in);    // copy prep table
+        } catch (IOException ex) {
+            // Prep is optional (only required for OpenType (MS) fonts)
+            //log.error("TrueType warning: " + ex.getMessage());
+        }
+
+        try {
+            createLoca(glyphs.size());    // create empty loca table
+        } catch (IOException ex) {
+            // Loca is optional (only required for OpenType (MS) fonts)
+            //log.error("TrueType warning: " + ex.getMessage());
+        }
+
+        try {
+            createGlyf(in, glyphs);
+        } catch (IOException ex) {
+            // Glyf is optional (only required for OpenType (MS) fonts)
+            //log.error("TrueType warning: " + ex.getMessage());
+        }
+
+        pad4();
+        createCheckSumAdjustment();
+
+        byte[] ret = new byte[realSize];
+        System.arraycopy(output, 0, ret, 0, realSize);
+
+        return ret;
+    }
+
+    /**
+     * writes a ISO-8859-1 string at the currentPosition
+     * updates currentPosition but not realSize
+     * @return number of bytes written
+     */
+    private int writeString(String str) {
+        int length = 0;
+        try {
+            byte[] buf = str.getBytes("ISO-8859-1");
+            System.arraycopy(buf, 0, output, currentPos, buf.length);
+            length = buf.length;
+            currentPos += length;
+        } catch (java.io.UnsupportedEncodingException e) {
+            // This should never happen!
+        }
+
+        return length;
+    }
+
+    /**
+     * Appends a byte to the output array,
+     * updates currentPost but not realSize
+     */
+    private void writeByte(byte b) {
+        output[currentPos++] = b;
+    }
+
+    /**
+     * Appends a USHORT to the output array,
+     * updates currentPost but not realSize
+     */
+    private void writeUShort(int s) {
+        byte b1 = (byte)((s >> 8) & 0xff);
+        byte b2 = (byte)(s & 0xff);
+        writeByte(b1);
+        writeByte(b2);
+    }
+
+    /**
+     * Appends a USHORT to the output array,
+     * at the given position without changing currentPos
+     */
+    private void writeUShort(int pos, int s) {
+        byte b1 = (byte)((s >> 8) & 0xff);
+        byte b2 = (byte)(s & 0xff);
+        output[pos] = b1;
+        output[pos + 1] = b2;
+    }
+
+    /**
+     * Appends a ULONG to the output array,
+     * updates currentPos but not realSize
+     */
+    private void writeULong(int s) {
+        byte b1 = (byte)((s >> 24) & 0xff);
+        byte b2 = (byte)((s >> 16) & 0xff);
+        byte b3 = (byte)((s >> 8) & 0xff);
+        byte b4 = (byte)(s & 0xff);
+        writeByte(b1);
+        writeByte(b2);
+        writeByte(b3);
+        writeByte(b4);
+    }
+
+    /**
+     * Appends a ULONG to the output array,
+     * at the given position without changing currentPos
+     */
+    private void writeULong(int pos, int s) {
+        byte b1 = (byte)((s >> 24) & 0xff);
+        byte b2 = (byte)((s >> 16) & 0xff);
+        byte b3 = (byte)((s >> 8) & 0xff);
+        byte b4 = (byte)(s & 0xff);
+        output[pos] = b1;
+        output[pos + 1] = b2;
+        output[pos + 2] = b3;
+        output[pos + 3] = b4;
+    }
+
+    /**
+     * Read a signed short value at given position
+     */
+    private short readShort(int pos) {
+        int ret = readUShort(pos);
+        return (short)ret;
+    }
+
+    /**
+     * Read a unsigned short value at given position
+     */
+    private int readUShort(int pos) {
+        int ret = (int)output[pos];
+        if (ret < 0) {
+            ret += 256;
+        }
+        ret = ret << 8;
+        if ((int)output[pos + 1] < 0) {
+            ret |= (int)output[pos + 1] + 256;
+        } else {
+            ret |= (int)output[pos + 1];
+        }
+
+        return ret;
+    }
+
+    /**
+     * Create a padding in the fontfile to align
+     * on a 4-byte boundary
+     */
+    private void pad4() {
+        int padSize = currentPos % 4;
+        for (int i = 0; i < padSize; i++) {
+            output[currentPos++] = 0;
+            realSize++;
+        }
+    }
+
+    /**
+     * Returns the maximum power of 2 <= max
+     */
+    private int maxPow2(int max) {
+        int i = 0;
+        while (Math.pow(2, (double)i) < max) {
+            i++;
+        }
+
+        return (i - 1);
+    }
+
+    private int log2(int num) {
+        return (int)(Math.log((double)num) / Math.log(2));
+    }
+
+
+    private int getCheckSum(int start, int size) {
+        return (int)getLongCheckSum(start, size);
+    }
+
+    private long getLongCheckSum(int start, int size) {
+        // All the tables here are aligned on four byte boundaries
+        // Add remainder to size if it's not a multiple of 4
+        int remainder = size % 4;
+        if (remainder != 0) {
+            size += remainder;
+        }
+
+        long sum = 0;
+
+        for (int i = 0; i < size; i += 4) {
+            int l = (int)(output[start + i] << 24);
+            l += (int)(output[start + i + 1] << 16);
+            l += (int)(output[start + i + 2] << 16);
+            l += (int)(output[start + i + 3] << 16);
+            sum += l;
+            if (sum > 0xffffffff) {
+                sum = sum - 0xffffffff;
+            }
+        }
+
+        return sum;
+    }
+
+    private void createCheckSumAdjustment() {
+        long sum = getLongCheckSum(0, realSize);
+        int checksum = (int)(0xb1b0afba - sum);
+        writeULong(checkSumAdjustmentOffset, checksum);
+    }
+
+}
+
+
+