Index: conf/fop.xconf
===================================================================
--- conf/fop.xconf	(revision 900314)
+++ conf/fop.xconf	(working copy)
@@ -396,6 +396,35 @@
           <font-triplet name="Courier" style="italic" weight="bold"/>
           <font-triplet name="monospace" style="italic" weight="bold"/>
         </font>
+        
+         <!-- 
+        Configure double-byte (CID Keyed font (Type 0))AFP fonts with type="double-byte".  
+        This type is associated with the optional attributes  @fallback-type and @fallback-value.
+        
+        @fallback-* specify a fallback for substituting the width metric of characters that lack 
+        	width metric definition in the font resource.
+        	fallback-type={"em", "char"} - default "em".
+        	
+        	The meaning and value of @fallback-value depends upon the value of @fallback-type as follows:
+        	
+        	1-	fallback-type="em" (fallback-value ignored)
+        		-	Use the em width of the font.
+        		
+        	2-	fallback-type="char" fallback-value="X" 
+        		-	where X is the unicode codepoint (integert) of the character
+        			in decimal or hexidecimal (u+xxxx) form.
+        		Declares a substitute character to use for the fallback width metric.
+        			
+        	NOTE no fallback declaration defaults to fallback-type="em"
+        
+        example:
+         <font>
+        	<afp-font type="double-byte" encoding="UnicodeBigUnmarked"  
+        	codepage="T1120000" characterset="CZJHMNU" 
+        	base-uri="fonts" [fallback-type="char" fallback-value="u+20"] />
+         	<font-triplet name="J-Heisei Mincho" style="normal" weight="normal" />
+     	 </font>
+        -->
       </fonts>
     </renderer>
 
Index: src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
===================================================================
--- src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java	(revision 900314)
+++ src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java	(working copy)
@@ -30,9 +30,12 @@
 
 import org.apache.fop.afp.AFPResourceLevel;
 import org.apache.fop.afp.AFPResourceLevelDefaults;
+import org.apache.fop.afp.fonts.AFPFont;
 import org.apache.fop.afp.fonts.AFPFontCollection;
 import org.apache.fop.afp.fonts.AFPFontInfo;
+import org.apache.fop.afp.fonts.AFPFontReader;
 import org.apache.fop.afp.fonts.CharacterSet;
+import org.apache.fop.afp.fonts.DoubleByteFont;
 import org.apache.fop.afp.fonts.FopCharacterSet;
 import org.apache.fop.afp.fonts.OutlineFont;
 import org.apache.fop.afp.fonts.RasterFont;
@@ -57,7 +60,7 @@
  * AFP Renderer configurator
  */
 public class AFPRendererConfigurator extends PrintRendererConfigurator
-            implements IFDocumentHandlerConfigurator {
+implements IFDocumentHandlerConfigurator {
 
     /**
      * Default constructor
@@ -69,7 +72,7 @@
     }
 
     private AFPFontInfo buildFont(Configuration fontCfg, String fontPath)
-            throws ConfigurationException {
+    throws ConfigurationException {
 
         FontManager fontManager = this.userAgent.getFactory().getFontManager();
 
@@ -127,11 +130,33 @@
             return null;
         }
         String encoding = afpFontCfg.getAttribute("encoding");
+
         if (encoding == null) {
             log.error("Mandatory afp-font configuration attribute 'encoding=' is missing");
             return null;
         }
 
+        AFPFont  font = fontFromType(type, codepage, encoding, accessor, afpFontCfg);
+
+        return font != null ? new AFPFontInfo(font, tripletList) : null;
+    }
+
+
+    /**
+     * Create the AFPFont based on type and type-dependent configuration.
+     *
+     * @param type font type e.g. 'raster', 'outline'
+     * @param codepage codepage file
+     * @param encoding character encoding e.g. 'Cp500', 'UnicodeBigUnmarked'
+     * @param accessor
+     * @param afpFontCfg
+     * @return
+     * @throws ConfigurationException
+     */
+    private AFPFont fontFromType(String type, String codepage, String encoding,
+            ResourceAccessor accessor, Configuration afpFontCfg)
+    throws ConfigurationException {
+
         if ("raster".equalsIgnoreCase(type)) {
 
             String name = afpFontCfg.getAttribute("name", "Unknown");
@@ -161,27 +186,27 @@
                 if (base14 != null) {
                     try {
                         Class clazz = Class.forName("org.apache.fop.fonts.base14."
-                            + base14);
+                                + base14);
                         try {
                             Typeface tf = (Typeface)clazz.newInstance();
                             font.addCharacterSet(sizeMpt, new FopCharacterSet(
-                                codepage, encoding, characterset, tf));
+                                    codepage, encoding, characterset, tf));
                         } catch (Exception ie) {
                             String msg = "The base 14 font class " + clazz.getName()
-                                + " could not be instantiated";
+                            + " could not be instantiated";
                             log.error(msg);
                         }
                     } catch (ClassNotFoundException cnfe) {
                         String msg = "The base 14 font class for " + characterset
-                            + " could not be found";
+                        + " could not be found";
                         log.error(msg);
                     }
                 } else {
                     font.addCharacterSet(sizeMpt, new CharacterSet(
-                        codepage, encoding, characterset, accessor));
+                            codepage, encoding, characterset, accessor));
                 }
             }
