]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #48567:
authorJeremias Maerki <jeremias@apache.org>
Thu, 21 Jan 2010 17:37:07 +0000 (17:37 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 21 Jan 2010 17:37:07 +0000 (17:37 +0000)
Initial support for CID-keyed double-byte fonts (Type 0) in AFP output.
Submitted by: Peter Hancock <peter.hancock.at.gmail.com>

Patch modified by jeremias:
- as discussed: removed fallback character code
- as discussed: changed "double-byte" to "CIDKeyed" for the font type.
- some cosmetic changes
- removed some dead code and commented code.

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

12 files changed:
conf/fop.xconf
src/documentation/content/xdocs/trunk/output.xml
src/java/org/apache/fop/afp/fonts/AFPFontReader.java [deleted file]
src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java [new file with mode: 0644]
src/java/org/apache/fop/afp/fonts/CharacterSet.java
src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java [new file with mode: 0644]
src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java
src/java/org/apache/fop/afp/fonts/DoubleByteFont.java [new file with mode: 0644]
src/java/org/apache/fop/afp/fonts/OutlineFont.java
src/java/org/apache/fop/afp/modca/MapCodedFont.java
src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
status.xml

index 43963604903c9d1515e39f7e9c4219c1cb6a7c4c..10b31cf861736e3284287769d59cd9fd45d111bb 100644 (file)
@@ -396,6 +396,20 @@ the location of this file.
           <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="CIDKeyed".  
+        
+        example:
+         <font>
+               <afp-font type="CIDKeyed" encoding="UnicodeBigUnmarked"  
+               codepage="T1120000" characterset="CZJHMNU" 
+               base-uri="fonts" />
+               <font-triplet name="J-Heisei Mincho" style="normal" weight="normal" />
+        </font>
+        -->
+        
+        
       </fonts>
     </renderer>
 
index 01af40a56df7a1267cf395ce4fbab32c29be23cf..54ed357a010c3ebd499c71ff3672cdcfdc9e8311 100644 (file)
@@ -548,10 +548,11 @@ out = proc.getOutputStream();]]></source>
   <!-- AFP Renderer -->
   ...
 </renderer>]]></source>
-      <p>There are 3 font configuration variants supported:</p>
+      <p>There are 4 font configuration variants supported:</p>
       <ol>
         <li>IBM Raster fonts</li>
         <li>IBM Outline fonts</li>
+        <li>IBM CID-keyed (Type 0) fonts</li>
         <li>FOP built-in Base14 fonts</li>
       </ol>
       <p>A typical raster font configuration looks like:</p>
@@ -600,6 +601,18 @@ out = proc.getOutputStream();]]></source>
         supported for the time being, but you should move to using the more flexible "base-uri"
         attribute so you can profit from the power of URI resolvers.
       </note>
+      <p>A CID-keyed font (Type 0, double-byte outline font) configuration is much the same as an outline font.
+      However, the characterset definition is now required within the afp-font element.</p>
+<source><![CDATA[      <font>
+          <afp-font type="CIDKeyed" characterset="CZJHMNU" 
+             codepage="T1120000" encoding="UnicodeBigUnmarked"
+             base-uri="file:/fonts/ibm" />
+          <font-triplet name="J-Heisei Mincho" style="normal" weight="normal" />
+      </font>
+]]></source>
+      <p>
+Note that the value of the encoding attribute in the example is the double-byte encoding 'UnicodeBigUnmarked' (UTF-16BE).
+      </p>
       <p>Experimentation has shown that the font metrics for the FOP built-in Base14 fonts are actually
       very similar to some of the IBM outline and raster fonts. In cases were the IBM font files are not
       available the base-uri attribute in the afp-font element can be replaced by a base14-font attribute 
diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java b/src/java/org/apache/fop/afp/fonts/AFPFontReader.java
deleted file mode 100644 (file)
index 25ea152..0000000
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * 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;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.fop.afp.AFPConstants;
-import org.apache.fop.afp.util.ResourceAccessor;
-import org.apache.fop.afp.util.StructuredFieldReader;
-
-/**
- * The AFPFontReader is responsible for reading the font attributes from binary
- * code page files and the character set metric files. In IBM font structure, a
- * code page maps each character of text to the characters in a character set.
- * Each character is translated into a code point. When the character is
- * printed, each code point is matched to a character ID on the code page
- * specified. The character ID is then matched to the image (raster pattern or
- * outline pattern) of the character in the character set specified. The image
- * in the character set is the image that is printed in the document. To be a
- * valid code page for a particular character set, all character IDs in the code
- * page must be included in that character set. <p/>This class will read the
- * font information from the binary code page files and character set metric
- * 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 {
-
-    /**
-     * Static logging instance
-     */
-    protected static final Log log = LogFactory.getLog(AFPFontReader.class);
-
-    /**
-     * Template used to convert lists to arrays.
-     */
-    private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
-
-    /** Codepage MO:DCA structured field. */
-    private static final byte[] CODEPAGE_SF = new byte[] {
-        (byte) 0xD3, (byte) 0xA8, (byte) 0x87};
-
-    /** Character table MO:DCA structured field. */
-    private static final byte[] CHARACTER_TABLE_SF = new byte[] {
-        (byte) 0xD3, (byte) 0x8C, (byte) 0x87};
-
-    /** Font descriptor MO:DCA structured field. */
-    private static final byte[] FONT_DESCRIPTOR_SF = new byte[] {
-        (byte) 0xD3, (byte) 0xA6, (byte) 0x89 };
-
-    /** Font control MO:DCA structured field. */
-    private static final byte[] FONT_CONTROL_SF = new byte[] {
-        (byte) 0xD3, (byte) 0xA7, (byte) 0x89 };
-
-    /** Font orientation MO:DCA structured field. */
-    private static final byte[] FONT_ORIENTATION_SF = new byte[] {
-        (byte) 0xD3, (byte) 0xAE, (byte) 0x89 };
-
-    /** Font position MO:DCA structured field. */
-    private static final byte[] FONT_POSITION_SF = new byte[] {
-        (byte) 0xD3, (byte) 0xAC, (byte) 0x89 };
-
-    /** Font index MO:DCA structured field. */
-    private static final byte[] FONT_INDEX_SF = new byte[] {
-        (byte) 0xD3, (byte) 0x8C, (byte) 0x89 };
-
-    /**
-     * The collection of code pages
-     */
-    private final Map/*<String, Map<String, String>>*/ codePagesCache
-        = new java.util.HashMap/*<String, Map<String, String>>*/();
-
-    /**
-     * Returns an InputStream to a given file path and filename
-     *
-     * @param path the file path
-     * @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)
-            throws IOException {
-        URI uri;
-        try {
-            uri = new URI(filename.trim());
-        } catch (URISyntaxException e) {
-            throw new FileNotFoundException("Invalid filename: "
-                    + filename + " (" + e.getMessage() + ")");
-        }
-        InputStream inputStream = accessor.createInputStream(uri);
-        return inputStream;
-    }
-
-    /**
-     * Closes the inputstream
-     *
-     * @param inputStream the inputstream to close
-     */
-    private void closeInputStream(InputStream inputStream) {
-        try {
-            if (inputStream != null) {
-                inputStream.close();
-            }
-        } catch (Exception ex) {
-            // Lets log at least!
-            log.error(ex.getMessage());
-        }
-    }
-
-    /**
-     * Load the font details and metrics into the CharacterSetMetric object,
-     * this will use the actual afp code page and character set files to load
-     * the object with the necessary metrics.
-     *
-     * @param characterSet the CharacterSetMetric object to populate
-     * @throws IOException if an I/O exception of some sort has occurred.
-     */
-    public void loadCharacterSetMetric(CharacterSet characterSet) throws IOException {
-
-        InputStream inputStream = null;
-
-        try {
-
-            /**
-             * Get the code page which contains the character mapping
-             * information to map the unicode character id to the graphic
-             * chracter global identifier.
-             */
-            String codePageId = new String(characterSet.getCodePage());
-            ResourceAccessor accessor = characterSet.getResourceAccessor();
-
-            Map/*<String,String>*/ codePage
-                = (Map/*<String,String>*/)codePagesCache.get(codePageId);
-
-            if (codePage == null) {
-                codePage = loadCodePage(codePageId, characterSet.getEncoding(), accessor);
-                codePagesCache.put(codePageId, codePage);
-            }
-
-            /**
-             * Load the character set metric information, no need to cache this
-             * information as it should be cached by the objects that wish to
-             * load character set metric information.
-             */
-            final String characterSetName = characterSet.getName();
-
-            inputStream = openInputStream(accessor, characterSetName);
-
-            StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
-
-            // Process D3A689 Font Descriptor
-            FontDescriptor fontDescriptor = processFontDescriptor(structuredFieldReader);
-            characterSet.setNominalVerticalSize(fontDescriptor.getNominalFontSizeInMillipoints());
-
-            // Process D3A789 Font Control
-            FontControl fontControl = processFontControl(structuredFieldReader);
-
-            if (fontControl != null) {
-                //process D3AE89 Font Orientation
-                CharacterSetOrientation[] characterSetOrientations
-                    = processFontOrientation(structuredFieldReader);
-
-                int metricNormalizationFactor;
-                if (fontControl.isRelative()) {
-                    metricNormalizationFactor = 1;
-                } else {
-                    int dpi = fontControl.getDpi();
-                    metricNormalizationFactor = 1000 * 72000
-                        / fontDescriptor.getNominalFontSizeInMillipoints() / dpi;
-                }
-
-                //process D3AC89 Font Position
-                processFontPosition(structuredFieldReader, characterSetOrientations,
-                        metricNormalizationFactor);
-
-                //process D38C89 Font Index (per orientation)
-                for (int i = 0; i < characterSetOrientations.length; i++) {
-                    processFontIndex(structuredFieldReader,
-                            characterSetOrientations[i], codePage, metricNormalizationFactor);
-                    characterSet.addCharacterSetOrientation(characterSetOrientations[i]);
-                }
-            } else {
-                throw new IOException(
-                        "Failed to read font control structured field in character set "
-                        + characterSetName);
-            }
-
-        } finally {
-            closeInputStream(inputStream);
-        }
-
-    }
-
-    /**
-     * Load the code page information from the appropriate file. The file name
-     * to load is determined by the code page name and the file extension 'CDP'.
-     *
-     * @param codePage
-     *            the code page identifier
-     * @param encoding
-     *            the encoding to use for the character decoding
-     * @param accessor the resource accessor
-     * @returns a code page mapping
-     */
-    private 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);
-
-            int position = 0;
-            byte[] gcgiBytes = new byte[8];
-            byte[] charBytes = new byte[1];
-
-            // 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) {
-                    position = 0;
-                    // Set the character
-                    charBytes[0] = 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;
-    }
-
-    /**
-     * Process the font descriptor details using the structured field reader.
-     *
-     * @param structuredFieldReader the structured field reader
-     * @return a class representing the font descriptor
-     */
-    private static FontDescriptor processFontDescriptor(StructuredFieldReader structuredFieldReader)
-    throws IOException {
-
-        byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
-        return new FontDescriptor(fndData);
-    }
-
-    /**
-     * Process the font control details using the structured field reader.
-     *
-     * @param structuredFieldReader
-     *            the structured field reader
-     */
-    private FontControl processFontControl(StructuredFieldReader structuredFieldReader)
-    throws IOException {
-
-        byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);
-
-        FontControl fontControl = null;
-        if (fncData != null) {
-            fontControl = new FontControl();
-
-            if (fncData[7] == (byte) 0x02) {
-                fontControl.setRelative(true);
-            }
-            int metricResolution = getUBIN(fncData, 9);
-            if (metricResolution == 1000) {
-                //Special case: 1000 units per em (rather than dpi)
-                fontControl.setUnitsPerEm(1000);
-            } else {
-                fontControl.setDpi(metricResolution / 10);
-            }
-        }
-        return fontControl;
-    }
-
-    /**
-     * Process the font orientation details from using the structured field
-     * reader.
-     *
-     * @param structuredFieldReader
-     *            the structured field reader
-     */
-    private 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");
-                }
-
-                CharacterSetOrientation cso = new CharacterSetOrientation(
-                    orientation);
-                orientations.add(cso);
-
-            }
-        }
-
-        return (CharacterSetOrientation[]) orientations
-            .toArray(EMPTY_CSO_ARRAY);
-    }
-
-    /**
-     * Populate the CharacterSetOrientation object in the suplied array with the
-     * font position details using the supplied structured field reader.
-     *
-     * @param structuredFieldReader
-     *            the structured field reader
-     * @param characterSetOrientations
-     *            the array of CharacterSetOrientation objects
-     * @param metricNormalizationFactor factor to apply to the metrics to get normalized
-     *                  font metric values
-     */
-    private void processFontPosition(StructuredFieldReader structuredFieldReader,
-        CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
-            throws IOException {
-
-        byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF);
-
-        int position = 0;
-        byte[] fpData = new byte[26];
-
-        int characterSetOrientationIndex = 0;
-
-        // Read data, ignoring bytes 0 - 2
-        for (int index = 3; index < data.length; index++) {
-            if (position < 22) {
-                // Build the font orientation record
-                fpData[position] = data[index];
-                if (position == 9) {
-                    CharacterSetOrientation characterSetOrientation
-                            = characterSetOrientations[characterSetOrientationIndex];
-
-                    int xHeight = getSBIN(fpData, 2);
-                    int capHeight = getSBIN(fpData, 4);
-                    int ascHeight = getSBIN(fpData, 6);
-                    int dscHeight = getSBIN(fpData, 8);
-
-                    dscHeight = dscHeight * -1;
-
-                    characterSetOrientation.setXHeight(
-                            (int)Math.round(xHeight * metricNormalizationFactor));
-                    characterSetOrientation.setCapHeight(
-                            (int)Math.round(capHeight * metricNormalizationFactor));
-                    characterSetOrientation.setAscender(
-                            (int)Math.round(ascHeight * metricNormalizationFactor));
-                    characterSetOrientation.setDescender(
-                            (int)Math.round(dscHeight * metricNormalizationFactor));
-                }
-            } else if (position == 22) {
-                position = 0;
-                characterSetOrientationIndex++;
-                fpData[position] = data[index];
-            }
-
-            position++;
-        }
-
-    }
-
-    /**
-     * Process the font index details for the character set orientation.
-     *
-     * @param structuredFieldReader the structured field reader
-     * @param cso the CharacterSetOrientation object to populate
-     * @param codepage the map of code pages
-     * @param metricNormalizationFactor factor to apply to the metrics to get normalized
-     *                  font metric values
-     */
-    private void processFontIndex(StructuredFieldReader structuredFieldReader,
-        CharacterSetOrientation cso, Map/*<String,String>*/ codepage,
-        double metricNormalizationFactor)
-        throws IOException {
-
-        byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF);
-
-        int position = 0;
-
-        byte[] gcgid = new byte[8];
-        byte[] fiData = new byte[20];
-
-        int lowest = 255;
-        int highest = 0;
-        String firstABCMismatch = null;
-
-        // Read data, ignoring bytes 0 - 2
-        for (int index = 3; index < data.length; index++) {
-            if (position < 8) {
-                gcgid[position] = data[index];
-                position++;
-            } else if (position < 27) {
-                fiData[position - 8] = data[index];
-                position++;
-            } else if (position == 27) {
-
-                fiData[position - 8] = data[index];
-
-                position = 0;
-
-                String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING);
-
-                String idx = (String) codepage.get(gcgiString);
-
-                if (idx != null) {
-
-                    int cidx = idx.charAt(0);
-                    int width = getUBIN(fiData, 0);
-                    int a = getSBIN(fiData, 10);
-                    int b = getUBIN(fiData, 12);
-                    int c = getSBIN(fiData, 14);
-                    int abc = a + b + c;
-                    int diff = Math.abs(abc - width);
-                    if (diff != 0 && width != 0) {
-                        double diffPercent = 100 * diff / (double)width;
-                        //if difference > 2%
-                        if (diffPercent > 2) {
-                            if (log.isTraceEnabled()) {
-                                log.trace(gcgiString + ": "
-                                        + a + " + " + b + " + " + c + " = " + (a + b + c)
-                                        + " but found: " + width);
-                            }
-                            if (firstABCMismatch == null) {
-                                firstABCMismatch = gcgiString;
-                            }
-                        }
-                    }
-
-                    if (cidx < lowest) {
-                        lowest = cidx;
-                    }
-
-                    if (cidx > highest) {
-                        highest = cidx;
-                    }
-
-                    int normalizedWidth = (int)Math.round(width * metricNormalizationFactor);
-
-                    cso.setWidth(cidx, normalizedWidth);
-
-                }
-
-            }
-        }
-
-        cso.setFirstChar(lowest);
-        cso.setLastChar(highest);
-
-        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"
-                    + " character increment. The first such character found: "
-                    + firstABCMismatch);
-        }
-    }
-
-    private static int getUBIN(byte[] data, int start) {
-        return ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
-    }
-
-    private static int getSBIN(byte[] data, int start) {
-        int ubin = ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
-        if ((ubin & 0x8000) != 0) {
-            //extend sign
-            return ubin | 0xFFFF0000;
-        } else {
-            return ubin;
-        }
-    }
-
-    private class FontControl {
-
-        private int dpi;
-        private int unitsPerEm;
-
-        private boolean isRelative = false;
-
-        public int getDpi() {
-            return dpi;
-        }
-
-        public void setDpi(int i) {
-            dpi = i;
-        }
-
-        public int getUnitsPerEm() {
-            return this.unitsPerEm;
-        }
-
-        public void setUnitsPerEm(int value) {
-            this.unitsPerEm = value;
-        }
-
-        public boolean isRelative() {
-            return isRelative;
-        }
-
-        public void setRelative(boolean b) {
-            isRelative = b;
-        }
-    }
-
-    private static class FontDescriptor {
-
-        private byte[] data;
-
-        public FontDescriptor(byte[] data) {
-            this.data = data;
-        }
-
-        public int getNominalFontSizeInMillipoints() {
-            int nominalFontSize = 100 * getUBIN(data, 39);
-            return nominalFontSize;
-        }
-    }
-
-}
diff --git a/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java b/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java
new file mode 100644 (file)
index 0000000..2061e9d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+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
index 48d5f4f30d16e912f2f6c182c0e4c28dbda661d3..555d7015843a397020b3e4fb4b7e9ff70a921269 100644 (file)
@@ -20,7 +20,6 @@
 package org.apache.fop.afp.fonts;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.nio.ByteBuffer;
