You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by je...@apache.org on 2008/02/14 09:12:38 UTC

svn commit: r627679 [1/2] - in /xmlgraphics/fop/trunk: ./ lib/ src/codegen/fonts/ src/java/org/apache/fop/fonts/ src/java/org/apache/fop/fonts/truetype/ src/java/org/apache/fop/fonts/type1/ src/java/org/apache/fop/pdf/ src/java/org/apache/fop/render/pd...

Author: jeremias
Date: Thu Feb 14 00:12:34 2008
New Revision: 627679

URL: http://svn.apache.org/viewvc?rev=627679&view=rev
Log:
Added support for Type 1 fonts which don't use the AdobeStandardEncoding for PDF and PS output. Details:
Added an Type 1 AFM parser (only basic ltr script fonts are properly supported).
Font loading changed slightly to allow loading an AFM in addition to a PFM.
Added some mapping functionality to CodePointMapping. Now we also build custom CodePointMapping instances from AFM files and use it in SingleByteFonts.
Changed more PDF object classes to make use of the generic PDFDictionary and PDFArray base classes.
Type 1 Fonts with a special encoding now register their encoding in the Encoding value of the font dictionary so the mapping is correct. For PS this isn't necessary as the interpreter just uses the font's default encoding.
Refactored CMap building code to it can also be used outside the PDF context. A CMap can now also be built from a single byte encoding.
Update of XML Graphics Commons snapshot.


Added:
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java   (with props)
Modified:
    xmlgraphics/fop/trunk/lib/xmlgraphics-commons-1.3svn.jar
    xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl
    xmlgraphics/fop/trunk/src/codegen/fonts/encodings.xml
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CIDFontType.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CustomFont.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontLoader.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontType.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/SingleByteFont.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFCIDFontDescriptor.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFCMap.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFDictionary.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEncoding.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFont.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFontDescriptor.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFontNonBase14.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFontTrueType.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFontType0.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFontType3.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFObject.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFRectangle.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFT1Stream.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFTTFStream.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFToUnicodeCMap.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderer.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/PSFontUtils.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/util/CharUtilities.java
    xmlgraphics/fop/trunk/status.xml
    xmlgraphics/fop/trunk/test/java/org/apache/fop/render/pdf/PDFCMapTestCase.java

Modified: xmlgraphics/fop/trunk/lib/xmlgraphics-commons-1.3svn.jar
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/lib/xmlgraphics-commons-1.3svn.jar?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
Binary files - no diff available.

Modified: xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl (original)
+++ xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl Thu Feb 14 00:12:34 2008
@@ -40,23 +40,37 @@
 
 package org.apache.fop.fonts;
 
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Collections;
 
+import org.apache.fop.util.CharUtilities;
+
 public class CodePointMapping {
+
+<xsl:apply-templates mode="constant"/>
+
+    private String name;
     private char[] latin1Map;
     private char[] characters;
     private char[] codepoints;
+    private char[] unicodeMap; //code point to Unicode char
 
-    private CodePointMapping(int [] table) {
+    public CodePointMapping(String name, int[] table) {
+        this.name = name;
         int nonLatin1 = 0;
         latin1Map = new char[256];
+        unicodeMap = new char[256];
+        Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
         for (int i = 0; i &lt; table.length; i += 2) {
            if (table[i + 1] &lt; 256) {
                latin1Map[table[i + 1]] = (char) table[i];
            } else {
                ++nonLatin1;
            }
+           if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
+               unicodeMap[table[i]] = (char)table[i + 1];
+           }
         }
         characters = new char[nonLatin1];
         codepoints = new char[nonLatin1];
@@ -79,6 +93,10 @@
         }
     }
 
+    public String getName() {
+        return this.name;
+    }
+
     public final char mapChar(char c) {
         if (c &lt; 256) {
             return latin1Map[c];
@@ -100,10 +118,26 @@
         }
     }
 
+    public final char getUnicodeForIndex(int idx) {
+        return this.unicodeMap[idx];
+    }
+
+    public final char[] getUnicodeCharMap() {
+        char[] copy = new char[this.unicodeMap.length];
+        System.arraycopy(this.unicodeMap, 0, copy, 0, this.unicodeMap.length);
+        return copy;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        return getName();
+    }
+
     private static Map mappings;
     static {
         mappings = Collections.synchronizedMap(new java.util.HashMap());
     }
+
     public static CodePointMapping getMapping(String encoding) {
         CodePointMapping mapping = (CodePointMapping) mappings.get(encoding);
         if (mapping != null) {
@@ -119,10 +153,12 @@
 }
   </xsl:template>
 
+  <xsl:template match="encoding" mode="constant">    public static final String <xsl:value-of select="@constant"/> = "<xsl:value-of select="@id"/>";</xsl:template>
+  
   <xsl:template match="encoding" mode="get">
-        else if (encoding.equals("<xsl:value-of select="@id"/>")) {
-            mapping = new CodePointMapping(enc<xsl:value-of select="@id"/>);
-            mappings.put("<xsl:value-of select="@id"/>", mapping);
+        else if (encoding.equals(<xsl:value-of select="@constant"/>)) {
+            mapping = new CodePointMapping(<xsl:value-of select="@constant"/>, enc<xsl:value-of select="@id"/>);
+            mappings.put(<xsl:value-of select="@constant"/>, mapping);
             return mapping;
         }
   </xsl:template>

Modified: xmlgraphics/fop/trunk/src/codegen/fonts/encodings.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/codegen/fonts/encodings.xml?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/codegen/fonts/encodings.xml (original)
+++ xmlgraphics/fop/trunk/src/codegen/fonts/encodings.xml Thu Feb 14 00:12:34 2008
@@ -19,12 +19,12 @@
 <!DOCTYPE encoding-set [
   <!ELEMENT encoding-set (encoding+)>
   <!ELEMENT encoding (glyph+)>
-  <!ATTLIST encoding id ID #REQUIRED glyphlist CDATA "AGL">
+  <!ATTLIST encoding id ID #REQUIRED constant CDATA #REQUIRED glyphlist CDATA "AGL">
   <!ELEMENT glyph EMPTY>
   <!ATTLIST glyph codepoint CDATA #REQUIRED name CDATA #REQUIRED>
 ]>
 <encoding-set>
-  <encoding id='StandardEncoding' glyphlist='AGL'>
+  <encoding id='StandardEncoding' constant="STANDARD_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='quotedbl'/>
@@ -175,7 +175,7 @@
     <glyph codepoint='fa' name='oe'/>
     <glyph codepoint='fb' name='germandbls'/>
   </encoding>
-  <encoding id='ISOLatin1Encoding' glyphlist='AGL'>
+  <encoding id='ISOLatin1Encoding' constant="ISOLATIN1_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='quotedbl'/>
@@ -377,7 +377,7 @@
     <glyph codepoint='fe' name='thorn'/>
     <glyph codepoint='ff' name='ydieresis'/>
   </encoding>
-  <encoding id='CEEncoding' glyphlist='AGL'>
+  <encoding id='CEEncoding' constant="CE_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='quotedbl'/>
@@ -594,7 +594,7 @@
     <glyph codepoint='fe' name='tcommaaccent'/>
     <glyph codepoint='ff' name='dotaccent'/>
   </encoding>
-  <encoding id='MacRomanEncoding' glyphlist='AGL'>
+  <encoding id='MacRomanEncoding' constant="MAC_ROMAN_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='quotedbl'/>
@@ -803,7 +803,7 @@
     <glyph codepoint='b4' name='yen'/>
     <glyph codepoint='7a' name='z'/>
   </encoding>
-  <encoding id='WinAnsiEncoding' glyphlist='AGL'>
+  <encoding id='WinAnsiEncoding' constant="WIN_ANSI_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='quotedbl'/>
@@ -1021,7 +1021,7 @@
     <glyph codepoint='fe' name='thorn'/>
     <glyph codepoint='ff' name='ydieresis'/>
   </encoding>
-  <encoding id='PDFDocEncoding' glyphlist='AGL'>
+  <encoding id='PDFDocEncoding' constant="PDF_DOC_ENCODING" glyphlist='AGL'>
     <glyph codepoint='18' name='breve'/>
     <glyph codepoint='19' name='caron'/>
     <glyph codepoint='1a' name='circumflex'/>
@@ -1252,7 +1252,7 @@
     <glyph codepoint='fe' name='thorn'/>
     <glyph codepoint='ff' name='ydieresis'/>
   </encoding>
-  <encoding id='SymbolEncoding' glyphlist='AGL'>
+  <encoding id='SymbolEncoding' constant="SYMBOL_ENCODING" glyphlist='AGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='exclam'/>
     <glyph codepoint='22' name='universal'/>
@@ -1443,7 +1443,7 @@
     <glyph codepoint='fd' name='bracerightmid'/>
     <glyph codepoint='fe' name='bracerightbt'/>
   </encoding>
-  <encoding id='ZapfDingbatsEncoding' glyphlist='ZGL'>
+  <encoding id='ZapfDingbatsEncoding' constant="ZAPF_DINGBATS_ENCODING" glyphlist='ZGL'>
     <glyph codepoint='20' name='space'/>
     <glyph codepoint='21' name='a1'/>
     <glyph codepoint='22' name='a2'/>

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CIDFontType.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CIDFontType.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CIDFontType.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CIDFontType.java Thu Feb 14 00:12:34 2008
@@ -27,12 +27,12 @@
 public class CIDFontType extends ValuedEnum {
 
     /**
-     * CID Font Type 0
+     * CID Font Type 0 (based on Type 1 format)
      */
     public static final CIDFontType CIDTYPE0 = new CIDFontType("CIDFontType0", 0);
 
     /**
-     * CID Font Type 2
+     * CID Font Type 2 (based on TrueType format)
      */
     public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1);
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CustomFont.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CustomFont.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CustomFont.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/CustomFont.java Thu Feb 14 00:12:34 2008
@@ -230,9 +230,7 @@
      * @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 */
+        return firstChar;
     }
 
     /**
@@ -408,14 +406,25 @@
         this.resolver = resolver;
     }
 
-    /**
-     * {@inheritDoc} 
-     */
+    /** {@inheritDoc} */
     public void putKerningEntry(Integer key, Map value) {
         if (kerning == null) {
             kerning = new java.util.HashMap();
         }
         this.kerning.put(key, value);
+    }
+    
+    /**
+     * Replaces the existing kerning map with a new one.
+     * @param kerningMap the kerning map (Map<Integer, Map<Integer, Integer>, the integers are
+     *                          character codes)
+     */
+    public void replaceKerningMap(Map kerningMap) {
+        if (kerningMap == null) {
+            this.kerning = Collections.EMPTY_MAP;
+        } else {
+            this.kerning = kerningMap;
+        }
     }
 
 }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontLoader.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontLoader.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontLoader.java Thu Feb 14 00:12:34 2008