-            return new AFPFontInfo(font, tripletList);
+            return font;
 
         } else if ("outline".equalsIgnoreCase(type)) {
             String characterset = afpFontCfg.getAttribute("characterset");
@@ -195,30 +220,83 @@
             if (base14 != null) {
                 try {
                     Class clazz = Class.forName("org.apache.fop.fonts.base14."
-                        + base14);
+                            + base14);
                     try {
                         Typeface tf = (Typeface)clazz.newInstance();
                         characterSet = new FopCharacterSet(
                                 codepage, encoding, characterset, tf);
                     } catch (Exception ie) {
                         String msg = "The base 14 font class " + clazz.getName()
-                            + " could not be instantiated";
+                        + " could not be instantiated";
                         log.error(msg);
                     }
                 } catch (ClassNotFoundException cnfe) {
                     String msg = "The base 14 font class for " + characterset
-                        + " could not be found";
+                    + " could not be found";
                     log.error(msg);
                 }
             } else {
                 characterSet = new CharacterSet(codepage, encoding, characterset, accessor);
             }
+            // Return new font object
+            return new OutlineFont(name, characterSet);
+
+        } else if ("double-byte".equalsIgnoreCase(type)) {
+            String characterset = afpFontCfg.getAttribute("characterset");
+            if (characterset == null) {
+                log.error("Mandatory afp-font configuration attribute 'characterset=' is missing");
+                return null;
+            }
+            String name = afpFontCfg.getAttribute("name", characterset);
+            CharacterSet characterSet = null;
+            characterSet = new CharacterSet(codepage, encoding, characterset, accessor);
+            characterSet.setAFPFontReader(AFPFontReader.getDoubleByteInstance());
+
+
             // Create a new font object
-            OutlineFont font = new OutlineFont(name, characterSet);
-            return new AFPFontInfo(font, tripletList);
+            DoubleByteFont font;
+
+            String fallbackTypeAttr = afpFontCfg.getAttribute("fallback-type", null);
+
+            if (fallbackTypeAttr != null) {
+                String fallbackValueAttr = afpFontCfg.getAttribute("fallback-value", null);
+                if (fallbackTypeAttr.equalsIgnoreCase("char") && fallbackValueAttr != null) {
+
+                    try {
+                        int fallbackChar;
+                        if (fallbackValueAttr.matches("[Uu]\\+.*")) {
+                            fallbackChar = Integer.parseInt(fallbackValueAttr.substring(2), 16);
+                        } else {
+                            fallbackChar = Integer.parseInt(fallbackValueAttr);
+                        }
+
+                        font = new DoubleByteFont(name, characterSet, fallbackChar);
+                    } catch (NumberFormatException e) {
+                        font = new DoubleByteFont(name, characterSet);
+                        log.warn("The value of the fallback-value attribute is badly formed: '"
+                                + fallbackValueAttr + "' not integral.");
+                    }
+                } else if (fallbackTypeAttr.equalsIgnoreCase("em")) {
+                    //  use the em value
+                    font = new DoubleByteFont(name, characterSet);
+                } else {
+
+                    log.warn("The value of the fallback-type attribute  '"
+                            + fallbackTypeAttr + "' is not recognised.");
+                    font = new DoubleByteFont(name, characterSet);
+                }
+
+            } else {
+                font = new DoubleByteFont(name, characterSet);
+            }
+
+
+            return font;
+
         } else {
             log.error("No or incorrect type attribute");
         }
+
         return null;
     }
 
@@ -230,7 +308,7 @@
      * @throws ConfigurationException if something's wrong with the config data
      */
     private List/*<AFPFontInfo>*/ buildFontListFromConfiguration(Configuration cfg)