@@ -60,7 +59,7 @@ import org.apache.fop.afp.util.StringUtils;
 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";
@@ -86,9 +85,6 @@ public class CharacterSet {
     /** The path to the installed fonts */
     private ResourceAccessor accessor;
 
-    /** Indicator as to whether to metrics have been loaded */
-    private boolean isMetricsLoaded = false;
-
     /** The current orientation (currently only 0 is supported by FOP) */
     private final String currentOrientation = "0";
 
@@ -122,11 +118,11 @@ public class CharacterSet {
      * @param name the character set name
      * @param accessor the resource accessor to load resource with
      */
-    public CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) {
+     CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) {
         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);
         }
 
@@ -192,7 +188,7 @@ public class CharacterSet {
      * @return the ascender value in millipoints
      */
     public int getAscender() {
-        load();
+
         return getCharacterSetOrientation().getAscender();
     }
 
@@ -204,7 +200,7 @@ public class CharacterSet {
      * @return the cap height value in millipoints
      */
     public int getCapHeight() {
-        load();
+
         return getCharacterSetOrientation().getCapHeight();
     }
 
@@ -216,7 +212,7 @@ public class CharacterSet {
      * @return the descender value in millipoints
      */
     public int getDescender() {
-        load();
+
         return getCharacterSetOrientation().getDescender();
     }
 
@@ -226,7 +222,7 @@ public class CharacterSet {
      * @return the first character in the character set
      */
     public int getFirstChar() {
-        load();
+
         return getCharacterSetOrientation().getFirstChar();
     }
 
@@ -236,7 +232,7 @@ public class CharacterSet {
      * @return the last character in the character set
      */
     public int getLastChar() {
-        load();
+
         return getCharacterSetOrientation().getLastChar();
     }
 
@@ -254,7 +250,7 @@ public class CharacterSet {
      * @return the widths of all characters
      */
     public int[] getWidths() {
-        load();
+
         return getCharacterSetOrientation().getWidths();
     }
 
@@ -264,7 +260,7 @@ public class CharacterSet {
      * @return the typical height of characters
      */
     public int getXHeight() {
-        load();
+
         return getCharacterSetOrientation().getXHeight();
     }
 
@@ -276,27 +272,11 @@ public class CharacterSet {
      * @return the width of the character
      */
     public int getWidth(int character) {
-        load();
+
         return getCharacterSetOrientation().getWidth(character);
     }
 
-    /**
-     * Lazy creation of the character metrics, the afp font file will only
-     * be processed on a method call requiring the metric information.
-     */
-    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);
-                throw new RuntimeException(e.getMessage());
-            }
-        }
-    }
+
 
     /**
      * Returns the AFP character set identifier
@@ -318,7 +298,7 @@ public class CharacterSet {
             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 +397,20 @@ public class CharacterSet {
         return c;
     }
 
+    /**
+     * Returns the increment for an space.
+     * @return the space increment
+     */
+    public int getSpaceIncrement() {
+        return getCharacterSetOrientation().getSpaceIncrement();
+    }
+
+    /**
+     * Returns the increment for an em space.
+     * @return the em space increment
+     */
+    public int getEmSpaceIncrement() {
+        return getCharacterSetOrientation().getEmSpaceIncrement();
+    }
+
 }
diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java
new file mode 100644 (file)
index 0000000..ea9703a
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * 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;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.afp.AFPConstants;
+import org.apache.fop.afp.util.ResourceAccessor;
+import org.apache.fop.afp.util.StructuredFieldReader;
+import org.apache.fop.fonts.Typeface;
+
+/**
+ * The CharacterSetBuilder is responsible building the a CharacterSet instance that holds
+ *  the font metric data.  The data is either read from disk and passed to a CharacterSet (*)
+ *  or a FopCharacterSet is instantiated that is composed of a Typeface instance configured
+ *  with this data.<p/>
+ * -*- For referenced fonts CharacterSetBuilder is responsible for reading the font attributes
+ * from binary code page files and the character set metric files. In IBM font structure, a
+ * code page maps each character of text to the characters in a character set.
+ * Each character is translated into a code point. When the character is
+ * printed, each code point is matched to a character ID on the code page
+ * specified. The character ID is then matched to the image (raster pattern or
+ * outline pattern) of the character in the character set specified. The image
+ * in the character set is the image that is printed in the document. To be a
+ * valid code page for a particular character set, all character IDs in the code
+ * page must be included in that character set. <p/>This class will read the
+ * font information from the binary code page files and character set metric
+ * files in order to determine the correct metrics to use when rendering the
+ * formatted object. <p/>
+ *
+ */
+public class CharacterSetBuilder {
+
+    /**
+     * Static logging instance
+     */
+    protected static final Log LOG = LogFactory.getLog(CharacterSetBuilder.class);
+
+    /**
+     * Template used to convert lists to arrays.
+     */
+    private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
+
+    /** Codepage MO:DCA structured field. */
+    private static final byte[] CODEPAGE_SF = new byte[] {
+        (byte) 0xD3, (byte) 0xA8, (byte) 0x87};
+
+    /** Character table MO:DCA structured field. */
+    private static final byte[] CHARACTER_TABLE_SF = new byte[] {
+        (byte) 0xD3, (byte) 0x8C, (byte) 0x87};
+
+    /** Font descriptor MO:DCA structured field. */
+    private static final byte[] FONT_DESCRIPTOR_SF = new byte[] {
+        (byte) 0xD3, (byte) 0xA6, (byte) 0x89 };
+
+    /** Font control MO:DCA structured field. */
+    private static final byte[] FONT_CONTROL_SF = new byte[] {
+        (byte) 0xD3, (byte) 0xA7, (byte) 0x89 };
+
+    /** Font orientation MO:DCA structured field. */
+    private static final byte[] FONT_ORIENTATION_SF = new byte[] {
+        (byte) 0xD3, (byte) 0xAE, (byte) 0x89 };
+
+    /** Font position MO:DCA structured field. */
+    private static final byte[] FONT_POSITION_SF = new byte[] {
+        (byte) 0xD3, (byte) 0xAC, (byte) 0x89 };
+
+    /** Font index MO:DCA structured field. */
+    private static final byte[] FONT_INDEX_SF = new byte[] {
+        (byte) 0xD3, (byte) 0x8C, (byte) 0x89 };
+
+    /**
+     * The collection of code pages
+     */
+    private final Map/*<String, Map<String, String>>*/ codePagesCache
+        = new java.util.HashMap/*<String, Map<String, String>>*/();
+
+
+    private CharacterSetBuilder() { }
+
+    /**
+     * Factory method for the single-byte implementation of AFPFontReader.
+     * @return AFPFontReader
+     */
+    public static CharacterSetBuilder getInstance() {
+        return new CharacterSetBuilder();
+    }
+
+    /**
+     * Factory method for the double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
+     * @return AFPFontReader
+     */
+    public static CharacterSetBuilder getDoubleByteInstance() {
+        return new DoubleByteLoader();
+    }
+
+
+    /**
+     * Returns an InputStream to a given file path and filename
+     *
+     * * @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
+     */
+    protected InputStream openInputStream(ResourceAccessor accessor, String filename)
+            throws IOException {
+        URI uri;
+        try {
+            uri = new URI(filename.trim());
+        } catch (URISyntaxException e) {
+            throw new FileNotFoundException("Invalid filename: "
+                    + filename + " (" + e.getMessage() + ")");
+        }
+        InputStream inputStream = accessor.createInputStream(uri);
+        return inputStream;
+    }
+
+    /**
+     * Closes the inputstream
+     *
+     * @param inputStream the inputstream to close
+     */
+    protected void closeInputStream(InputStream inputStream) {
+        try {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        } catch (Exception ex) {
+            // Lets log at least!
+            LOG.error(ex.getMessage());
+        }
+    }
+
+    /**
+     * Load the font details and metrics into the CharacterSetMetric object,
+     * this will use the actual afp code page and character set files to load
+     * the object with the necessary metrics.
+     * @param codePageName name of the code page file
+     * @param encoding
+     * @throws RuntimeException if an I/O exception of some sort has occurred.
+     */
+    public CharacterSet build(String characterSetName, String codePageName,
+            String encoding, ResourceAccessor accessor) {
+
+        CharacterSet characterSet = new CharacterSet(
+                codePageName, encoding, characterSetName, accessor);
+
+        InputStream inputStream = null;
+
+        try {
+
+            /**
+             * Get the code page which contains the character mapping
+             * information to map the unicode character id to the graphic
+             * chracter global identifier.
+             */
+
+            Map/*<String,String>*/ codePage
+                = (Map/*<String,String>*/)codePagesCache.get(codePageName);
+
+            if (codePage == null) {
+                codePage = loadCodePage(codePageName, encoding, accessor);
+                codePagesCache.put(codePageName, codePage);
+            }
+
+            inputStream = openInputStream(accessor, characterSetName);
+
+            StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
+
+            // Process D3A689 Font Descriptor
+            FontDescriptor fontDescriptor = processFontDescriptor(structuredFieldReader);
+            characterSet.setNominalVerticalSize(fontDescriptor.getNominalFontSizeInMillipoints());
+
+            // Process D3A789 Font Control
+            FontControl fontControl = processFontControl(structuredFieldReader);
+
+            if (fontControl != null) {
+                //process D3AE89 Font Orientation
+                CharacterSetOrientation[] characterSetOrientations
+                    = processFontOrientation(structuredFieldReader);
+
+                int metricNormalizationFactor;
+                if (fontControl.isRelative()) {
+                    metricNormalizationFactor = 1;
+                } else {
+                    int dpi = fontControl.getDpi();
+                    metricNormalizationFactor = 1000 * 72000
+                        / fontDescriptor.getNominalFontSizeInMillipoints() / dpi;
+                }
+
+                //process D3AC89 Font Position
+                processFontPosition(structuredFieldReader, characterSetOrientations,
+                        metricNormalizationFactor);
+
+                //process D38C89 Font Index (per orientation)
+                for (int i = 0; i < characterSetOrientations.length; i++) {
+                    processFontIndex(structuredFieldReader,
+                            characterSetOrientations[i], codePage, metricNormalizationFactor);
+                    characterSet.addCharacterSetOrientation(characterSetOrientations[i]);
+                }
+            } else {
+
+                    String msg = "Failed to load the character set metrics for code page "
+                        + codePageName;
+                    LOG.error(msg);
+                    throw new RuntimeException("Failed to read font control structured field"
+                            + "in character set " + characterSetName);
+
+            }
+
+        } catch(IOException e){
+            String msg = "Failed to load the character set metrics for code page " + codePageName;
+            LOG.error(msg);
+            throw new RuntimeException("Failed to read font control structured field"
+                    + "in character set " + characterSetName);
+        }
+        finally {
+
+            closeInputStream(inputStream);
+        }
+
+        return characterSet;
+
+    }
+
+    /**
+     * Load the font details and metrics into the CharacterSetMetric object,
+     * this will use the actual afp code page and character set files to load
+     * the object with the necessary metrics.
+     *
+     * @param characterSet the CharacterSetMetric object to populate
+     */
+    public CharacterSet build(String characterSetName, String codePageName,
+            String encoding, Typeface typeface) {
+       return new FopCharacterSet(codePageName, encoding, characterSetName, typeface);
+    }
+
+    /**
+     * Load the code page information from the appropriate file. The file name
+     * to load is determined by the code page name and the file extension 'CDP'.
+     *
+     * @param codePage
+     *            the code page identifier
+     * @param encoding
+     *            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.
+     */
+    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);
+
+            int position = 0;
+            byte[] gcgiBytes = new byte[8];
+            byte[] charBytes = new byte[1];
+
+            // 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) {
+                    position = 0;
+                    // Set the character
+                    charBytes[0] = 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;
+    }
+
+    /**
+     * Process the font descriptor details using the structured field reader.
+     *
+     * @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.
+     */
+    protected static FontDescriptor processFontDescriptor(
+                StructuredFieldReader structuredFieldReader)
+    throws IOException {
+
+        byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
+        return new FontDescriptor(fndData);
+    }
+
+    /**
+     * Process the font control details using the structured field reader.
+     *
+     * @param structuredFieldReader
+     *            the structured field reader
+     * @return the FontControl
+     * @throws IOException if an I/O exception of some sort has occurred.
+     */
+    protected FontControl processFontControl(StructuredFieldReader structuredFieldReader)
+    throws IOException {
+
+        byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);
+
+        FontControl fontControl = null;
+        if (fncData != null) {
+            fontControl = new FontControl();
+
+            if (fncData[7] == (byte) 0x02) {
+                fontControl.setRelative(true);
+            }
+            int metricResolution = getUBIN(fncData, 9);
+            if (metricResolution == 1000) {
+                //Special case: 1000 units per em (rather than dpi)
+                fontControl.setUnitsPerEm(1000);
+            } else {
+                fontControl.setDpi(metricResolution / 10);
+            }
+        }
+        return fontControl;
+    }
+
+    /**
+     * Process the font orientation details from using the structured field
+     * reader.
+     *
+     * @param structuredFieldReader
+     *            the structured field reader
+     * @return CharacterSetOrientation array
+     * @throws IOException if an I/O exception of some sort has occurred.
+     */
+    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 = determineOrientation(fnoData[2]);
+                //  Space Increment
+                int space = ((fnoData[8] & 0xFF ) << 8) + (fnoData[9] & 0xFF);
+                //  Em-Space Increment
+                int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF);
+
+                CharacterSetOrientation cso = new CharacterSetOrientation(orientation);
+                cso.setSpaceIncrement(space);
+                cso.setEmSpaceIncrement(em);
+                orientations.add(cso);
+
+            }
+        }
+
+        return (CharacterSetOrientation[]) orientations
+            .toArray(EMPTY_CSO_ARRAY);
+    }
+
+    /**
+     * Populate the CharacterSetOrientation object in the suplied array with the
+     * font position details using the supplied structured field reader.
+     *
+     * @param structuredFieldReader
+     *            the structured field reader
+     * @param characterSetOrientations
+     *            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.
+     */
+    protected void processFontPosition(StructuredFieldReader structuredFieldReader,
+        CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
+            throws IOException {
+
+        byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF);
+
+        int position = 0;
+        byte[] fpData = new byte[26];
+
+        int characterSetOrientationIndex = 0;
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            if (position < 22) {
+                // Build the font orientation record
+                fpData[position] = data[index];
+                if (position == 9) {
+                    CharacterSetOrientation characterSetOrientation
+                            = characterSetOrientations[characterSetOrientationIndex];
+
+                    int xHeight = getSBIN(fpData, 2);
+                    int capHeight = getSBIN(fpData, 4);
+                    int ascHeight = getSBIN(fpData, 6);
+                    int dscHeight = getSBIN(fpData, 8);
+
+                    dscHeight = dscHeight * -1;
+
+                    characterSetOrientation.setXHeight(
+                            (int)Math.round(xHeight * metricNormalizationFactor));
+                    characterSetOrientation.setCapHeight(
+                            (int)Math.round(capHeight * metricNormalizationFactor));
+                    characterSetOrientation.setAscender(
+                            (int)Math.round(ascHeight * metricNormalizationFactor));
+                    characterSetOrientation.setDescender(
+                            (int)Math.round(dscHeight * metricNormalizationFactor));
+                }
+            } else if (position == 22) {
+                position = 0;
+                characterSetOrientationIndex++;
+                fpData[position] = data[index];
+            }
+
+            position++;
+        }
+
+    }
+
+    /**
+     * Process the font index details for the character set orientation.
+     *
+     * @param structuredFieldReader the structured field reader
+     * @param cso the CharacterSetOrientation object to populate
+     * @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.
+     */
+    protected void processFontIndex(StructuredFieldReader structuredFieldReader,
+        CharacterSetOrientation cso, Map/*<String,String>*/ codepage,
+        double metricNormalizationFactor)
+        throws IOException {
+
+        byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF);
+
+        int position = 0;
+
+        byte[] gcgid = new byte[8];
+        byte[] fiData = new byte[20];
+
+        int lowest = 255;
+        int highest = 0;
+        String firstABCMismatch = null;
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            if (position < 8) {
+                gcgid[position] = data[index];
+                position++;
+            } else if (position < 27) {
+                fiData[position - 8] = data[index];
+                position++;
+            } else if (position == 27) {
+
+                fiData[position - 8] = data[index];
+
+                position = 0;
+
+                String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING);
+
+                String idx = (String) codepage.get(gcgiString);
+
+                if (idx != null) {
+
+                    int cidx = idx.charAt(0);
+                    int width = getUBIN(fiData, 0);
+                    int a = getSBIN(fiData, 10);
+                    int b = getUBIN(fiData, 12);
+                    int c = getSBIN(fiData, 14);
+                    int abc = a + b + c;
+                    int diff = Math.abs(abc - width);
+                    if (diff != 0 && width != 0) {
+                        double diffPercent = 100 * diff / (double)width;
+                        if (diffPercent > 2) {
+                            if (LOG.isTraceEnabled()) {
+                                LOG.trace(gcgiString + ": "
+                                        + a + " + " + b + " + " + c + " = " + (a + b + c)
+                                        + " but found: " + width);
+                            }
+                            if (firstABCMismatch == null) {
+                                firstABCMismatch = gcgiString;
+                            }
+                        }
+                    }
+
+                    if (cidx < lowest) {
+                        lowest = cidx;
+                    }
+
+                    if (cidx > highest) {
+                        highest = cidx;
+                    }
+
+                    int normalizedWidth = (int)Math.round(width * metricNormalizationFactor);
+
+                    cso.setWidth(cidx, normalizedWidth);
+
+                }
+
+            }
+        }
+
+        cso.setFirstChar(lowest);
+        cso.setLastChar(highest);
+
+        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"
+                    + " character increment. The first such character found: "
+                    + firstABCMismatch);
+        }
+    }
+
+    private static int getUBIN(byte[] data, int start) {
+        return ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
+    }
+
+    private static int getSBIN(byte[] data, int start) {
+        int ubin = ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
+        if ((ubin & 0x8000) != 0) {
+            //extend sign
+            return ubin | 0xFFFF0000;
+        } else {
+            return ubin;
+        }
+    }
+
+    private class FontControl {
+
+        private int dpi;
+        private int unitsPerEm;
+
+        private boolean isRelative = false;
+
+        public int getDpi() {
+            return dpi;
+        }
+
+        public void setDpi(int i) {
+            dpi = i;
+        }
+
+        public int getUnitsPerEm() {
+            return this.unitsPerEm;
+        }
+
+        public void setUnitsPerEm(int value) {
+            this.unitsPerEm = value;
+        }
+
+        public boolean isRelative() {
+            return isRelative;
+        }
+
+        public void setRelative(boolean b) {
+            isRelative = b;
+        }
+    }
+
+    private static class FontDescriptor {
+
+        private byte[] data;
+
+        public FontDescriptor(byte[] data) {
+            this.data = data;
+        }
+
+        public int getNominalFontSizeInMillipoints() {
+            int nominalFontSize = 100 * getUBIN(data, 39);
+            return nominalFontSize;
+        }
+    }
+
+    /**
+     * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
+     */
+    private static class DoubleByteLoader extends CharacterSetBuilder {
+
+        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;
+                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;
+        }
+
+    }
+
+    private static int determineOrientation(byte orientation) {
+        int degrees = 0;
+
+        switch (orientation) {
+        case 0x00:
+            degrees = 0;
+            break;
+        case 0x2D:
+            degrees = 90;
+            break;
+        case 0x5A:
+            degrees = 180;
+            break;
+        case (byte) 0x87:
+            degrees = 270;
+            break;
+        default:
+            throw new IllegalStateException("Invalid orientation: " + orientation);
+        }
+        return degrees;
+    }
+}
index 1946fd4a4ab137db18758ce2616d8e10b8ecb846..db0908acb3f8a91b6fad817fcb8f8e40acd9dc0e 100644 (file)
@@ -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 @@ public class CharacterSetOrientation {
     /**
      * The character widths in the character set
      */
-    private int[] charsWidths = new int[256];
+    private int[] charsWidths = null;
 
     /**
      * The height of lowercase letters
@@ -81,6 +83,12 @@ public class CharacterSetOrientation {
      */
     private int orientation = 0;
 
+    /** space increment */
+    private int spaceIncrement;
+    /** em space increment */
+    private int emSpaceIncrement = -1;
+
+
     /**
      * Constructor for the CharacterSetOrientation, the orientation is
      * expressed as the degrees rotation (i.e 0, 90, 180, 270)
@@ -88,6 +96,8 @@ public class CharacterSetOrientation {
      */
     public CharacterSetOrientation(int orientation) {
         this.orientation = orientation;
+        charsWidths = new int[256];
+        Arrays.fill(charsWidths, -1);
     }
 
     /**
@@ -245,8 +255,10 @@ public class CharacterSetOrientation {
     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 +273,37 @@ public class CharacterSetOrientation {
     public void setXHeight(int xHeight) {
         this.xHeight = xHeight;
     }
+
+    /**
+     * Returns the space increment.
+     * @return the space increment
+     */
+    public int getSpaceIncrement(){
+        return this.spaceIncrement;
+    }
+
+    /**
+     * Sets the space increment.
+     * @param value the space increment
+     */
+    public void setSpaceIncrement(int value) {
+        this.spaceIncrement = value;
+    }
+
+    /**
+     * Returns the em space increment.
+     * @return the em space increment
+     */
+    public int getEmSpaceIncrement(){
+        return this.emSpaceIncrement;
+    }
+
+    /**
+     * Sets the em space increment.
+     * @param value the em space increment
+     */
+    public void setEmSpaceIncrement(int value) {
+        this.emSpaceIncrement = value;
+    }
+
 }