@@ -28,9 +28,9 @@
 import javax.xml.transform.Source;
 import javax.xml.transform.stream.StreamSource;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
 import org.apache.fop.fonts.truetype.TTFFontLoader;
 import org.apache.fop.fonts.type1.Type1FontLoader;
 
@@ -46,8 +46,6 @@
 
     /** URI representing the font file */
     protected String fontFileURI = null;
-    /** the InputStream to load the font from */
-    protected InputStream in = null;
     /** the FontResolver to use for font URI resolution */
     protected FontResolver resolver = null;
     /** the loaded font */
@@ -59,12 +57,10 @@
     /**
      * Default constructor.
      * @param fontFileURI the URI to the PFB file of a Type 1 font
-     * @param in the InputStream reading the PFM file of a Type 1 font
      * @param resolver the font resolver used to resolve URIs
      */
-    public FontLoader(String fontFileURI, InputStream in, FontResolver resolver) {
+    public FontLoader(String fontFileURI, FontResolver resolver) {
         this.fontFileURI = fontFileURI;
-        this.in = in;
         this.resolver = resolver;
     }
 
@@ -107,58 +103,25 @@
     public static CustomFont loadFont(String fontFileURI, FontResolver resolver)
                 throws IOException {
         fontFileURI = fontFileURI.trim();
-        String effURI;
         boolean type1 = isType1(fontFileURI);
+        FontLoader loader;
         if (type1) {
-            String pfmExt = fontFileURI.substring(
-                    fontFileURI.length() - 3, fontFileURI.length());
-            pfmExt = pfmExt.substring(0, 2) + (Character.isUpperCase(pfmExt.charAt(2)) ? "M" : "m");
-            effURI = fontFileURI.substring(0, fontFileURI.length() - 4) + "." + pfmExt;
+            loader = new Type1FontLoader(fontFileURI, resolver);
         } else {
-            effURI = fontFileURI;
+            loader = new TTFFontLoader(fontFileURI, resolver);
         }
-        if (log.isDebugEnabled()) {
-            log.debug("opening " + effURI);
-        }
-        InputStream in = openFontUri(resolver, effURI);
-        return loadFontFromInputStream(fontFileURI, resolver, type1, in);
+        return loader.getFont();
     }
 
     /**
-     * Loads and returns a font given an input stream.
-     * @param fontFileURI font file uri
-     * @param resolver font resolver
-     * @param isType1 is it a type1 font?
-     * @param in input stream
-     * @return the loaded font.
-     * @throws IOException In case of an I/O error
-     */
-    protected static CustomFont loadFontFromInputStream(
-            String fontFileURI, FontResolver resolver, boolean isType1,
-            InputStream in)
-                throws IOException {
-        FontLoader loader;
-        try {
-            if (isType1) {
-                loader = new Type1FontLoader(fontFileURI, in, resolver);
-            } else {
-                loader = new TTFFontLoader(fontFileURI, in, resolver);
-            }
-            return loader.getFont();
-        } finally {
-            IOUtils.closeQuietly(in);
-        }
-    }
-
-    /**
-     * Opens a font uri and returns an input stream.
+     * Opens a font URI and returns an input stream.
      * @param resolver the FontResolver to use for font URI resolution
      * @param uri the URI representing the font
      * @return the InputStream to read the font from.
      * @throws IOException In case of an I/O error
      * @throws MalformedURLException If an invalid URL is built
      */
-    private static InputStream openFontUri(FontResolver resolver, String uri) 
+    protected static InputStream openFontUri(FontResolver resolver, String uri) 
                     throws IOException, MalformedURLException {
         InputStream in = null;
         if (resolver != null) {
@@ -191,7 +154,11 @@
      */
     protected abstract void read() throws IOException;
 
-    /** @see FontLoader#getFont() */
+    /**
+     * Returns the custom font that was read using this instance of FontLoader.
+     * @return the newly loaded font
+     * @throws IOException if an I/O error occurs
+     */
     public CustomFont getFont() throws IOException {
         if (!loaded) {
             read();

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontType.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontType.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontType.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/FontType.java Thu Feb 14 00:12:34 2008
@@ -31,7 +31,7 @@
      */
     public static final FontType OTHER       = new FontType("Other", 0);
     /**
-     * Adobe Type 0 fonts
+     * Adobe Type 0 fonts (composite font)
      */
     public static final FontType TYPE0       = new FontType("Type0", 1);
     /**

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/SingleByteFont.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/SingleByteFont.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/SingleByteFont.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/SingleByteFont.java Thu Feb 14 00:12:34 2008
@@ -19,6 +19,8 @@
 
 package org.apache.fop.fonts;
 
+import java.util.Set;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -32,86 +34,79 @@
 
     private CodePointMapping mapping;
 
-    private String encoding = "WinAnsiEncoding";
-
     private int[] width = null;
 
+    private Set warnedChars;
+    
     /**
      * Main constructor.
      */
     public SingleByteFont() {
-        updateMapping();
+        setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
     }
     
-    /**
-     * Updates the mapping variable based on the encoding.
-     */
-    protected void updateMapping() {
-        try {
-            mapping = CodePointMapping.getMapping(getEncoding());
-        } catch (UnsupportedOperationException e) {
-            log.error("Font '" + super.getFontName() + "': " + e.getMessage());
-        }
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public boolean isEmbeddable() {
         return (getEmbedFileName() == null && getEmbedResourceName() == null) ? false
                : true;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public String getEncoding() {
-        return encoding;
+        return this.mapping.getName();
     }
 
     /**
-     * Sets the encoding of the font.
-     * @param encoding the encoding (ex. "WinAnsiEncoding" or "SymbolEncoding")
+     * Returns the code point mapping (encoding) of this font.
+     * @return the code point mapping
      */
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-        updateMapping();
+    public CodePointMapping getCodePointMapping() {
+        return this.mapping;
     }
-
-    /**
-     * {@inheritDoc} 
-     */
+    
+    /** {@inheritDoc} */
     public int getWidth(int i, int size) {
-        return size * width[i];
+        int idx = i - getFirstChar();
+        if (idx >= 0 && idx < width.length) {
+            return size * width[i - getFirstChar()];
+        } else {
+            return 0;
+        }
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public int[] getWidths() {
         int[] arr = new int[width.length];
         System.arraycopy(width, 0, arr, 0, width.length - 1);
         return arr;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public char mapChar(char c) {
         notifyMapOperation();
         char d = mapping.mapChar(c);
         if (d != 0) {
             return d;
         } else {
-            log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c) 
-                    + ") not available in font " + getFontName());
+            Character ch = new Character(c);
+            if (warnedChars == null) {
+                warnedChars = new java.util.HashSet();
+            }
+            if (warnedChars.size() < 8 && !warnedChars.contains(ch)) {
+                warnedChars.add(ch);
+                if (warnedChars.size() == 8) {
+                    log.warn("Many requested glyphs are not available in font " + getFontName());
+                } else {
+                    log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c) 
+                            + ", " + Glyphs.charToGlyphName(c)
+                            + ") not available in font " + getFontName());
+                }
+            }
             return '#';
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public boolean hasChar(char c) {
         return (mapping.mapChar(c) > 0);
     }
@@ -119,15 +114,43 @@
     /* ---- single byte font specific setters --- */
 
     /**
+     * Updates the mapping variable based on the encoding.
+     * @param encoding the name of the encoding
+     */
+    protected void updateMapping(String encoding) {
+        try {
+            this.mapping = CodePointMapping.getMapping(encoding);
+        } catch (UnsupportedOperationException e) {
+            log.error("Font '" + super.getFontName() + "': " + e.getMessage());
+        }
+    }
+    
+    /**
+     * Sets the encoding of the font.
+     * @param encoding the encoding (ex. "WinAnsiEncoding" or "SymbolEncoding")
+     */
+    public void setEncoding(String encoding) {
+        updateMapping(encoding);
+    }
+    
+    /**
+     * Sets the encoding of the font.
+     * @param encoding the encoding information
+     */
+    public void setEncoding(CodePointMapping encoding) {
+        this.mapping = encoding;
+    }
+
+    /**
      * 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 = new int[getLastChar() - getFirstChar() + 1];
         }
-        this.width[index] = width;
+        this.width[index - getFirstChar()] = width;
     }
 
 }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java Thu Feb 14 00:12:34 2008
@@ -25,6 +25,8 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.io.IOUtils;
+
 import org.apache.fop.fonts.BFEntry;
 import org.apache.fop.fonts.CIDFontType;
 import org.apache.fop.fonts.FontLoader;
@@ -41,23 +43,30 @@
     /**
      * Default constructor
      * @param fontFileURI the URI representing the font file
-     * @param in the InputStream to load the font from
      * @param resolver the FontResolver for font URI resolution
      */
-    public TTFFontLoader(String fontFileURI, InputStream in, FontResolver resolver) {
-        super(fontFileURI, in, resolver);
+    public TTFFontLoader(String fontFileURI, FontResolver resolver) {
+        super(fontFileURI, resolver);
     }
     
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     protected void read() throws IOException {
-        TTFFile ttf = new TTFFile();
-        FontFileReader reader = new FontFileReader(in);
-        boolean supported = ttf.readFont(reader, null);
-        if (!supported) {
-            throw new IOException("Could not load TrueType font: " + fontFileURI);
+        InputStream in = openFontUri(resolver, this.fontFileURI);
+        try {
+            TTFFile ttf = new TTFFile();
+            FontFileReader reader = new FontFileReader(in);
+            boolean supported = ttf.readFont(reader, null);
+            if (!supported) {
+                throw new IOException("Could not load TrueType font: " + fontFileURI);
+            }
+            buildFont(ttf);
+            loaded = true;
+        } finally {
+            IOUtils.closeQuietly(in);
         }
+    }
+
+    private void buildFont(TTFFile ttf) {
         if (ttf.isCFF()) {
             throw new UnsupportedOperationException(
                     "OpenType fonts with CFF data are not supported, yet");
@@ -98,7 +107,6 @@
         multiFont.setBFEntries(bfentries);
         copyKerning(ttf, true);
         multiFont.setEmbedFileName(this.fontFileURI);
-        loaded = true;
     }
     
     /**

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java?rev=627679&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java Thu Feb 14 00:12:34 2008
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.type1;
+
+
+/**
+ * Holds the metrics of a single character from an AFM file.
+ */
+public class AFMCharMetrics {
+
+    private int charCode;
+    private String unicodeChars;
+    private String charName;
+    private double widthX;
+    private double widthY;
+    
+    /**
+     * Returns the character code.
+     * @return the charCode
+     */
+    public int getCharCode() {
+        return charCode;
+    }
+    
+    /**
+     * Sets the character code.
+     * @param charCode the charCode to set
+     */
+    public void setCharCode(int charCode) {
+        this.charCode = charCode;
+    }
+    
+    /**
+     * Returns the Unicode characters represented by this object. Some character names can be
+     * mapped to multiple Unicode code points, so expect to find more than one character in the
+     * String.
+     * @return the Unicode characters
+     */
+    public String getUnicodeChars() {
+        return this.unicodeChars;
+    }
+    
+    /**
+     * Sets the Unicode characters represented by this object.
+     * @param unicodeChars the Unicode characters
+     */
+    public void setUnicodeChars(String unicodeChars) {
+        this.unicodeChars = unicodeChars;
+    }
+    
+    /**
+     * Returns the PostScript character name.
+     * @return the charName
+     */
+    public String getCharName() {
+        return charName;
+    }
+    
+    /**
+     * Sets the PostScript character name.
+     * @param charName the charName to set
+     */
+    public void setCharName(String charName) {
+        this.charName = charName;
+    }
+    
+    /**
+     * Returns the progression dimension in x-direction.
+     * @return the widthX
+     */
+    public double getWidthX() {
+        return widthX;
+    }
+    
+    /**
+     * Sets the progression dimension in x-direction
+     * @param widthX the widthX to set
+     */
+    public void setWidthX(double widthX) {
+        this.widthX = widthX;
+    }
+    
+    /**
+     * Returns the progression dimension in y-direction.
+     * @return the widthY
+     */
+    public double getWidthY() {
+        return widthY;
+    }
+    
+    /**
+     * Sets the progression dimension in y-direction
+     * @param widthY the widthY to set
+     */
+    public void setWidthY(double widthY) {
+        this.widthY = widthY;
+    }
+    
+    /** {@inheritDoc} */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("AFM Char: ");
+        sb.append(getCharCode());
+        sb.append(" (");
+        if (getUnicodeChars() != null) {
+            for (int i = 0, c = getUnicodeChars().length(); i < c; i++) {
+                sb.append("0x").append(Integer.toHexString(getUnicodeChars().charAt(i)));
+                sb.append(", ");
+            }
+        }
+        sb.append(getCharName()).append(')');
+        return sb.toString();
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java?rev=627679&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java Thu Feb 14 00:12:34 2008
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.type1;
+
+import java.awt.geom.RectangularShape;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.xmlgraphics.fonts.Glyphs;
+import org.apache.xmlgraphics.java2d.Dimension2DDouble;
+
+/**
+ * Represents the contents of a Type 1 AFM font metrics file.
+ */
+public class AFMFile {
+
+    private String fontName;
+    private String fullName;
+    private String familyName;
+    
+    private String weight;
+    private RectangularShape fontBBox;
+    
+    private String encodingScheme;
+    private String characterSet;
+    
+    private Number capHeight;
+    private Number xHeight;
+    private Number ascender;
+    private Number descender;
+    private Number stdHW;
+    private Number stdVW;
+
+    private AFMWritingDirectionMetrics[] writingDirectionMetrics
+        = new AFMWritingDirectionMetrics[3];
+    
+    private List charMetrics = new java.util.ArrayList();
+    //List<AFMCharMetrics>
+    private Map charNameToMetrics = new java.util.HashMap();
+    //Map<String, AFMCharMetrics>
+    
+    private Map kerningMap;
+    //Map<String, Map<String, Dimension>>
+    
+    /**
+     * Default constructor.
+     */
+    public AFMFile() {
+        //nop
+    }
+
+    /**
+     * Returns the FontName value.
+     * @return the font name
+     */
+    public String getFontName() {
+        return fontName;
+    }
+
+    /**
+     * Sets the FontName value.
+     * @param fontName the font name to set
+     */
+    public void setFontName(String fontName) {
+        this.fontName = fontName;
+    }
+
+    /**
+     * Returns the FullName value.
+     * @return the full name of the font
+     */
+    public String getFullName() {
+        return fullName;
+    }
+
+    /**
+     * Sets the FullName value.
+     * @param fullName the full name to set
+     */
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+    /**
+     * Returns the FamilyName value.
+     * @return the family name of the font
+     */
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    /**
+     * Sets the FamilyName value.
+     * @param familyName the family name to set
+     */
+    public void setFamilyName(String familyName) {
+        this.familyName = familyName;
+    }
+
+    /**
+     * Returns the Weight value.
+     * @return the weight
+     */
+    public String getWeight() {
+        return weight;
+    }
+
+    /**
+     * Sets the Weight value.
+     * @param weight the weight to set
+     */
+    public void setWeight(String weight) {
+        this.weight = weight;
+    }
+
+    /**
+     * Returns the FontBBox value.
+     * @return the font's bounding box
+     */
+    public RectangularShape getFontBBox() {
+        return fontBBox;
+    }
+
+    /**
+     * Returns the FontBBox value as integer array.
+     * @return the font's bounding box
+     */
+    public int[] getFontBBoxAsIntArray() {
+        RectangularShape rect = getFontBBox();
+        return new int[] {
+                (int)Math.floor(rect.getMinX()), (int)Math.floor(rect.getMinY()), 
+                (int)Math.ceil(rect.getMaxX()), (int)Math.ceil(rect.getMaxY())}; 
+    }
+    
+    /**
+     * Sets the FontBBox value.
+     * @param fontBBox the fontBBox to set
+     */
+    public void setFontBBox(RectangularShape fontBBox) {
+        this.fontBBox = fontBBox;
+    }
+
+    /**
+     * Returns the EncodingScheme value.
+     * @return the encoding scheme
+     */
+    public String getEncodingScheme() {
+        return encodingScheme;
+    }
+
+    /**
+     * Sets the EncodingScheme value
+     * @param encodingScheme the encodingScheme to set
+     */
+    public void setEncodingScheme(String encodingScheme) {
+        this.encodingScheme = encodingScheme;
+    }
+
+    /**
+     * Returns the CharacterSet value.
+     * @return the characterSet
+     */
+    public String getCharacterSet() {
+        return characterSet;
+    }
+
+    /**
+     * Sets the CharacterSet value.
+     * @param characterSet the characterSet to set
+     */
+    public void setCharacterSet(String characterSet) {
+        this.characterSet = characterSet;
+    }
+
+    /**
+     * Returns the CapHeight value.
+     * @return the capHeight
+     */
+    public Number getCapHeight() {
+        return capHeight;
+    }
+
+    /**
+     * Sets the CapHeight value.
+     * @param capHeight the capHeight to set
+     */
+    public void setCapHeight(Number capHeight) {
+        this.capHeight = capHeight;
+    }
+
+    /**
+     * Returns the XHeight value.
+     * @return the xHeight
+     */
+    public Number getXHeight() {
+        return xHeight;
+    }
+
+    /**
+     * Sets the XHeight value.
+     * @param height the xHeight to set
+     */
+    public void setXHeight(Number height) {
+        xHeight = height;
+    }
+
+    /**
+     * Returns the Ascender value.
+     * @return the ascender
+     */
+    public Number getAscender() {
+        return ascender;
+    }
+
+    /**
+     * Sets the Ascender value.
+     * @param ascender the ascender to set
+     */
+    public void setAscender(Number ascender) {
+        this.ascender = ascender;
+    }
+
+    /**
+     * Returns the Descender value.
+     * @return the descender
+     */
+    public Number getDescender() {
+        return descender;
+    }
+
+    /**
+     * Sets the Descender value.
+     * @param descender the descender to set
+     */
+    public void setDescender(Number descender) {
+        this.descender = descender;
+    }
+    
+    /**
+     * Returns the StdHW value.
+     * @return the descender
+     */
+    public Number getStdHW() {
+        return stdHW;
+    }
+
+    /**
+     * Sets the StdHW value.
+     * @param stdHW the StdHW to set
+     */
+    public void setStdHW(Number stdHW) {
+        this.stdHW = stdHW;
+    }
+    
+    /**
+     * Returns the StdVW value.
+     * @return the descender
+     */
+    public Number getStdVW() {
+        return stdVW;
+    }
+
+    /**
+     * Sets the StdVW value.
+     * @param stdVW the StdVW to set
+     */
+    public void setStdVW(Number stdVW) {
+        this.stdVW = stdVW;
+    }
+    
+    /**
+     * Gets writing direction metrics.
+     * @param index the writing direction (0, 1 or 2)
+     * @return the writing direction metrics
+     */
+    public AFMWritingDirectionMetrics getWritingDirectionMetrics(int index) {
+        return this.writingDirectionMetrics[index];
+    }
+    
+    /**
+     * Sets writing direction metrics.
+     * @param index the writing direction (0, 1 or 2)
+     * @param metrics the writing direction metrics
+     */
+    public void setWritingDirectionMetrics(int index, AFMWritingDirectionMetrics metrics) {
+        this.writingDirectionMetrics[index] = metrics;
+    }
+
+    /**
+     * Adds new character metrics.
+     * @param metrics the character metrics
+     */
+    public void addCharMetrics(AFMCharMetrics metrics) {
+        String name = metrics.getCharName();
+        if (metrics.getUnicodeChars() == null) {
+            if (name != null) {
+                String u = Glyphs.getUnicodeCodePointsForGlyphName(metrics.getCharName());
+                if (u != null) {
+                    metrics.setUnicodeChars(u);
+                }
+            } else {
+                //Ignore as no Unicode assignment is possible
+                return;
+            }
+        }
+        this.charMetrics.add(metrics);
+        if (name != null) {
+            this.charNameToMetrics.put(name, metrics);
+        }
+    }
+    
+    /**
+     * Returns the number of character available for this font.
+     * @return the number of character
+     */
+    public int getCharCount() {
+        return this.charMetrics.size();
+    }
+    
+    /**
+     * Returns the character metrics associated with the character name.
+     * @param name the character name
+     * @return the character metrics or null if there's no such character
+     */
+    public AFMCharMetrics getChar(String name) {
+        return (AFMCharMetrics)this.charNameToMetrics.get(name);
+    }
+    
+    /**
+     * Returns the list of AFMCharMetrics instances representing all the available characters.
+     * @return a List of AFMCharMetrics instances
+     */
+    public List getCharMetrics() {
+        return Collections.unmodifiableList(this.charMetrics);
+    }
+    
+    /**
+     * Adds a X-kerning entry.
+     * @param name1 the name of the first character
+     * @param name2 the name of the second character
+     * @param kx kerning value in x-direction
+     */
+    public void addXKerning(String name1, String name2, double kx) {
+        if (this.kerningMap == null) {
+            this.kerningMap = new java.util.HashMap();
+        }
+        Map entries = (Map)this.kerningMap.get(name1);
+        if (entries == null) {
+            entries = new java.util.HashMap();
+            this.kerningMap.put(name1, entries);
+        }
+        entries.put(name2, new Dimension2DDouble(kx, 0));
+    }
+    
+    /** {@inheritDoc} */
+    public String toString() {
+        return "AFM: " + getFullName();
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java?rev=627679&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java Thu Feb 14 00:12:34 2008
@@ -0,0 +1,594 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.type1;
+
+import java.awt.Rectangle;
+import java.beans.Statement;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Parses the contents of a Type 1 AFM font metrics file into an object structure ({@link AFMFile}).
+ */
+public class AFMParser {
+    
+    private static final String START_FONT_METRICS = "StartFontMetrics";
+    //private static final String END_FONT_METRICS = "EndFontMetrics";
+    private static final String FONT_NAME = "FontName";
+    private static final String FULL_NAME = "FullName";
+    private static final String FAMILY_NAME = "FamilyName";
+    private static final String WEIGHT = "Weight";
+    private static final String FONT_BBOX = "FontBBox";
+    private static final String ENCODING_SCHEME = "EncodingScheme";
+    private static final String CHARACTER_SET = "CharacterSet";
+    private static final String IS_BASE_FONT = "IsBaseFont";
+    private static final String IS_CID_FONT = "IsCIDFont";
+    private static final String CAP_HEIGHT = "CapHeight";
+    private static final String X_HEIGHT = "XHeight";
+    private static final String ASCENDER = "Ascender";
+    private static final String DESCENDER = "Descender";
+    private static final String STDHW = "StdHW";
+    private static final String STDVW = "StdVW";
+    private static final String UNDERLINE_POSITION = "UnderlinePosition";
+    private static final String UNDERLINE_THICKNESS = "UnderlineThickness";
+    private static final String ITALIC_ANGLE = "ItalicAngle";
+    private static final String IS_FIXED_PITCH = "IsFixedPitch";
+    private static final String START_DIRECTION = "StartDirection";
+    private static final String END_DIRECTION = "EndDirection";
+    private static final String START_CHAR_METRICS = "StartCharMetrics";
+    private static final String END_CHAR_METRICS = "EndCharMetrics";
+    private static final String C = "C";
+    private static final String CH = "CH";
+    private static final String WX = "WX";
+    private static final String W0X = "W0X";
+    private static final String W1X = "W1X";
+    private static final String WY = "WY";
+    private static final String W0Y = "W0Y";
+    private static final String W1Y = "W1Y";
+    private static final String W = "W";
+    private static final String W0 = "W0";
+    private static final String W1 = "W1";
+    private static final String N = "N";
+    private static final String START_TRACK_KERN = "StartTrackKern";
+    private static final String END_TRACK_KERN = "EndTrackKern";
+    //private static final String START_KERN_PAIRS = "StartKernPairs";
+    //private static final String START_KERN_PAIRS0 = "StartKernPairs0";
+    private static final String START_KERN_PAIRS1 = "StartKernPairs1";
+    //private static final String END_KERN_PAIRS = "EndKernPairs";
+    private static final String KP = "KP";
+    private static final String KPH = "KPH";
+    private static final String KPX = "KPX";
+    private static final String KPY = "KPY";
+
+    private static final int PARSE_NORMAL = 0;
+    private static final int PARSE_CHAR_METRICS = 1;
+    
+    private static final Map VALUE_PARSERS;
+    private static final Map PARSE_MODE_CHANGES;
+    
+    static {
+        VALUE_PARSERS = new java.util.HashMap();
+        VALUE_PARSERS.put(START_FONT_METRICS, new StartFontMetrics());
+        VALUE_PARSERS.put(FONT_NAME, new StringSetter(FONT_NAME));
+        VALUE_PARSERS.put(FULL_NAME, new StringSetter(FULL_NAME));
+        VALUE_PARSERS.put(FAMILY_NAME, new StringSetter(FAMILY_NAME));
+        VALUE_PARSERS.put(WEIGHT, new StringSetter(WEIGHT));
+        VALUE_PARSERS.put(ENCODING_SCHEME, new StringSetter(ENCODING_SCHEME));
+        VALUE_PARSERS.put(FONT_BBOX, new FontBBox());
+        VALUE_PARSERS.put(CHARACTER_SET, new StringSetter(CHARACTER_SET));
+        VALUE_PARSERS.put(IS_BASE_FONT, new IsBaseFont());
+        VALUE_PARSERS.put(IS_CID_FONT, new IsCIDFont());
+        VALUE_PARSERS.put(CAP_HEIGHT, new NumberSetter(CAP_HEIGHT));
+        VALUE_PARSERS.put(X_HEIGHT, new NumberSetter(X_HEIGHT));
+        VALUE_PARSERS.put(ASCENDER, new NumberSetter(ASCENDER));
+        VALUE_PARSERS.put(DESCENDER, new NumberSetter(DESCENDER));
+        VALUE_PARSERS.put(STDHW, new NumberSetter(STDHW));
+        VALUE_PARSERS.put(STDVW, new NumberSetter(STDVW));
+        VALUE_PARSERS.put(START_DIRECTION, new StartDirection());
+        VALUE_PARSERS.put(END_DIRECTION, new EndDirection());
+        VALUE_PARSERS.put(UNDERLINE_POSITION, new WritingDirNumberSetter(UNDERLINE_POSITION));
+        VALUE_PARSERS.put(UNDERLINE_THICKNESS, new WritingDirNumberSetter(UNDERLINE_THICKNESS));
+        VALUE_PARSERS.put(ITALIC_ANGLE, new WritingDirDoubleSetter(ITALIC_ANGLE));
+        VALUE_PARSERS.put(IS_FIXED_PITCH, new WritingDirBooleanSetter(IS_FIXED_PITCH));
+        VALUE_PARSERS.put(C, new IntegerSetter("CharCode"));
+        VALUE_PARSERS.put(CH, new NotImplementedYet(CH));
+        VALUE_PARSERS.put(WX, new DoubleSetter("WidthX"));
+        VALUE_PARSERS.put(W0X, new DoubleSetter("WidthX"));
+        VALUE_PARSERS.put(W1X, new NotImplementedYet(W1X));
+        VALUE_PARSERS.put(WY, new DoubleSetter("WidthY"));
+        VALUE_PARSERS.put(W0Y, new DoubleSetter("WidthY"));
+        VALUE_PARSERS.put(W1Y, new NotImplementedYet(W1Y));
+        VALUE_PARSERS.put(W, new NotImplementedYet(W));
+        VALUE_PARSERS.put(W0, new NotImplementedYet(W0));
+        VALUE_PARSERS.put(W1, new NotImplementedYet(W1));
+        VALUE_PARSERS.put(N, new StringSetter("CharName"));
+        VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN));
+        VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN));
+        VALUE_PARSERS.put(START_KERN_PAIRS1, new NotImplementedYet(START_KERN_PAIRS1));
+        VALUE_PARSERS.put(KP, new NotImplementedYet(KP));
+        VALUE_PARSERS.put(KPH, new NotImplementedYet(KPH));
+        VALUE_PARSERS.put(KPX, new KPXHandler());
+        VALUE_PARSERS.put(KPY, new NotImplementedYet(KPY));
+
+        PARSE_MODE_CHANGES = new java.util.HashMap();
+        PARSE_MODE_CHANGES.put(START_CHAR_METRICS, new Integer(PARSE_CHAR_METRICS));
+        PARSE_MODE_CHANGES.put(END_CHAR_METRICS, new Integer(PARSE_NORMAL));
+    }
+    
+    /**
+     * Main constructor.
+     */
+    public AFMParser() {
+        //nop
+    }
+
+    /**
+     * Parses an AFM file from a local file.
+     * @param afmFile the AFM file
+     * @return the parsed AFM file
+     * @throws IOException if an I/O error occurs
+     */
+    public AFMFile parse(File afmFile) throws IOException {
+        InputStream in = new java.io.FileInputStream(afmFile);
+        try {
+            return parse(in);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+    
+    /**
+     * Parses an AFM file from a stream.
+     * @param in the stream to read from
+     * @return the parsed AFM file
+     * @throws IOException if an I/O error occurs
+     */
+    public AFMFile parse(InputStream in) throws IOException {
+        Reader reader = new java.io.InputStreamReader(in, "US-ASCII");
+        try {
+            return parse(new BufferedReader(reader));
+        } finally {
+            IOUtils.closeQuietly(reader);
+        }
+    }
+
+    /**
+     * Parses an AFM file from a BufferedReader.
+     * @param reader the BufferedReader instance to read from
+     * @return the parsed AFM file
+     * @throws IOException if an I/O error occurs
+     */
+    public AFMFile parse(BufferedReader reader) throws IOException {
+        Stack stack = new Stack();
+        int parseMode = PARSE_NORMAL;
+        while (true) {
+            String line = reader.readLine();
+            if (line == null) {
+                break;
+            }
+            String key = null;
+            switch (parseMode) {
+            case PARSE_NORMAL:
+                key = parseLine(line, stack);
+                break;
+            case PARSE_CHAR_METRICS:
+                key = parseCharMetrics(line, stack);
+                break;
+            default:
+                throw new IllegalStateException("Invalid parse mode");
+            }
+            Integer newParseMode = (Integer)PARSE_MODE_CHANGES.get(key);
+            if (newParseMode != null) {
+                parseMode = newParseMode.intValue();
+            }
+        }
+        return (AFMFile)stack.pop();
+    }
+    
+    private String parseLine(String line, Stack stack) throws IOException {
+        int startpos = 0;
+        //Find key
+        startpos = skipToNonWhiteSpace(line, startpos);
+        int endpos = skipToWhiteSpace(line, startpos);
+        String key = line.substring(startpos, endpos);
+        
+        //Parse value
+        startpos = skipToNonWhiteSpace(line, endpos);
+        ValueHandler vp = (ValueHandler)VALUE_PARSERS.get(key);
+        if (vp != null) {
+            vp.parse(line, startpos, stack);
+        }
+        return key;
+    }
+    
+    private String parseCharMetrics(String line, Stack stack) throws IOException {
+        int startpos = 0;
+        AFMCharMetrics chm = new AFMCharMetrics();
+        stack.push(chm);
+        while (true) {
+            //Find key
+            startpos = skipToNonWhiteSpace(line, startpos);
+            int endpos = skipToWhiteSpace(line, startpos);
+            String key = line.substring(startpos, endpos);
+            if (END_CHAR_METRICS.equals(key)) {
+                stack.pop(); //Pop and forget unused AFMCharMetrics instance
+                return key;
+            } else if (key.length() == 0) {
+                //EOL: No more key so break
+                break;
+            }
+            
+            //Extract value
+            startpos = skipToNonWhiteSpace(line, endpos);
+            endpos = skipToSemicolon(line, startpos);
+            String value = line.substring(startpos, endpos).trim();
+            startpos = endpos + 1;
+            
+            //Parse value
+            ValueHandler vp = (ValueHandler)VALUE_PARSERS.get(key);
+            if (vp != null) {
+                vp.parse(value, 0, stack);
+            }
+            if (false) {
+                break;
+            }
+        }
+        stack.pop();
+        AFMFile afm = (AFMFile)stack.peek();
+        afm.addCharMetrics(chm);
+        return null;
+    }
+    
+    private static int skipToNonWhiteSpace(String line, int startpos) {
+        int pos = startpos;
+        while (pos < line.length() && isWhitespace(line.charAt(pos))) {
+            pos++;
+        }
+        return pos;
+    }
+
+    private static int skipToWhiteSpace(String line, int startpos) {
+        int pos = startpos;
+        while (pos < line.length() && !isWhitespace(line.charAt(pos))) {
+            pos++;
+        }
+        return pos;
+    }
+
+    private static int skipToSemicolon(String line, int startpos) {
+        int pos = startpos;
+        while (pos < line.length() && ';' != line.charAt(pos)) {
+            pos++;
+        }
+        return pos;
+    }
+
+    private static boolean isWhitespace(char ch) {
+        return ch == ' '
+            || ch == '\t';
+    }
+
+    // ---------------- Value Handlers ---------------------------
+    
+    private interface ValueHandler {
+        void parse(String line, int startpos, Stack stack) throws IOException;
+    }
+    
+    private abstract static class AbstractValueHandler implements ValueHandler {
+        
+        protected int findValue(String line, int startpos) {
+            return skipToWhiteSpace(line, startpos);
+        }
+        
+        protected String getStringValue(String line, int startpos) {
+            return line.substring(startpos);
+        }
+        
+        protected Number getNumberValue(String line, int startpos) {
+            try {
+                return new Integer(getIntegerValue(line, startpos));
+            } catch (NumberFormatException nfe) {
+                return new Double(getDoubleValue(line, startpos));
+            }
+        }
+        
+        protected int getIntegerValue(String line, int startpos) {
+            int endpos = findValue(line, startpos);
+            return Integer.parseInt(line.substring(startpos, endpos));
+        }
+        
+        protected double getDoubleValue(String line, int startpos) {
+            int endpos = findValue(line, startpos);
+            return Double.parseDouble(line.substring(startpos, endpos));
+        }
+        
+        protected Boolean getBooleanValue(String line, int startpos) {
+            return Boolean.valueOf(getStringValue(line, startpos));
+        }
+        
+    }
+    
+    private static class StartFontMetrics extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            int endpos = findValue(line, startpos);
+            double version = Double.parseDouble(line.substring(startpos, endpos));
+            if (version < 2) {
+                throw new IOException(
+                        "AFM version must be at least 2.0 but it is " + version + "!");
+            }
+            AFMFile afm = new AFMFile();
+            stack.push(afm);
+        }
+    }
+    
+    private abstract static class BeanSetter extends AbstractValueHandler {
+        private String method;
+        
+        public BeanSetter(String variable) {
+            this.method = "set" + variable;
+        }
+        
+        protected void setValue(Object target, Object value) {
+            //Uses Java Beans API
+            Statement statement = new Statement(target, method, new Object[] {value});
+            try {
+                statement.execute();
+            } catch (Exception e) {
+                //Should never happen
+                throw new RuntimeException("Bean error: " + e.getMessage());
+            }
+        }
+    }
+    
+    private static class StringSetter extends BeanSetter {
+        
+        public StringSetter(String variable) {
+            super(variable);
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            String s = getStringValue(line, startpos);
+            Object obj = stack.peek();
+            setValue(obj, s);
+        }
+    }
+    
+    private static class NumberSetter extends BeanSetter {
+        public NumberSetter(String variable) {
+            super(variable);
+        }
+        
+        protected Object getContextObject(Stack stack) {
+            return stack.peek();
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            Number num = getNumberValue(line, startpos);
+            setValue(getContextObject(stack), num);
+        }
+    }
+    
+    private static class IntegerSetter extends NumberSetter {
+        public IntegerSetter(String variable) {
+            super(variable);
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            int value = getIntegerValue(line, startpos);
+            setValue(getContextObject(stack), new Integer(value));
+        }
+    }
+    
+    private static class DoubleSetter extends NumberSetter {
+        public DoubleSetter(String variable) {
+            super(variable);
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            double value = getDoubleValue(line, startpos);
+            setValue(getContextObject(stack), new Double(value));
+        }
+    }
+    
+    private static class WritingDirNumberSetter extends NumberSetter {
+
+        public WritingDirNumberSetter(String variable) {
+            super(variable);
+        }
+        
+        protected Object getContextObject(Stack stack) {
+            if (stack.peek() instanceof AFMWritingDirectionMetrics) {
+                return (AFMWritingDirectionMetrics)stack.peek();
+            } else {
+                AFMFile afm = (AFMFile)stack.peek();
+                AFMWritingDirectionMetrics wdm = afm.getWritingDirectionMetrics(0);
+                if (wdm == null) {
+                    wdm = new AFMWritingDirectionMetrics();
+                    afm.setWritingDirectionMetrics(0, wdm);
+                }
+                return wdm;
+            }
+        }
+        
+    }
+    
+    private static class WritingDirDoubleSetter extends WritingDirNumberSetter {
+
+        public WritingDirDoubleSetter(String variable) {
+            super(variable);
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            double value = getDoubleValue(line, startpos);
+            setValue(getContextObject(stack), new Double(value));
+        }
+    }
+    
+    private static class BooleanSetter extends AbstractValueHandler {
+        private String method;
+        
+        public BooleanSetter(String variable) {
+            this.method = "set" + variable.substring(2); //Cut "Is" in front
+        }
+        
+        protected Object getContextObject(Stack stack) {
+            return (AFMFile)stack.peek();
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            Boolean b = getBooleanValue(line, startpos);
+            //Uses Java Beans API
+            Statement statement = new Statement(getContextObject(stack),
+                    method, new Object[] {b});
+            try {
+                statement.execute();
+            } catch (Exception e) {
+                //Should never happen
+                throw new RuntimeException("Bean error: " + e.getMessage());
+            }
+        }
+    }
+    
+    private static class WritingDirBooleanSetter extends BooleanSetter {
+
+        public WritingDirBooleanSetter(String variable) {
+            super(variable);
+        }
+        
+        protected Object getContextObject(Stack stack) {
+            if (stack.peek() instanceof AFMWritingDirectionMetrics) {
+                return (AFMWritingDirectionMetrics)stack.peek();
+            } else {
+                AFMFile afm = (AFMFile)stack.peek();
+                AFMWritingDirectionMetrics wdm = afm.getWritingDirectionMetrics(0);
+                if (wdm == null) {
+                    wdm = new AFMWritingDirectionMetrics();
+                    afm.setWritingDirectionMetrics(0, wdm);
+                }
+                return wdm;
+            }
+        }
+        
+    }
+    
+    private static class FontBBox extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            AFMFile afm = (AFMFile)stack.peek();
+            Rectangle rect = new Rectangle();
+            int endpos;
+            
+            endpos = findValue(line, startpos);
+            rect.x = Integer.parseInt(line.substring(startpos, endpos));
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            endpos = findValue(line, startpos);
+            rect.y = Integer.parseInt(line.substring(startpos, endpos));
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            endpos = findValue(line, startpos);
+            int v = Integer.parseInt(line.substring(startpos, endpos));
+            rect.width = v - rect.x;
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            endpos = findValue(line, startpos);
+            v = Integer.parseInt(line.substring(startpos, endpos));
+            rect.height = v - rect.y;
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            afm.setFontBBox(rect);
+        }
+    }
+    
+    private static class IsBaseFont extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            if (getBooleanValue(line, startpos).booleanValue()) {
+                throw new IOException("Only base fonts are currently supported!");
+            }
+        }
+    }
+    
+    private static class IsCIDFont extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            if (getBooleanValue(line, startpos).booleanValue()) {
+                throw new IOException("CID fonts are currently not supported!");
+            }
+        }
+    }
+    
+    private static class NotImplementedYet extends AbstractValueHandler {
+        private String key;
+        
+        public NotImplementedYet(String key) {
+            this.key = key;
+        }
+        
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            throw new IOException("Support for '" + key
+                    + "' has not been implemented, yet! Font is not supported.");
+        }
+    }
+    
+    private static class StartDirection extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            int index = getIntegerValue(line, startpos);
+            AFMWritingDirectionMetrics wdm = new AFMWritingDirectionMetrics();
+            AFMFile afm = (AFMFile)stack.peek();
+            afm.setWritingDirectionMetrics(index, wdm);
+            stack.push(wdm);
+        }
+    }
+    
+    private static class EndDirection extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            if (!(stack.pop() instanceof AFMWritingDirectionMetrics)) {
+                throw new IOException("AFM format error: nesting incorrect");
+            }
+        }
+    }
+    
+    private static class KPXHandler extends AbstractValueHandler {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            AFMFile afm = (AFMFile)stack.peek();
+            int endpos;
+            
+            endpos = findValue(line, startpos);
+            String name1 = line.substring(startpos, endpos);
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            endpos = findValue(line, startpos);
+            String name2 = line.substring(startpos, endpos);
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            endpos = findValue(line, startpos);
+            double kx = Double.parseDouble(line.substring(startpos, endpos));
+            startpos = skipToNonWhiteSpace(line, endpos);
+            
+            afm.addXKerning(name1, name2, kx);
+        }
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java?rev=627679&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java Thu Feb 14 00:12:34 2008
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.type1;
+
+/**
+ * Represents a writing direction metrics section from an AFM file.
+ */
+public class AFMWritingDirectionMetrics {
+
+    private Number underlinePosition;
+    private Number underlineThickness;
+    private double italicAngle;
+    private boolean isFixedPitch;
+    
+    /**
+     * Returns the UnderlinePosition value.
+     * @return the underlinePosition
+     */
+    public Number getUnderlinePosition() {
+        return underlinePosition;
+    }
+    
+    /**
+     * Sets the UnderlinePosition value. 
+     * @param underlinePosition the underlinePosition to set
+     */
+    public void setUnderlinePosition(Number underlinePosition) {
+        this.underlinePosition = underlinePosition;
+    }
+    
+    /**
+     * Returns the UnderlineThickness value.
+     * @return the underlineThickness
+     */
+    public Number getUnderlineThickness() {
+        return underlineThickness;
+    }
+    
+    /**
+     * Sets the UnderlineThickness value.
+     * @param underlineThickness the underlineThickness to set
+     */
+    public void setUnderlineThickness(Number underlineThickness) {
+        this.underlineThickness = underlineThickness;
+    }
+    
+    /**
+     * Returns the ItalicAngle value.
+     * @return the italicAngle
+     */
+    public double getItalicAngle() {
+        return italicAngle;
+    }
+    
+    /**
+     * Sets the ItalicAngle value.
+     * @param italicAngle the italicAngle to set
+     */
+    public void setItalicAngle(double italicAngle) {
+        this.italicAngle = italicAngle;
+    }
+    
+    /**
+     * Returns the IsFixedPitch value.
+     * @return the isFixedPitch
+     */
+    public boolean isFixedPitch() {
+        return isFixedPitch;
+    }
+    
+    /**
+     * Set the IsFixedPitch value.
+     * @param value the isFixedPitch to set
+     */
+    public void setFixedPitch(boolean value) {
+        this.isFixedPitch = value;
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMWritingDirectionMetrics.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java Thu Feb 14 00:12:34 2008
@@ -21,8 +21,13 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.fonts.CodePointMapping;
 import org.apache.fop.fonts.FontLoader;
 import org.apache.fop.fonts.FontResolver;
 import org.apache.fop.fonts.FontType;
@@ -33,27 +38,88 @@
  */
 public class Type1FontLoader extends FontLoader {
 
-    private PFMFile pfm;
     private SingleByteFont singleFont;
     
     /**
      * Constructs a new Type 1 font loader.
      * @param fontFileURI the URI to the PFB file of a Type 1 font
-     * @param in the InputStream reading the PFM file of a Type 1 font
      * @param resolver the font resolver used to resolve URIs
      * @throws IOException In case of an I/O error
      */
-    public Type1FontLoader(String fontFileURI, InputStream in, FontResolver resolver) 
+    public Type1FontLoader(String fontFileURI, FontResolver resolver) 
                 throws IOException {
-        super(fontFileURI, in, resolver);
+        super(fontFileURI, resolver);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    private String getPFMURI(String pfbURI) {
+        String pfbExt = pfbURI.substring(pfbURI.length() - 3, pfbURI.length());
+        String pfmExt = pfbExt.substring(0, 2)
+                + (Character.isUpperCase(pfbExt.charAt(2)) ? "M" : "m");
+        return pfbURI.substring(0, pfbURI.length() - 4) + "." + pfmExt;
+    }
+    
+    private static final String[] AFM_EXTENSIONS = new String[] {".AFM", ".afm", ".Afm"};
+    
+    /** {@inheritDoc} */
     protected void read() throws IOException {
-        pfm = new PFMFile();
-        pfm.load(in);
+        AFMFile afm = null;
+        PFMFile pfm = null;
+        
+        InputStream afmIn = null;
+        for (int i = 0; i < AFM_EXTENSIONS.length; i++) {
+            try {
+                String afmUri = this.fontFileURI.substring(0, this.fontFileURI.length() - 4)
+                        + AFM_EXTENSIONS[i];
+                afmIn = openFontUri(resolver, afmUri);
+                if (afmIn != null) {
+                    break;
+                }
+            } catch (IOException ioe) {
+                //Ignore, AFM probably not available under the URI
+            }
+        }
+        if (afmIn != null) {
+            try {
+                AFMParser afmParser = new AFMParser();
+                afm = afmParser.parse(afmIn);
+            } finally {
+                IOUtils.closeQuietly(afmIn);
+            }
+        }
+        
+        String pfmUri = getPFMURI(this.fontFileURI);
+        InputStream pfmIn = null;
+        try {
+            pfmIn = openFontUri(resolver, pfmUri);
+        } catch (IOException ioe) {
+            //Ignore, PFM probably not available under the URI
+        }
+        if (pfmIn != null) {
+            try {
+                pfm = new PFMFile();
+                pfm.load(pfmIn);
+            } finally {
+                IOUtils.closeQuietly(pfmIn);
+            }
+        }
+        
+        if (afm == null && pfm == null) {
+            throw new java.io.FileNotFoundException(
+                    "Neither an AFM nor a PFM file was found for " + this.fontFileURI);
+        }
+        if (pfm == null) {
+            //Cannot do without for now
+            throw new java.io.FileNotFoundException(
+                    "No PFM file was found for " + this.fontFileURI);
+        }
+        buildFont(afm, pfm);
+        this.loaded = true;
+    }
+
+    private void buildFont(AFMFile afm, PFMFile pfm) {
+        if (afm == null && pfm == null) {
+            throw new IllegalArgumentException("Need at least an AFM or a PFM!");
+        }
         singleFont = new SingleByteFont();
         singleFont.setFontType(FontType.TYPE1);
         if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
@@ -65,28 +131,127 @@
         }
         singleFont.setResolver(this.resolver);
         returnFont = singleFont;
-        returnFont.setFontName(pfm.getPostscriptName());
-        String fullName = pfm.getPostscriptName();
-        fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
-        returnFont.setFullName(fullName); //should be afm.getFullName()!!
-        //TODO not accurate: we need FullName from the AFM file but we don't have an AFM parser
-        Set names = new java.util.HashSet();
-        names.add(pfm.getWindowsName()); //should be afm.getFamilyName()!!
-        returnFont.setFamilyNames(names);
-        returnFont.setCapHeight(pfm.getCapHeight());
-        returnFont.setXHeight(pfm.getXHeight());
-        returnFont.setAscender(pfm.getLowerCaseAscent());
-        returnFont.setDescender(pfm.getLowerCaseDescent());
-        returnFont.setFontBBox(pfm.getFontBBox());
+        
+        //Font name
+        if (afm != null) {
+            returnFont.setFontName(afm.getFontName()); //PostScript font name
+            returnFont.setFullName(afm.getFullName());
+            Set names = new java.util.HashSet();
+            names.add(afm.getFamilyName());
+            returnFont.setFamilyNames(names);
+        } else {
+            returnFont.setFontName(pfm.getPostscriptName());
+            String fullName = pfm.getPostscriptName();
+            fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
+            returnFont.setFullName(fullName); //emulate afm.getFullName()
+            Set names = new java.util.HashSet();
+            names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
+            returnFont.setFamilyNames(names);
+        }
+        
+        //Encoding
+        if (afm != null) {
+            String encoding = afm.getEncodingScheme();
+            if ("AdobeStandardEncoding".equals(encoding)) {
+                //Use WinAnsi in this case as it better fits the usual character set people need
+                singleFont.setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
+            } else {
+                String effEncodingName;
+                if ("FontSpecific".equals(encoding)) {
+                    effEncodingName = afm.getFontName() + "Encoding";
+                } else {
+                    effEncodingName = encoding;
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug("Unusual font encoding encountered: "
+                            + encoding + " -> " + effEncodingName);
+                }
+                CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm);
+                singleFont.setEncoding(mapping);
+            }
+        }
+        
+        //Basic metrics
+        if (afm != null) {
+            if (afm.getCapHeight() != null) {
+                returnFont.setCapHeight(afm.getCapHeight().intValue());
+            }
+            if (afm.getXHeight() != null) {
+                returnFont.setXHeight(afm.getXHeight().intValue());
+            }
+            if (afm.getAscender() != null) {
+                returnFont.setAscender(afm.getAscender().intValue());
+            }
+            if (afm.getDescender() != null) {
+                returnFont.setDescender(afm.getDescender().intValue());
+            }
+            returnFont.setFontBBox(afm.getFontBBoxAsIntArray());
+            if (afm.getStdVW() != null) {
+                returnFont.setStemV(afm.getStdVW().intValue());
+            } else {
+                returnFont.setStemV(80); //Arbitrary value
+            }
+            returnFont.setItalicAngle((int)afm.getWritingDirectionMetrics(0).getItalicAngle());
+        } else {
+            returnFont.setFontBBox(pfm.getFontBBox());
+            returnFont.setStemV(pfm.getStemV());
+            returnFont.setItalicAngle(pfm.getItalicAngle());
+        }
+        if (pfm != null) {
+            if (returnFont.getCapHeight() == 0) {
+                returnFont.setCapHeight(pfm.getCapHeight());
+            }
+            if (returnFont.getXHeight(1) == 0) {
+                returnFont.setXHeight(pfm.getXHeight());
+            }
+            if (returnFont.getAscender() == 0) {
+                returnFont.setAscender(pfm.getLowerCaseAscent());
+            }
+            if (returnFont.getDescender() == 0) {
+                returnFont.setDescender(pfm.getLowerCaseDescent());
+            }
+        }
         returnFont.setFirstChar(pfm.getFirstChar());
         returnFont.setLastChar(pfm.getLastChar());
         returnFont.setFlags(pfm.getFlags());
-        returnFont.setStemV(pfm.getStemV());
-        returnFont.setItalicAngle(pfm.getItalicAngle());
         returnFont.setMissingWidth(0);
         for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
             singleFont.setWidth(i, pfm.getCharWidth(i));
         }
+        returnFont.replaceKerningMap(pfm.getKerning());
         singleFont.setEmbedFileName(this.fontFileURI);
+    }
+
+    private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) {
+        List chars = afm.getCharMetrics();
+        int mappingCount = 0;
+        //Just count the first time...
+        Iterator iter = chars.iterator();
+        while (iter.hasNext()) {
+            AFMCharMetrics charMetrics = (AFMCharMetrics)iter.next();
+            if (charMetrics.getCharCode() >= 0) {
+                String u = charMetrics.getUnicodeChars();
+                if (u != null) {
+                    mappingCount += u.length();
+                }
+            }
+        }
+        //...and now build the table.
+        int[] table = new int[mappingCount * 2];
+        iter = chars.iterator();
+        int idx = 0;
+        while (iter.hasNext()) {
+            AFMCharMetrics charMetrics = (AFMCharMetrics)iter.next();
+            if (charMetrics.getCharCode() >= 0) {
+                String unicodes = charMetrics.getUnicodeChars();
+                for (int i = 0, c = unicodes.length(); i < c; i++) {
+                    table[idx] = charMetrics.getCharCode();
+                    idx++;
+                    table[idx] = unicodes.charAt(i);
+                    idx++;
+                }
+            }
+        }
+        return new CodePointMapping(encodingName, table);
     }
 }

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java?rev=627679&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java Thu Feb 14 00:12:34 2008
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+ 
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public class CMapBuilder {
+    
+    protected String name;
+    protected Writer writer;
+    
+    public CMapBuilder(Writer writer, String name) {
+        this.writer = writer;
+        this.name = name;
+    }
+    
+    /**
+     * Writes the CMap to a Writer.
+     * @throws IOException if an I/O error occurs
+     */
+    public void writeCMap() throws IOException {
+        writePreStream();
+        writeStreamComments();
+        writeCIDInit();
+        writeCIDSystemInfo();
+        writeVersion("1");
+        writeType("1");
+        writeName(name);
+        writeCodeSpaceRange();
+        writeCIDRange();
+        writeBFEntries();
+        writeWrapUp();
+        writeStreamAfterComments();
+        writeUseCMap();
+    }
+    
+    protected void writePreStream() throws IOException {
+        // writer.write("/Type /CMap\n");
+        // writer.write(sysInfo.toPDFString());
+        // writer.write("/CMapName /" + name + EOL);
+    }
+
+    protected void writeStreamComments() throws IOException {
+        writer.write("%!PS-Adobe-3.0 Resource-CMap\n");
+        writer.write("%%DocumentNeededResources: ProcSet (CIDInit)\n");
+        writer.write("%%IncludeResource: ProcSet (CIDInit)\n");
+        writer.write("%%BeginResource: CMap (" + name + ")\n");
+        writer.write("%%EndComments\n");
+    }
+
+    protected void writeCIDInit() throws IOException {
+        writer.write("/CIDInit /ProcSet findresource begin\n");
+        writer.write("12 dict begin\n");
+        writer.write("begincmap\n");
+    }
+
+    protected void writeCIDSystemInfo(String registry, String ordering, int supplement)
+                throws IOException {
+        writer.write("/CIDSystemInfo 3 dict dup begin\n");
+        writer.write("  /Registry (");
+        writer.write(registry);
+        writer.write(") def\n");
+        writer.write("  /Ordering (");
+        writer.write(ordering);
+        writer.write(") def\n");
+        writer.write("  /Supplement ");
+        writer.write(Integer.toString(supplement));
+        writer.write(" def\n");
+        writer.write("end def\n");
+    }
+    
+    protected void writeCIDSystemInfo() throws IOException {
+        writeCIDSystemInfo("Adobe", "Identity", 0);
+    }
+
+    protected void writeVersion(String version) throws IOException {
+        writer.write("/CMapVersion ");
+        writer.write(version);
+        writer.write(" def\n");
+    }
+
+    protected void writeType(String type) throws IOException {
+        writer.write("/CMapType ");
+        writer.write(type);
+        writer.write(" def\n");
+    }
+
+    protected void writeName(String name) throws IOException {
+        writer.write("/CMapName /");
+        writer.write(name);
+        writer.write(" def\n");
+    }
+
+    protected void writeCodeSpaceRange() throws IOException {
+        writer.write("1 begincodespacerange\n");
+        writer.write("<0000> <FFFF>\n");
+        writer.write("endcodespacerange\n");
+    }
+
+    protected void writeCIDRange() throws IOException {
+        writer.write("1 begincidrange\n");
+        writer.write("<0000> <FFFF> 0\n");
+        writer.write("endcidrange\n");
+    }
+
+    protected void writeBFEntries() throws IOException {
+        // writer.write("1 beginbfrange\n");
+        // writer.write("<0020> <0100> <0000>\n");
+        // writer.write("endbfrange\n");
+    }
+
+    protected void writeWrapUp() throws IOException {
+        writer.write("endcmap\n");
+        writer.write("CMapName currentdict /CMap defineresource pop\n");
+        writer.write("end\n");
+        writer.write("end\n");
+    }
+
+    protected void writeStreamAfterComments() throws IOException {
+        writer.write("%%EndResource\n");
+        writer.write("%%EOF\n");
+    }
+
+    protected void writeUseCMap() {
+        /*
+         * writer.write(" /Type /CMap");
+         * writer.write("/CMapName /" + name + EOL);
+         * writer.write("/WMode " + wMode + EOL);
+         * if (base != null) {
+         *     writer.write("/UseCMap ");
+         * if (base instanceof String) {
+         * writer.write("/"+base);
+         * } else {// base instanceof PDFStream
+         * writer.write(((PDFStream)base).referencePDF());
+         * }
+         * }
+         */
+    }
+}
\ No newline at end of file

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/CMapBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java?rev=627679&r1=627678&r2=627679&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java Thu Feb 14 00:12:34 2008
@@ -147,7 +147,10 @@
      */
     public void add(Object obj) {
         if (obj instanceof PDFObject) {
-            ((PDFObject)obj).setParent(this);
+            PDFObject pdfObj = (PDFObject)obj;
+            if (!pdfObj.hasObjectNumber()) {
+                pdfObj.setParent(this);
+            }
         }
         this.values.add(obj);
     }



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org