]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Moved PFM classes to type1 subpackage
authorJeremias Maerki <jeremias@apache.org>
Mon, 2 Dec 2002 14:24:07 +0000 (14:24 +0000)
committerJeremias Maerki <jeremias@apache.org>
Mon, 2 Dec 2002 14:24:07 +0000 (14:24 +0000)
Added PFB classes (taken from branch)

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

src/org/apache/fop/fonts/type1/PFBData.java [new file with mode: 0644]
src/org/apache/fop/fonts/type1/PFBParser.java [new file with mode: 0644]
src/org/apache/fop/fonts/type1/PFMFile.java [new file with mode: 0644]
src/org/apache/fop/fonts/type1/PFMInputStream.java [new file with mode: 0644]
src/org/apache/fop/fonts/type1/package.html [new file with mode: 0644]

diff --git a/src/org/apache/fop/fonts/type1/PFBData.java b/src/org/apache/fop/fonts/type1/PFBData.java
new file mode 100644 (file)
index 0000000..ae0653e
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * $Id$
+ * Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+package org.apache.fop.fonts.type1;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Class that represents the contents of a PFB file.
+ * 
+ * @see PFBParser
+ */
+public class PFBData {
+
+    /**
+     * Raw format, no special file structure
+     */
+    public static final int PFB_RAW = 0;
+    
+    /**
+     * PC format
+     */
+    public static final int PFB_PC  = 1;
+    
+    /**
+     * MAC Format (unsupported, yet)
+     */
+    public static final int PFB_MAC = 2;
+
+    private int pfbFormat; //One of the PFB_* constants
+    private byte[] headerSegment;
+    private byte[] encryptedSegment;
+    private byte[] trailerSegment;
+
+
+    /**
+     * Sets the PFB format the font was loaded with.
+     * @param format one of the PFB_* constants
+     */
+    public void setPFBFormat(int format) {
+        switch (format) {
+            case PFB_RAW:
+            case PFB_PC:
+                this.pfbFormat = format;
+                break;
+            case PFB_MAC:
+                throw new UnsupportedOperationException("Mac format is not yet implemented");
+            default:
+                throw new IllegalArgumentException("Invalid value for PFB format: " + format);
+        }
+    }
+
+
+    /**
+     * Returns the format the font was loaded with.
+     * @return int one of the PFB_* constants
+     */
+    public int getPFBFormat() {
+        return this.pfbFormat;
+    }
+
+    /**
+     * Sets the header segment of the font file.
+     * @param headerSeg the header segment
+     */
+    public void setHeaderSegment(byte[] headerSeg) {
+        this.headerSegment = headerSeg;
+    }
+
+    /**
+     * Sets the encrypted segment of the font file.
+     * @param encryptedSeg the encrypted segment
+     */
+    public void setEncryptedSegment(byte[] encryptedSeg) {
+        this.encryptedSegment = encryptedSeg;
+    }
+
+    /**
+     * Sets the trailer segment of the font file.
+     * @param trailerSeg the trailer segment
+     */
+    public void setTrailerSegment(byte[] trailerSeg) {
+        this.trailerSegment = trailerSeg;
+    }
+
+    /**
+     * Returns the full length of the raw font file.
+     * @return int the raw file length
+     */
+    public int getLength() {
+        return getLength1() + getLength2() + getLength3();
+    }
+
+
+    /**
+     * Returns the Length1 (length of the header segment).
+     * @return int Length1
+     */
+    public int getLength1() {
+        return this.headerSegment.length;
+    }
+
+
+    /**
+     * Returns the Length2 (length of the encrypted segment).
+     * @return int Length2
+     */
+    public int getLength2() {
+        return this.encryptedSegment.length;
+    }
+
+
+    /**
+     * Returns the Length3 (length of the trailer segment).
+     * @return int Length3
+     */
+    public int getLength3() {
+        return this.trailerSegment.length;
+    }
+
+
+    /**
+     * Writes the PFB file in raw format to an OutputStream.
+     * @param out the OutputStream to write to
+     * @throws IOException In case of an I/O problem
+     */
+    public void outputAllParts(OutputStream out) throws IOException {
+        out.write(this.headerSegment);
+        out.write(this.encryptedSegment);
+        out.write(this.trailerSegment);
+    }
+
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "PFB: format=" + getPFBFormat()
+                + " len1=" + getLength1()
+                + " len2=" + getLength2()
+                + " len3=" + getLength3();
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/fop/fonts/type1/PFBParser.java b/src/org/apache/fop/fonts/type1/PFBParser.java
new file mode 100644 (file)
index 0000000..6b43c3f
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * $Id$
+ * Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+package org.apache.fop.fonts.type1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.BufferedInputStream;
+
+//FOP
+import org.apache.fop.util.StreamUtilities;
+
+/**
+ * This class represents a parser for Adobe Type 1 PFB files.
+ * 
+ * @see PFBData
+ */
+public class PFBParser {
+
+    private static final byte[] CURRENTFILE_EEXEC;
+    private static final byte[] CLEARTOMARK;
+
+    static {
+        try {
+            CURRENTFILE_EEXEC = "currentfile eexec".getBytes("US-ASCII");
+            CLEARTOMARK = "cleartomark".getBytes("US-ASCII");
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException("Incompatible VM. It doesn't support the US-ASCII encoding");
+        }
+    }
+
+
+    /**
+     * Parses a PFB file into a PFBData object.
+     * @param url URL to load the PFB file from
+     * @return PFBData memory representation of the font
+     * @throws IOException In case of an I/O problem
+     */
+    public PFBData parsePFB(java.net.URL url) throws IOException {
+        InputStream in = url.openStream();
+        try {
+            return parsePFB(in);
+        } finally {
+            in.close();
+        }
+    }
+
+
+    /**
+     * Parses a PFB file into a PFBData object.
+     * @param pfbFile File to load the PFB file from
+     * @return PFBData memory representation of the font
+     * @throws IOException In case of an I/O problem
+     */
+    public PFBData parsePFB(java.io.File pfbFile) throws IOException {
+        InputStream in = new java.io.FileInputStream(pfbFile);
+        try {
+            return parsePFB(in);
+        } finally {
+            in.close();
+        }
+    }
+
+
+    /**
+     * Parses a PFB file into a PFBData object.
+     * @param in InputStream to load the PFB file from
+     * @return PFBData memory representation of the font
+     * @throws IOException In case of an I/O problem
+     */
+    public PFBData parsePFB(InputStream in) throws IOException {
+        PFBData pfb = new PFBData();
+        BufferedInputStream bin = new BufferedInputStream(in);
+        DataInputStream din = new DataInputStream(bin);
+        din.mark(32);
+        int firstByte = din.readUnsignedByte();
+        din.reset();
+        if (firstByte == 128) {
+            pfb.setPFBFormat(PFBData.PFB_PC);
+            parsePCFormat(pfb, din);
+        } else {
+            pfb.setPFBFormat(PFBData.PFB_RAW);
+            parseRAWFormat(pfb, bin);
+        }
+        return pfb;
+    }
+
+
+    private static int swapInteger(final int value) {
+        return (((value >> 0) & 0xff) << 24) 
+             + (((value >> 8) & 0xff) << 16)
+             + (((value >> 16) & 0xff) << 8)
+             + (((value >> 24) & 0xff) << 0);
+    }
+
+
+    private void parsePCFormat(PFBData pfb, DataInputStream din) throws IOException {
+        int segmentHead;
+        int segmentType;
+        int bytesRead;
+
+        //Read first segment
+        segmentHead = din.readUnsignedByte();
+        if (segmentHead != 128) {
+            throw new IOException("Invalid file format. Expected ASCII 80hex");
+        }
+        segmentType = din.readUnsignedByte(); //Read
+        int len1 = swapInteger(din.readInt());
+        byte[] headerSegment = new byte[len1];
+        bytesRead = din.read(headerSegment);
+        if (bytesRead != len1) {
+            throw new IOException("Could not load the whole segment");
+        }
+        pfb.setHeaderSegment(headerSegment);
+
+        //Read second segment
+        segmentHead = din.readUnsignedByte();
+        if (segmentHead != 128) {
+            throw new IOException("Invalid file format. Expected ASCII 80hex");
+        }
+        segmentType = din.readUnsignedByte();
+        int len2 = swapInteger(din.readInt());
+        byte[] encryptedSegment = new byte[len2];
+        bytesRead = din.read(encryptedSegment);
+        if (bytesRead != len2) {
+            throw new IOException("Could not load the whole segment");
+        }
+        pfb.setEncryptedSegment(encryptedSegment);
+
+        //Read third segment
+        segmentHead = din.readUnsignedByte();
+        if (segmentHead != 128) {
+            throw new IOException("Invalid file format. Expected ASCII 80hex");
+        }
+        segmentType = din.readUnsignedByte();
+        int len3 = swapInteger(din.readInt());
+        byte[] trailerSegment = new byte[len3];
+        bytesRead = din.read(trailerSegment);
+        if (bytesRead != len3) {
+            throw new IOException("Could not load the whole segment");
+        }
+        pfb.setTrailerSegment(trailerSegment);
+
+        //Read EOF indicator
+        segmentHead = din.readUnsignedByte();
+        if (segmentHead != 128) {
+            throw new IOException("Invalid file format. Expected ASCII 80hex");
+        }
+        segmentType = din.readUnsignedByte();
+        if (segmentType != 3) {
+            throw new IOException("Expected segment type 3, but found: " + segmentType);
+        }
+    }
+
+
+    private static final boolean byteCmp(byte[] src, int srcOffset, byte[] cmp) {
+        for (int i = 0; i < cmp.length; i++) {
+            // System.out.println("Compare: " + src[srcOffset + i] + " " + cmp[i]);
+            if (src[srcOffset + i] != cmp[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void calcLengths(PFBData pfb, byte[] originalData) {
+        // Calculate length 1 and 3
+        // System.out.println ("Checking font, size = "+originalData.length);
+
+        // Length1 is the size of the initial ascii portion
+        // search for "currentfile eexec"
+        // Get the first binary number and search backwards for "eexec"
+        int len1 = 30;
+
+        // System.out.println("Length1="+len1);
+        while (!byteCmp(originalData, len1 - CURRENTFILE_EEXEC.length, CURRENTFILE_EEXEC)) {
+            len1++;
+        }
+
+        // Skip newline
+        len1++;
+
+        // Length3 is length of the last portion of the file
+        int len3 = 0;
+        len3 -= CLEARTOMARK.length;
+        while (!byteCmp(originalData, originalData.length + len3, CLEARTOMARK)) {
+            len3--;
+            // System.out.println("Len3="+len3);
+        }
+        len3 = -len3;
+        len3++;
+        // Eat 512 zeroes
+        int numZeroes = 0;
+        byte[] ws1 = new byte[]{0x0D}; //CR
+        byte[] ws2 = new byte[]{0x0A}; //LF
+        byte[] ws3 = new byte[]{0x30}; //"0"
+        while ((originalData[originalData.length - len3] == ws1[0]
+                || originalData[originalData.length - len3] == ws2[0]
+                || originalData[originalData.length - len3] == ws3[0])
+                && numZeroes < 512) {
+            len3++;
+            if (originalData[originalData.length - len3] == ws3[0]) {
+                numZeroes++;
+            }
+        }
+        // System.out.println("Length3="+len3);
+
+        //Create the 3 segments
+        byte[] buffer = new byte[len1];
+        System.arraycopy(originalData, 0, buffer, 0, len1);
+        pfb.setHeaderSegment(buffer);
+
+        int len2 = originalData.length - len3 - len1;
+        buffer = new byte[len2];
+        System.arraycopy(originalData, len1, buffer, 0, len2);
+        pfb.setEncryptedSegment(buffer);
+
+        buffer = new byte[len3];
+        System.arraycopy(originalData, len1 + len2, buffer, 0, len3);
+        pfb.setTrailerSegment(buffer);
+    }
+
+    private void parseRAWFormat(PFBData pfb, BufferedInputStream bin)
+            throws IOException {
+        calcLengths(pfb, StreamUtilities.toByteArray(bin, 32768));
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/fop/fonts/type1/PFMFile.java b/src/org/apache/fop/fonts/type1/PFMFile.java
new file mode 100644 (file)
index 0000000..682b59b
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.type1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+//Avalon
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+
+//FOP
+import org.apache.fop.fonts.Glyphs;
+import org.apache.fop.util.StreamUtilities;
+
+/**
+ * This class represents a PFM file (or parts of it) as a Java object.
+ */
+public class PFMFile extends AbstractLogEnabled {
+
+    // Header stuff
+    private String windowsName;
+    private String postscriptName;
+    private short dfItalic;
+    private int dfWeight;
+    private short dfCharSet;
+    private short dfPitchAndFamily;
+    private int dfAvgWidth;
+    private int dfMaxWidth;
+    private int dfMinWidth;
+    private short dfFirstChar;
+    private short dfLastChar;
+
+    // Extension stuff
+    // ---
+
+    // Extend Text Metrics
+    private int etmCapHeight;
+    private int etmXHeight;
+    private int etmLowerCaseAscent;
+    private int etmLowerCaseDescent;
+
+    // Extent table
+    private int[] extentTable;
+
+    private Map kerningTab = new java.util.HashMap();
+    
+    /**
+     * Parses a PFM file
+     *
+     * @param  inStream The stream from which to read the PFM file.
+     * @throws IOException In case of an I/O problem
+     */
+    public void load(InputStream inStream) throws IOException {
+        final byte[] buf = StreamUtilities.toByteArray(inStream, 8000);
+        final InputStream bufin = new java.io.ByteArrayInputStream(buf);
+        PFMInputStream in = new PFMInputStream(bufin);
+        /*final int version =*/ in.readShort();
+        final long filesize = in.readInt();
+        if (filesize != buf.length) {
+            getLogger().warn("Effective file size is not the same as indicated in the header.");
+        }
+        bufin.reset();
+
+        loadHeader(in);
+        loadExtension(in);
+    }
+
+    /**
+     * Parses the header of the PFM file.
+     *
+     * @param  inStream The stream from which to read the PFM file.
+     * @throws IOException In case of an I/O problem
+     */
+    private void loadHeader(PFMInputStream inStream) throws IOException {
+        inStream.skip(80);
+        dfItalic = inStream.readByte();
+        inStream.skip(2);
+        dfWeight = inStream.readShort();
+        dfCharSet = inStream.readByte();
+        inStream.skip(4);
+        dfPitchAndFamily = inStream.readByte();
+        dfAvgWidth = inStream.readShort();
+        dfMaxWidth = inStream.readShort();
+        dfFirstChar = inStream.readByte();
+        dfLastChar = inStream.readByte();
+        inStream.skip(8);
+        long faceOffset = inStream.readInt();
+
+        inStream.reset();
+        inStream.skip(faceOffset);
+        windowsName = inStream.readString();
+
+        inStream.reset();
+        inStream.skip(117);
+    }
+
+    /**
+     * Parses the extension part of the PFM file.
+     *
+     * @param     inStream The stream from which to read the PFM file.
+     */
+    private void loadExtension(PFMInputStream inStream) throws IOException {
+        final int size = inStream.readShort();
+        if (size != 30) {
+            getLogger().warn("Size of extension block was expected to be "
+                + "30 bytes, but was " + size + " bytes.");
+        }
+        final long extMetricsOffset = inStream.readInt();
+        final long extentTableOffset = inStream.readInt();
+        inStream.skip(4); //Skip dfOriginTable
+        final long kernPairOffset = inStream.readInt();
+        inStream.skip(4); //Skip dfTrackKernTable
+        long driverInfoOffset = inStream.readInt();
+
+        if (kernPairOffset > 0) {
+            inStream.reset();
+            inStream.skip(kernPairOffset);
+            loadKernPairs(inStream);
+        }
+
+        inStream.reset();
+        inStream.skip(driverInfoOffset);
+        postscriptName = inStream.readString();
+
+        if (extMetricsOffset != 0) {
+            inStream.reset();
+            inStream.skip(extMetricsOffset);
+            loadExtMetrics(inStream);
+        }
+        if (extentTableOffset != 0) {
+            inStream.reset();
+            inStream.skip(extentTableOffset);
+            loadExtentTable(inStream);
+        }
+
+    }
+
+    /**
+     * Parses the kernPairs part of the pfm file
+     *
+     * @param     inStream The stream from which to read the PFM file.
+     */
+    private void loadKernPairs(PFMInputStream inStream) throws IOException {
+        int i = inStream.readShort();
+
+
+        getLogger().info(i + " kerning pairs");
+        while (i > 0) {
+            int g1 = (int)inStream.readByte();
+            i--;
+
+            int g2 = (int)inStream.readByte();
+
+            int adj = inStream.readShort();
+            if (adj > 0x8000) {
+                adj = -(0x10000 - adj);
+            }
+            getLogger().debug("Char no: (" + g1 + ", " + g2 + ") kern: " + adj);
+
+            if (getLogger().isDebugEnabled()) {
+                final String glyph1 = Glyphs.TEX8R_GLYPH_NAMES[g1];
+                final String glyph2 = Glyphs.TEX8R_GLYPH_NAMES[g2];
+                getLogger().debug("glyphs: " + glyph1 + ", " + glyph2);
+            }
+
+            Map adjTab = (Map)kerningTab.get(new Integer(g1));
+            if (adjTab == null) {
+                adjTab = new java.util.HashMap();
+            }
+            adjTab.put(new Integer(g2), new Integer(adj));
+            kerningTab.put(new Integer(g1), adjTab);
+        }
+    }
+
+    /**
+     * Parses the extended metrics part of the PFM file.
+     *
+     * @param     inStream The stream from which to read the PFM file.
+     */
+    private void loadExtMetrics(PFMInputStream inStream) throws IOException {
+        final int size = inStream.readShort();
+        if (size != 52) {
+            getLogger().warn("Size of extension block was expected to be "
+                + "52 bytes, but was " + size + " bytes.");
+        }
+        inStream.skip(12); //Skip etmPointSize, etmOrientation, etmMasterHeight, 
+                           //etmMinScale, etmMaxScale, emtMasterUnits
+        etmCapHeight = inStream.readShort();
+        etmXHeight = inStream.readShort();
+        etmLowerCaseAscent = inStream.readShort();
+        etmLowerCaseDescent = inStream.readShort();
+        //Ignore the rest of the values
+    }
+
+    /**
+     * Parses the extent table of the PFM file.
+     *
+     * @param     inStream The stream from which to read the PFM file.
+     */
+    private void loadExtentTable(PFMInputStream inStream) throws IOException {
+        extentTable = new int[dfLastChar - dfFirstChar + 1];
+        dfMinWidth = dfMaxWidth;
+        for (short i = dfFirstChar; i <= dfLastChar; i++) {
+            extentTable[i - dfFirstChar] = inStream.readShort();
+            if (extentTable[i - dfFirstChar] < dfMinWidth) {
+                dfMinWidth = extentTable[i - dfFirstChar];
+            }
+        }
+    }
+
+    /**
+     * Returns the Windows name of the font.
+     *
+     * @return The Windows name.
+     */
+    public String getWindowsName() {
+        return windowsName;
+    }
+
+    /**
+     * Return the kerning table. The kerning table is a Map with
+     * strings with glyphnames as keys, containing Maps as value.
+     * The value map contains a glyph name string key and an Integer value
+     * 
+     * @return A Map containing the kerning table
+     */
+    public Map getKerning() {
+        return kerningTab;
+    }
+
+    /**
+     * Returns the Postscript name of the font.
+     *
+     * @return The Postscript name.
+     */
+    public String getPostscriptName() {
+        return postscriptName;
+    }
+
+    /**
+     * Returns the charset used for the font.
+     *
+     * @return The charset (0=WinAnsi).
+     */
+    public short getCharSet() {
+        return dfCharSet;
+    }
+
+    /**
+     * Returns the charset of the font as a string.
+     *
+     * @return The name of the charset.
+     */
+    public String getCharSetName() {
+        switch (dfCharSet) {
+        case 0:
+            return "WinAnsi";
+        case 128:
+            return "Shift-JIS (Japanese)";
+        default:
+            return "Unknown";
+        }
+    }
+
+    /**
+     * Returns the number of the character that defines
+     * the first entry in the widths list.
+     *
+     * @return The number of the first character.
+     */
+    public short getFirstChar() {
+        return dfFirstChar;
+    }
+
+    /**
+     * Returns the number of the character that defines
+     * the last entry in the widths list.
+     *
+     * @return The number of the last character.
+     */
+    public short getLastChar() {
+        return dfLastChar;
+    }
+
+    /**
+     * Returns the CapHeight parameter for the font (height of uppercase H).
+     *
+     * @return The CapHeight parameter.
+     */
+    public int getCapHeight() {
+        return etmCapHeight;
+    }
+
+    /**
+     * Returns the XHeight parameter for the font (height of lowercase x).
+     *
+     * @return The CapHeight parameter.
+     */
+    public int getXHeight() {
+        return etmXHeight;
+    }
+
+    /**
+     * Returns the LowerCaseAscent parameter for the font (height of lowercase d).
+     *
+     * @return The LowerCaseAscent parameter.
+     */
+    public int getLowerCaseAscent() {
+        return etmLowerCaseAscent;
+    }
+
+    /**
+     * Returns the LowerCaseDescent parameter for the font (height of lowercase p).
+     *
+     * @return The LowerCaseDescent parameter.
+     */
+    public int getLowerCaseDescent() {
+        return etmLowerCaseDescent;
+    }
+
+    /**
+     * Tells whether the font has proportional character spacing.
+     *
+     * @return ex. true for Times, false for Courier.
+     */
+    public boolean getIsProportional() {
+        return ((dfPitchAndFamily & 1) == 1);
+    }
+
+    /**
+     * Returns the bounding box for the font.
+     * Note: this value is just an approximation,
+     * it does not really exist in the PFM file.
+     *
+     * @return The calculated Font BBox.
+     */
+    public int[] getFontBBox() {
+        int[] bbox = new int[4];
+
+        // Just guessing....
+        if (!getIsProportional() && (dfAvgWidth == dfMaxWidth)) {
+            bbox[0] = -20;
+        } else {
+            bbox[0] = -100;
+        }
+        bbox[1] = -(getLowerCaseDescent() + 5);
+        bbox[2] = dfMaxWidth + 10;
+        bbox[3] = getLowerCaseAscent() + 5;
+        return bbox;
+    }
+
+    /**
+     * Returns the characteristics flags for the font as
+     * needed for a PDF font descriptor (See PDF specs).
+     *
+     * @return The characteristics flags.
+     */
+    public int getFlags() {
+        int flags = 0;
+        if (!getIsProportional()) {
+            flags |= 1;
+        }
+        if ((dfPitchAndFamily & 16) == 16) {
+            flags |= 2;
+        }
+        if ((dfPitchAndFamily & 64) == 64) {
+            flags |= 4;
+        }
+        if (dfCharSet == 0) {
+            flags |= 6;
+        }
+        if (dfItalic != 0) {
+            flags |= 7;
+        }
+        return flags;
+    }
+
+    /**
+     * Returns the width of the dominant vertical stems of the font.
+     * Note: this value is just an approximation,
+     * it does not really exist in the PFM file.
+     *
+     * @return The vertical stem width.
+     */
+    public int getStemV() {
+        // Just guessing....
+        if (dfItalic != 0) {
+            return (int)Math.round(dfMinWidth * 0.25);
+        } else {
+            return (int)Math.round(dfMinWidth * 0.6);
+        }
+    }
+
+    /**
+     * Returns the italic angle of the font.
+     * Note: this value is just an approximation,
+     * it does not really exist in the PFM file.
+     *
+     * @return The italic angle.
+     */
+    public int getItalicAngle() {
+        if (dfItalic != 0) {
+            return -16;    // Just guessing....
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the width of a character
+     *
+     * @param  which The number of the character for which the width is requested.
+     * @return The width of a character.
+     */
+    public int getCharWidth(short which) {
+        return extentTable[which - dfFirstChar];
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/type1/PFMInputStream.java b/src/org/apache/fop/fonts/type1/PFMInputStream.java
new file mode 100644 (file)
index 0000000..1fbcbd4
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.type1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.InputStreamReader;
+
+/**
+ * This is a helper class for reading PFM files. It defines functions for
+ * extracting specific values out of the stream.
+ */
+public class PFMInputStream extends java.io.FilterInputStream {
+
+    private DataInputStream datain;
+
+    /**
+     * Constructs a PFMInputStream based on an InputStream representing the 
+     * PFM file.
+     *
+     * @param     in The stream from which to read the PFM file
+     */
+    public PFMInputStream(InputStream in) {
+        super(in);
+        datain = new DataInputStream(in);
+    }
+
+    /**
+     * Parses a one byte value out of the stream.
+     *
+     * @return The value extracted
+     * @throws IOException In case of an I/O problem
+     */
+    public short readByte() throws IOException {
+        short s = datain.readByte();
+        // Now, we've got to trick Java into forgetting the sign
+        int s1 = (((s & 0xF0) >>> 4) << 4) + (s & 0x0F);
+        return (short)s1;
+    }
+
+    /**
+     * Parses a two byte value out of the stream.
+     *
+     * @return The value extracted
+     * @throws IOException In case of an I/O problem
+     */
+    public int readShort() throws IOException {
+        int i = datain.readShort();
+
+        // Change byte order
+        int high = (i & 0xFF00) >>> 8;
+        int low = (i & 0x00FF) << 8;
+        return low + high;
+    }
+
+    /**
+     * Parses a four byte value out of the stream.
+     *
+     * @return The value extracted
+     * @throws IOException In case of an I/O problem
+     */
+    public long readInt() throws IOException {
+        int i = datain.readInt();
+
+        // Change byte order
+        int i1 = (i & 0xFF000000) >>> 24;
+        int i2 = (i & 0x00FF0000) >>> 8;
+        int i3 = (i & 0x0000FF00) << 8;
+        int i4 = (i & 0x000000FF) << 24;
+        return i1 + i2 + i3 + i4;
+    }
+
+    /**
+     * Parses a zero-terminated string out of the stream.
+     *
+     * @return The value extracted
+     * @throws IOException In case of an I/O problem
+     */
+    public String readString() throws IOException {
+        InputStreamReader reader = new InputStreamReader(in, "ISO-8859-1");
+        StringBuffer buf = new StringBuffer();
+        int ch = reader.read();
+        while (ch != 0) {
+            buf.append((char)ch);
+            ch = reader.read();
+        }
+        return buf.toString();
+    }
+
+}
diff --git a/src/org/apache/fop/fonts/type1/package.html b/src/org/apache/fop/fonts/type1/package.html
new file mode 100644 (file)
index 0000000..0c492fc
--- /dev/null
@@ -0,0 +1,6 @@
+<HTML>
+<TITLE>org.apache.fop.fonts.type1 Package</TITLE>
+<BODY>
+<P>Classes for Adobe Type 1 fonts.</P>
+</BODY>
+</HTML>
\ No newline at end of file