diff --git a/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java b/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java
new file mode 100644 (file)
index 0000000..1d3b03f
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+import java.util.Set;
+
+/**
+ * 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);
+
+    //See also http://unicode.org/reports/tr11/ which we've not closely looked at, yet
+    //TODO the Unicode block listed here is probably not complete (ex. Hiragana, Katakana etc.)
+    private static final Set IDEOGRAPHIC = new java.util.HashSet();
+    static {
+        IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS);
+        //IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT); //Java 1.5
+        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); //Java 1.1
+    }
+
+    /**
+     * 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);
+    }
+
+    /** {@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((char)character);
+        if (charBlock == null) {
+            isIdeographic = false;
+        } else if (IDEOGRAPHIC.contains(charBlock)) {
+            isIdeographic = true;
+        } else { //default
+            isIdeographic = false;
+        }
+
+        if (isIdeographic) {
+            return charSet.getEmSpaceIncrement();
+        } else {
+            return charSet.getSpaceIncrement();
+        }
+    }
+
+}
index 8dca69f9c37560f27c46c94ce0a8e06dc889e53f..26488e54d2bc82e79372220196199dfb0db90148 100644 (file)
 
 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/>
- *
+ * Default implementation of AbstractOutlineFont.
  */
-public class OutlineFont extends AFPFont {
-
-    /** 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
-     */
-    public OutlineFont(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);
-    }
+public class OutlineFont extends AbstractOutlineFont {
 
     /** {@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();
+    public OutlineFont(String name, CharacterSet charSet) {
+        super(name, charSet);
     }
 
 }
\ No newline at end of file
index e732a8bb7a2a9047d23542e4474c33732fd7ca07..084ae4b4ca9c0826404b94c63c76ad8dccaee471 100644 (file)
@@ -29,6 +29,7 @@ import java.util.List;
 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;
@@ -77,19 +78,22 @@ public class MapCodedFont extends AbstractStructuredObject {
             }
 
             // Font Character Set Name Reference
-            baos.write(0x0C);
+            baos.write(0x0C); //TODO Relax requirement for 8 chars in the name
             baos.write(0x02);
             baos.write((byte) 0x86);
             baos.write(0x00);
             baos.write(fd.characterSet);
 
             // Font Code Page Name Reference
-            baos.write(0x0C);
+            baos.write(0x0C); //TODO Relax requirement for 8 chars in the name
             baos.write(0x02);
             baos.write((byte) 0x85);
             baos.write(0x00);
             baos.write(fd.codePage);
 
+            //TODO idea: for CIDKeyed fonts, maybe hint at Unicode encoding with X'50' triplet
+            //to allow font substitution.
+
             // Character Rotation
             baos.write(0x04);
             baos.write(0x26);
@@ -211,6 +215,25 @@ public class MapCodedFont extends AbstractStructuredObject {
                 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 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);
+
+                //TODO Relax requirement for 8 characters
                 if (fontDefinition.codePage.length != 8) {
                     throw new IllegalArgumentException("The code page "
                         + new String(fontDefinition.codePage,
index 1e15d4c727ed76e61fd4eb311af872e3c1a84248..27ffeae62f3b80e4af4f34b00e4b15b558a1c389 100644 (file)
@@ -30,10 +30,12 @@ import org.apache.avalon.framework.configuration.ConfigurationException;
 
 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.CharacterSet;
-import org.apache.fop.afp.fonts.FopCharacterSet;
+import org.apache.fop.afp.fonts.CharacterSetBuilder;
+import org.apache.fop.afp.fonts.DoubleByteFont;
 import org.apache.fop.afp.fonts.OutlineFont;
 import org.apache.fop.afp.fonts.RasterFont;
 import org.apache.fop.afp.util.DefaultFOPResourceAccessor;
@@ -57,7 +59,7 @@ import org.apache.fop.util.LogUtil;
  * AFP Renderer configurator
  */
 public class AFPRendererConfigurator extends PrintRendererConfigurator
-            implements IFDocumentHandlerConfigurator {
+        implements IFDocumentHandlerConfigurator {
 
     /**
      * Default constructor
@@ -69,7 +71,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
     }
 
     private AFPFontInfo buildFont(Configuration fontCfg, String fontPath)
-            throws ConfigurationException {
+    throws ConfigurationException {
 
         FontManager fontManager = this.userAgent.getFactory().getFontManager();
 
@@ -127,11 +129,33 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
             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 +185,28 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
                 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));
+                            font.addCharacterSet(sizeMpt,
+                                    CharacterSetBuilder.getInstance()
+                                        .build(characterset, codepage, encoding, 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));
+                    font.addCharacterSet(sizeMpt, CharacterSetBuilder.getInstance()
+                            .build(characterset, codepage, encoding, accessor));
                 }
             }
-            return new AFPFontInfo(font, tripletList);
+            return font;
 
         } else if ("outline".equalsIgnoreCase(type)) {
             String characterset = afpFontCfg.getAttribute("characterset");
@@ -195,30 +220,47 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
             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);
+                        characterSet = CharacterSetBuilder.getInstance()
+                                        .build(characterset, codepage, encoding, 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);
+                characterSet = CharacterSetBuilder.getInstance().build(
+                        characterset, codepage, encoding, accessor);
             }
+            // Return new font object
+            return new OutlineFont(name, characterSet);
+
+        } else if ("CIDKeyed".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 = CharacterSetBuilder.getDoubleByteInstance()
+                            .build(characterset, codepage, encoding, accessor);
+
             // Create a new font object
-            OutlineFont font = new OutlineFont(name, characterSet);
-            return new AFPFontInfo(font, tripletList);
+            DoubleByteFont font = new DoubleByteFont(name, characterSet);
+            return font;
+
         } else {
-            log.error("No or incorrect type attribute");
+            log.error("No or incorrect type attribute: " + type);
         }
+
         return null;
     }
 
@@ -230,7 +272,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
      * @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 +303,9 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
                     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 +388,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
 
         // 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 +400,15 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
                         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);
             }
         }
 
index dcf1712ab67b4bf47a1225210f8375186951c9f1..c60118759422a90b2367de10eebf473a73e89853 100644 (file)
@@ -58,6 +58,9 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="JM" type="add" fixes-bug="48567" due-to="Peter Hancock">
+        Initial support for CID-keyed double-byte fonts (Type 0) in AFP output.
+      </action>
       <action context="Layout" dev="VH" type="fix" fixes-bug="46486">
         Bugfix: having a special page-master for the last page caused loss of content when normal 
         blocks were mixed with blocks spanning all columns.