-            throws FOPException, ConfigurationException {
+    throws FOPException, ConfigurationException {
 
         Configuration fonts = cfg.getChild("fonts");
         FontManager fontManager = this.userAgent.getFactory().getFontManager();
@@ -261,9 +339,9 @@
                     FontTriplet triplet = (FontTriplet) fontTriplets.get(j);
                     if (log.isDebugEnabled()) {
                         log.debug("  Font triplet "
-                                  + triplet.getName() + ", "
-                                  + triplet.getStyle() + ", "
-                                  + triplet.getWeight());
+                                + triplet.getName() + ", "
+                                + triplet.getStyle() + ", "
+                                + triplet.getWeight());
                     }
 
                     if ((referencedFontsMatcher != null && referencedFontsMatcher.matches(triplet))
@@ -346,7 +424,7 @@
 
         // a default external resource group file setting
         Configuration resourceGroupFileCfg
-            = cfg.getChild("resource-group-file", false);
+        = cfg.getChild("resource-group-file", false);
         if (resourceGroupFileCfg != null) {
             String resourceGroupDest = null;
             try {
@@ -358,14 +436,15 @@
                         customizable.setDefaultResourceGroupFilePath(resourceGroupDest);
                     } else {
                         log.warn("Unable to write to default external resource group file '"
-                                    + resourceGroupDest + "'");
+                                + resourceGroupDest + "'");
                     }
                 }
             } catch (ConfigurationException e) {
                 LogUtil.handleException(log, e,
                         userAgent.getFactory().validateUserConfigStrictly());
             } catch (IOException ioe) {
-                throw new FOPException("Could not create default external resource group file", ioe);
+                throw new FOPException("Could not create default external resource group file"
+                            , ioe);
             }
         }
 
@@ -401,7 +480,7 @@
 
     /** {@inheritDoc} */
     public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
-            throws FOPException {
+    throws FOPException {
         FontManager fontManager = userAgent.getFactory().getFontManager();
         List fontCollections = new java.util.ArrayList();
 
Index: src/java/org/apache/fop/afp/modca/MapCodedFont.java
===================================================================
--- src/java/org/apache/fop/afp/modca/MapCodedFont.java	(revision 900314)
+++ src/java/org/apache/fop/afp/modca/MapCodedFont.java	(working copy)
@@ -29,6 +29,7 @@
 import org.apache.fop.afp.AFPConstants;
 import org.apache.fop.afp.fonts.AFPFont;
 import org.apache.fop.afp.fonts.CharacterSet;
+import org.apache.fop.afp.fonts.DoubleByteFont;
 import org.apache.fop.afp.fonts.FontRuntimeException;
 import org.apache.fop.afp.fonts.OutlineFont;
 import org.apache.fop.afp.fonts.RasterFont;
@@ -217,6 +218,24 @@
                         AFPConstants.EBCIDIC_ENCODING)
                         + " must have a fixed length of 8 characters.");
                 }
+            }  else if (font instanceof DoubleByteFont) {
+                DoubleByteFont outline = (DoubleByteFont) font;
+                CharacterSet cs = outline.getCharacterSet();
+                fontDefinition.characterSet = cs.getNameBytes();
+
+                // There are approximately 72 points to 1 inch or 20 1440ths per point.
+
+                fontDefinition.scale = 20 * size / 1000;
+
+                fontDefinition.codePage = cs.getCodePage().getBytes(
+                    AFPConstants.EBCIDIC_ENCODING);
+
+                if (fontDefinition.codePage.length != 8) {
+                    throw new IllegalArgumentException("The code page "
+                        + new String(fontDefinition.codePage,
+                        AFPConstants.EBCIDIC_ENCODING)
+                        + " must have a fixed length of 8 characters.");
+                }
             } else {
                 String msg = "Font of type " + font.getClass().getName()
                     + " not recognized.";
Index: src/java/org/apache/fop/afp/fonts/OutlineFont.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/OutlineFont.java	(revision 900314)
+++ src/java/org/apache/fop/afp/fonts/OutlineFont.java	(working copy)
@@ -21,163 +21,17 @@
 
 
 /**
- * A font defined as a set of lines and curves as opposed to a bitmap font. An
- * outline font can be scaled to any size and otherwise transformed more easily
- * than a bitmap font, and with more attractive results. <p/>
+ * Default implementation of AbstractOutlineFont.
  *
  */
-public class OutlineFont extends AFPFont {
+public class OutlineFont extends AbstractOutlineFont {
 
-    /** The character set for this font */
-    private CharacterSet charSet = null;
-
-    /**
-     * Constructor for an outline font.
-     *
-     * @param name
-     *            the name of the font
-     * @param charSet
-     *            the chracter set
-     */
+    /** {@inheritDoc} */
     public OutlineFont(String name, CharacterSet charSet) {
-        super(name);
-        this.charSet = charSet;
-    }
+        super(name, charSet);
 
-    /**
-     * Get the character set metrics.
-     *
-     * @return the character set
-     */
-    public CharacterSet getCharacterSet() {
-
-        return charSet;
-
     }
 
-    /**
-     * Get the character set metrics.
-     * @param size ignored
-     * @return the character set
-     */
-    public CharacterSet getCharacterSet(int size) {
 
-        return charSet;
 
-    }
-
-    /**
-     * Get the first character in this font.
-     * @return the first character in this font
-     */
-    public int getFirstChar() {
-        return charSet.getFirstChar();
-    }
-
-    /**
-     * Get the last character in this font.
-     * @return the last character in this font
-     */
-    public int getLastChar() {
-        return charSet.getLastChar();
-    }
-
-    /**
-     * The ascender is the part of a lowercase letter that extends above the
-     * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
-     * used to denote the part of the letter extending above the x-height.
-     *
-     * @param size the font size (in mpt)
-     * @return the ascender for the given size
-     */
-    public int getAscender(int size) {
-        return charSet.getAscender() * size;
-    }
-
-    /**
-     * Obtains the height of capital letters for the specified point size.
-     *
-     * @param size the font size (in mpt)
-     * @return the cap height for the given size
-     */
-    public int getCapHeight(int size) {
-        return charSet.getCapHeight() * size;
-    }
-
-    /**
-     * The descender is the part of a lowercase letter that extends below the
-     * base line, such as "g", "j", or "p". Also used to denote the part of the
-     * letter extending below the base line.
-     *
-     * @param size the font size (in mpt)
-     * @return the descender for the given size
-     */
-    public int getDescender(int size) {
-        return charSet.getDescender() * size;
-    }
-
-    /**
-     * The "x-height" (the height of the letter "x").
-     *
-     * @param size the font size (in mpt)
-     * @return the x height for the given size
-     */
-    public int getXHeight(int size) {
-        return charSet.getXHeight() * size;
-    }
-
-    /**
-     * Obtain the width of the character for the specified point size.
-     * @param character the character
-     * @param size the font size (in mpt)
-     * @return the width of the character for the specified point size
-     */
-    public int getWidth(int character, int size) {
-        return charSet.getWidth(character) * size;
-    }
-
-    /**
-     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
-     * character set.
-     *
-     * @param size the font size (in mpt)
-     * @return the widths of all characters
-     */
-    public int[] getWidths(int size) {
-        int[] widths =  charSet.getWidths();
-        for (int i = 0; i < widths.length; i++) {
-            widths[i] = widths[i] * size;
-        }
-        return widths;
-    }
-
-    /**
-     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
-     * character set.
-     *
-     * @return the widths of all characters
-     */
-    public int[] getWidths() {
-        return getWidths(1000);
-    }
-
-    /** {@inheritDoc} */
-    public boolean hasChar(char c) {
-        return charSet.hasChar(c);
-    }
-
-    /**
-     * Map a Unicode character to a code point in the font.
-     * @param c character to map
-     * @return the mapped character
-     */
-    public char mapChar(char c) {
-        return charSet.mapChar(c);
-    }
-
-    /** {@inheritDoc} */
-    public String getEncodingName() {
-        return charSet.getEncoding();
-    }
-
 }
\ No newline at end of file
Index: src/java/org/apache/fop/afp/fonts/CharacterSet.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/CharacterSet.java	(revision 900314)
+++ src/java/org/apache/fop/afp/fonts/CharacterSet.java	(working copy)
@@ -60,7 +60,7 @@
 public class CharacterSet {
 
     /** Static logging instance */
-    protected static final Log log = LogFactory.getLog(CharacterSet.class.getName());
+    protected static final Log LOG = LogFactory.getLog(CharacterSet.class.getName());
 
     /** default codepage */
     public static final String DEFAULT_CODEPAGE = "T1V10500";
@@ -98,6 +98,9 @@
     /** The nominal vertical size (in millipoints) for bitmap fonts. 0 for outline fonts. */
     private int nominalVerticalSize = 0;
 
+    /** The AFPFontReader for extracting font metrics */
+    private AFPFontReader afpFontReader = AFPFontReader.getInstance();
+
     /**
      * Constructor for the CharacterSetMetric object, the character set is used
      * to load the font information from the actual AFP font.
@@ -126,7 +129,7 @@
         if (name.length() > MAX_NAME_LEN) {
             String msg = "Character set name '" + name + "' must be a maximum of "
                 + MAX_NAME_LEN + " characters";
-            log.error("Constructor:: " + msg);
+            LOG.error("Constructor:: " + msg);
             throw new IllegalArgumentException(msg);
         }
 
@@ -286,13 +289,12 @@
      */
     private void load() {
         if (!isMetricsLoaded) {
-            AFPFontReader afpFontReader = new AFPFontReader();
             try {
                 afpFontReader.loadCharacterSetMetric(this);
                 isMetricsLoaded = true;
             } catch (IOException e) {
                 String msg = "Failed to load the character set metrics for code page " + codePage;
-                log.error(msg);
+                LOG.error(msg);
                 throw new RuntimeException(e.getMessage());
             }
         }
@@ -318,7 +320,7 @@
             nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
         } catch (UnsupportedEncodingException usee) {
             nameBytes = name.getBytes();
-            log.warn(
+            LOG.warn(
                 "UnsupportedEncodingException translating the name " + name);
         }
         return nameBytes;
@@ -417,4 +419,21 @@
         return c;
     }
 
+    /**
+     *The AFPFontReader is used to extract font metric data
+     * @param afpFontReader to set
+     */
+    public void setAFPFontReader(AFPFontReader afpFontReader) {
+        this.afpFontReader = afpFontReader;
+    }
+
+    /**
+     *
+     * @return the number units per em
+     */
+    public int getEmSpaceIncrement() {
+        load();
+        return getCharacterSetOrientation().getUnitsPerEm();
+    }
+
 }
Index: src/java/org/apache/fop/afp/fonts/AFPFontReader.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/AFPFontReader.java	(revision 900314)
+++ src/java/org/apache/fop/afp/fonts/AFPFontReader.java	(working copy)
@@ -49,14 +49,13 @@
  * files in order to determine the correct metrics to use when rendering the
  * formatted object. <p/>
  *
- * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
  */
-public final class AFPFontReader {
+public class AFPFontReader {
 
     /**
      * Static logging instance
      */
-    protected static final Log log = LogFactory.getLog(AFPFontReader.class);
+    protected static final Log LOG = LogFactory.getLog(AFPFontReader.class);
 
     /**
      * Template used to convert lists to arrays.
@@ -97,16 +96,36 @@
     private final Map/*<String, Map<String, String>>*/ codePagesCache
         = new java.util.HashMap/*<String, Map<String, String>>*/();
 
+
+    private AFPFontReader() { }
+
     /**
+     * Factory method for the single-byte implementation of AFPFontReader.
+     * @return AFPFontReader
+     */
+    public static AFPFontReader getInstance() {
+        return new AFPFontReader();
+    }
+
+    /**
+     * Factory method for the double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
+     * @return AFPFontReader
+     */
+    public static AFPFontReader getDoubleByteInstance() {
+        return new DoubleByteAFPFontReader();
+    }
+
+
+    /**
      * Returns an InputStream to a given file path and filename
      *
-     * @param path the file path
+     * * @param accessor the resource accessor
      * @param filename the file name
      * @return an inputStream
      *
      * @throws IOException in the event that an I/O exception of some sort has occurred
      */
-    private InputStream openInputStream(ResourceAccessor accessor, String filename)
+    protected InputStream openInputStream(ResourceAccessor accessor, String filename)
             throws IOException {
         URI uri;
         try {
@@ -124,14 +143,14 @@
      *
      * @param inputStream the inputstream to close
      */
-    private void closeInputStream(InputStream inputStream) {
+    protected void closeInputStream(InputStream inputStream) {
         try {
             if (inputStream != null) {
                 inputStream.close();
             }
         } catch (Exception ex) {
             // Lets log at least!
-            log.error(ex.getMessage());
+            LOG.error(ex.getMessage());
         }
     }
 
@@ -229,8 +248,9 @@
      *            the encoding to use for the character decoding
      * @param accessor the resource accessor
      * @returns a code page mapping
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
+    protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
         ResourceAccessor accessor) throws IOException {
 
         // Create the HashMap to store code page information
@@ -277,8 +297,10 @@
      *
      * @param structuredFieldReader the structured field reader
      * @return a class representing the font descriptor
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private static FontDescriptor processFontDescriptor(StructuredFieldReader structuredFieldReader)
+    protected static FontDescriptor processFontDescriptor(
+                StructuredFieldReader structuredFieldReader)
     throws IOException {
 
         byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
@@ -290,8 +312,10 @@
      *
      * @param structuredFieldReader
      *            the structured field reader
+     * @return the FontControl
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private FontControl processFontControl(StructuredFieldReader structuredFieldReader)
+    protected FontControl processFontControl(StructuredFieldReader structuredFieldReader)
     throws IOException {
 
         byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);
@@ -320,8 +344,10 @@
      *
      * @param structuredFieldReader
      *            the structured field reader
+     * @return CharacterSetOrientation array
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private CharacterSetOrientation[] processFontOrientation(
+    protected CharacterSetOrientation[] processFontOrientation(
         StructuredFieldReader structuredFieldReader) throws IOException {
 
         byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF);
@@ -381,8 +407,9 @@
      *            the array of CharacterSetOrientation objects
      * @param metricNormalizationFactor factor to apply to the metrics to get normalized
      *                  font metric values
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private void processFontPosition(StructuredFieldReader structuredFieldReader,
+    protected void processFontPosition(StructuredFieldReader structuredFieldReader,
         CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
             throws IOException {
 
@@ -437,8 +464,9 @@
      * @param codepage the map of code pages
      * @param metricNormalizationFactor factor to apply to the metrics to get normalized
      *                  font metric values
+     * @throws IOException if an I/O exception of some sort has occurred.
      */
-    private void processFontIndex(StructuredFieldReader structuredFieldReader,
+    protected void processFontIndex(StructuredFieldReader structuredFieldReader,
         CharacterSetOrientation cso, Map/*<String,String>*/ codepage,
         double metricNormalizationFactor)
         throws IOException {
@@ -485,8 +513,8 @@
                         double diffPercent = 100 * diff / (double)width;
                         //if difference > 2%
                         if (diffPercent > 2) {
-                            if (log.isTraceEnabled()) {
-                                log.trace(gcgiString + ": "
+                            if (LOG.isTraceEnabled()) {
+                                LOG.trace(gcgiString + ": "
                                         + a + " + " + b + " + " + c + " = " + (a + b + c)
                                         + " but found: " + width);
                             }
@@ -516,9 +544,9 @@
         cso.setFirstChar(lowest);
         cso.setLastChar(highest);
 
-        if (log.isDebugEnabled() && firstABCMismatch != null) {
+        if (LOG.isDebugEnabled() && firstABCMismatch != null) {
             //Debug level because it usually is no problem.
-            log.debug("Font has metrics inconsitencies where A+B+C doesn't equal the"
+            LOG.debug("Font has metrics inconsitencies where A+B+C doesn't equal the"
                     + " character increment. The first such character found: "
                     + firstABCMismatch);
         }
@@ -584,4 +612,115 @@
         }
     }
 
+    /**
+     * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
+     */
+    private static class DoubleByteAFPFontReader extends AFPFontReader {
+
+        protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
+                ResourceAccessor accessor) throws IOException {
+
+            // Create the HashMap to store code page information
+            Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/();
+
+            InputStream inputStream = null;
+            try {
+                inputStream = openInputStream(accessor, codePage.trim());
+
+                StructuredFieldReader structuredFieldReader
+                    = new StructuredFieldReader(inputStream);
+                byte[] data; // = structuredFieldReader.getNext(CHARACTER_TABLE_SF);
+                while ((data = structuredFieldReader.getNext(CHARACTER_TABLE_SF)) != null) {
+                    int position = 0;
+
+                    byte[] gcgiBytes = new byte[8];
+                    byte[] charBytes = new byte[2];
+                    // Read data, ignoring bytes 0 - 2
+                    for (int index = 3; index < data.length; index++) {
+
+                        if (position < 8) {
+                            // Build the graphic character global identifier key
+                            gcgiBytes[position] = data[index];
+                            position++;
+                        } else if (position == 9) {
+                            // Set the character
+                            charBytes[0] = data[index];
+                            position++;
+                        } else if (position == 10) {
+                            position = 0;
+                            // Set the character
+                            charBytes[1] = data[index];
+
+                            String gcgiString = new String(gcgiBytes,
+                                    AFPConstants.EBCIDIC_ENCODING);
+                            String charString = new String(charBytes, encoding);
+                            codePages.put(gcgiString, charString);
+
+                        }
+                        else {
+                            position++;
+                        }
+                    }
+                }
+            } finally {
+                closeInputStream(inputStream);
+            }
+
+            return codePages;
+        }
+
+        protected CharacterSetOrientation[] processFontOrientation(
+                StructuredFieldReader structuredFieldReader) throws IOException {
+
+            byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF);
+
+            int position = 0;
+            byte[] fnoData = new byte[26];
+
+            List orientations = new java.util.ArrayList();
+
+            // Read data, ignoring bytes 0 - 2
+            for (int index = 3; index < data.length; index++) {
+                // Build the font orientation record
+                fnoData[position] = data[index];
+                position++;
+
+                if (position == 26) {
+
+                    position = 0;
+
+                    int orientation = 0;
+
+                    switch (fnoData[2]) {
+                    case 0x00:
+                        orientation = 0;
+                        break;
+                    case 0x2D:
+                        orientation = 90;
+                        break;
+                    case 0x5A:
+                        orientation = 180;
+                        break;
+                    case (byte) 0x87:
+                        orientation = 270;
+                    break;
+                    default:
+                        System.out.println("ERROR: Oriantation");
+                    }
+
+                    //  Em-Space Increment
+                    int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF);
+
+                    CharacterSetOrientation cso = new CharacterSetOrientation(
+                            orientation, em);
+                    orientations.add(cso);
+
+                }
+            }
+
+            return (CharacterSetOrientation[]) orientations
+            .toArray(EMPTY_CSO_ARRAY);
+        }
+    }
+
 }
Index: src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java	(revision 900314)
+++ src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java	(working copy)
@@ -19,6 +19,8 @@
 
 package org.apache.fop.afp.fonts;
 
+import java.util.Arrays;
+
 /**
  * The IBM Font Object Content Architecture (FOCA) supports presentation
  * of character shapes by defining their characteristics, which include
@@ -58,7 +60,7 @@
     /**
      * The character widths in the character set
      */
-    private int[] charsWidths = new int[256];
+    private int[] charsWidths = null;
 
     /**
      * The height of lowercase letters
@@ -82,15 +84,33 @@
     private int orientation = 0;
 
     /**
+     * Units per em
+     */
+    private int unitsPerEm = -1;
+    /**
      * Constructor for the CharacterSetOrientation, the orientation is
      * expressed as the degrees rotation (i.e 0, 90, 180, 270)
      * @param orientation the character set orientation
      */
     public CharacterSetOrientation(int orientation) {
         this.orientation = orientation;
+        charsWidths = new int[256];
+        Arrays.fill(charsWidths, -1);
     }
 
     /**
+     * Constructor for the CharacterSetOrientation, the orientation is
+     * expressed as the degrees rotation (i.e 0, 90, 180, 270)
+     * @param orientation the character set orientation
+     * @param unitsPerEm the units per em
+     */
+    public CharacterSetOrientation(int orientation, int unitsPerEm) {
+        this(orientation);
+        this.unitsPerEm = unitsPerEm;
+
+    }
+
+    /**
      * Ascender height is the distance from the character baseline to the
      * top of the character box. A negative ascender height signifies that
      * all of the graphic character is below the character baseline. For
@@ -245,8 +265,10 @@
     public void setWidth(int character, int width) {
         if (character >= charsWidths.length) {
             // Increase the size of the array if necessary
+            //  TODO Can we remove firstChar? surely firstChar==0 at this stage?
             int[] arr = new int[(character - firstChar) + 1];
             System.arraycopy(charsWidths, 0, arr, 0, charsWidths.length);
+            Arrays.fill(arr, charsWidths.length, character - firstChar, -1);
             charsWidths = arr;
         }
         charsWidths[character] = width;
@@ -261,4 +283,13 @@
     public void setXHeight(int xHeight) {
         this.xHeight = xHeight;
     }
+
+    /**
+     *
+     * @return units per em
+     */
+
+    public int getUnitsPerEm(){
+        return this.unitsPerEm;
+    }
 }
Index: src/java/org/apache/fop/afp/fonts/DoubleByteFont.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/DoubleByteFont.java	(revision 0)
+++ src/java/org/apache/fop/afp/fonts/DoubleByteFont.java	(revision 0)
@@ -0,0 +1,178 @@
+/*
+ * 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: OutlineFont.java 791781 2009-07-07 10:21:07Z cbowditch $ */
+
+package org.apache.fop.afp.fonts;
+
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Implementation of AbstractOutlineFont that supports double-byte fonts (CID Keyed font (Type 0)).
+ * The width of characters that are not prescribed a width metrics in the font resource use
+ * a fallback width.  The default width is 1 em.  A character can be supplied and queried for the
+ *  fallback width of all non-ideograph characters.<p />
+ */
+public class DoubleByteFont extends AbstractOutlineFont {
+
+    private static final Log LOG = LogFactory.getLog(DoubleByteFont.class);
+
+    private static final HashSet IDEOGRAPHIC = new HashSet();
+    static {
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS);
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT);
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS);
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A);
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B);
+    }
+
+    /*  The fallback character is used for assigning width metrics to characters
+        with no mapped width in font resource*/
+    private int fallbackChar;
+
+    // lazy assigned fallbackCharWidth
+    private int fallbackCharWidth = -1; // -1 to code for not assigned
+    // lazy assigned emWidth
+    private int emWidth = -1; // -1 to code for not assigned
+    // use em as fallback width
+    private  boolean isFallbackEmWidth = true;
+
+
+    /**
+     * Constructor for an double-byte outline font.
+     *
+     * @param name
+     *            the name of the font
+     * @param charSet
+     *            the character set
+
+     */
+    public DoubleByteFont(String name, CharacterSet charSet) {
+        super(name, charSet);
+
+    }
+
+    /**
+     * Constructor for an outline font.
+     *
+     * @param name
+     *            the name of the font
+     * @param charSet
+     *            the character set
+     * @param fallbackCharacter
+     *             fallback character for assigning width metrics to characters
+     *              with no mapped width in font resource
+     */
+    public DoubleByteFont(String name, CharacterSet charSet, int fallbackCharacter) {
+        super(name, charSet);
+        this.fallbackChar = fallbackCharacter;
+        this.isFallbackEmWidth = false;
+    }
+
+
+
+
+    /** {@inheritDoc} */
+    public int getWidth(int character, int size) {
+        int charWidth;
+        try {
+            charWidth = charSet.getWidth(character);
+        } catch (IllegalArgumentException e) {
+            //  We shall try and handle characters that have no mapped width metric in font resource
+            charWidth = -1;
+        }
+
+        if (charWidth == -1) {
+            charWidth = inferCharWidth(character);
+        }
+        return charWidth * size;
+    }
+
+
+
+    private int inferCharWidth(int character) {
+
+        //Is this character an ideograph?
+        boolean isIdeographic = false;
+        Character.UnicodeBlock charBlock = Character.UnicodeBlock.of(character);
+        if (charBlock == null) {
+            isIdeographic = false;
+        } else if (IDEOGRAPHIC.contains(charBlock)) {
+            isIdeographic = true;
+        } else { //default
+            isIdeographic = false;
+        }
+
+        return isIdeographic ? getEmWidth() : getFallbackWidth();
+    }
+
+
+    private int getEmWidth() {
+
+        if (emWidth == -1) {
+
+            int width = charSet.getEmSpaceIncrement();
+
+            LOG.debug("em is " + width);
+
+            if (width == -1) {
+                LOG.warn("Could not determine 'em' of font " + name
+                        + ".\n\tUsing the fallback width instead.)");
+                width = 0;
+            }
+            emWidth = width;
+        }
+
+        return emWidth;
+
+    }
+
+    private int getFallbackWidth() {
+        //We shall cache the width in fallbackCharWidth
+
+        if (isFallbackEmWidth) {
+            return getEmWidth();
+        } else {
+            if (fallbackCharWidth == -1) {
+                // cache the
+                try {
+                    fallbackCharWidth = charSet.getWidth(fallbackChar);
+
+                    if (fallbackCharWidth  == -1) {
+                        LOG.warn("No width metric found for the FALLBACK character (codepoint="
+                                 + fallbackChar + ") in font " + name);
+                        fallbackCharWidth = getEmWidth();
+                    }
+                } catch (IllegalArgumentException e) {
+                    LOG.warn("No width metric found for the FALLBACK character (codepoint="
+                            + fallbackChar + ") in font " + name);
+                    fallbackCharWidth = getEmWidth();
+                }
+                LOG.warn("Fallback width of font " + name + " is "
+                           + fallbackCharWidth + "*font-size");
+            }
+            return fallbackCharWidth;
+        }
+
+    }
+
+
+}
Index: src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java
===================================================================
--- src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java	(revision 0)
+++ src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java	(revision 0)
@@ -0,0 +1,183 @@
+/*
+ * 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.afp.fonts;
+
+
+/**
+ * A font defined as a set of lines and curves as opposed to a bitmap font. An
+ * outline font can be scaled to any size and otherwise transformed more easily
+ * than a bitmap font, and with more attractive results. <p/>
+ *
+ */
+public abstract class AbstractOutlineFont extends AFPFont {
+
+    /** The character set for this font */
+    protected CharacterSet charSet = null;
+
+    /**
+     * Constructor for an outline font.
+     *
+     * @param name
+     *            the name of the font
+     * @param charSet
+     *            the chracter set
+     */
+    public AbstractOutlineFont(String name, CharacterSet charSet) {
+        super(name);
+        this.charSet = charSet;
+    }
+
+    /**
+     * Get the character set metrics.
+     *
+     * @return the character set
+     */
+    public CharacterSet getCharacterSet() {
+
+        return charSet;
+
+    }
+
+    /**
+     * Get the character set metrics.
+     * @param size ignored
+     * @return the character set
+     */
+    public CharacterSet getCharacterSet(int size) {
+
+        return charSet;
+
+    }
+
+    /**
+     * Get the first character in this font.
+     * @return the first character in this font
+     */
+    public int getFirstChar() {
+        return charSet.getFirstChar();
+    }
+
+    /**
+     * Get the last character in this font.
+     * @return the last character in this font
+     */
+    public int getLastChar() {
+        return charSet.getLastChar();
+    }
+
+    /**
+     * The ascender is the part of a lowercase letter that extends above the
+     * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
+     * used to denote the part of the letter extending above the x-height.
+     *
+     * @param size the font size (in mpt)
+     * @return the ascender for the given size
+     */
+    public int getAscender(int size) {
+        return charSet.getAscender() * size;
+    }
+
+    /**
+     * Obtains the height of capital letters for the specified point size.
+     *
+     * @param size the font size (in mpt)
+     * @return the cap height for the given size
+     */
+    public int getCapHeight(int size) {
+        return charSet.getCapHeight() * size;
+    }
+
+    /**
+     * The descender is the part of a lowercase letter that extends below the
+     * base line, such as "g", "j", or "p". Also used to denote the part of the
+     * letter extending below the base line.
+     *
+     * @param size the font size (in mpt)
+     * @return the descender for the given size
+     */
+    public int getDescender(int size) {
+        return charSet.getDescender() * size;
+    }
+
+    /**
+     * The "x-height" (the height of the letter "x").
+     *
+     * @param size the font size (in mpt)
+     * @return the x height for the given size
+     */
+    public int getXHeight(int size) {
+        return charSet.getXHeight() * size;
+    }
+
+    /**
+     * Obtain the width of the character for the specified point size.
+     * @param character the character
+     * @param size the font size (in mpt)
+     * @return the width of the character for the specified point size
+     */
+    public int getWidth(int character, int size) {
+        return charSet.getWidth(character) * size;
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @param size the font size (in mpt)
+     * @return the widths of all characters
+     */
+    public int[] getWidths(int size) {
+        int[] widths =  charSet.getWidths();
+        for (int i = 0; i < widths.length; i++) {
+            widths[i] = widths[i] * size;
+        }
+        return widths;
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+        return getWidths(1000);
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasChar(char c) {
+        return charSet.hasChar(c);
+    }
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public char mapChar(char c) {
+        return charSet.mapChar(c);
+    }
+
+    /** {@inheritDoc} */
+    public String getEncodingName() {
+        return charSet.getEncoding();
+    }
+
+}
\ No newline at end of file
