]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Fix problem with alternate Unicode code point overriding existing better ones in...
authorJeremias Maerki <jeremias@apache.org>
Thu, 14 Feb 2008 10:41:26 +0000 (10:41 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 14 Feb 2008 10:41:26 +0000 (10:41 +0000)
Made PFM completely optional if an AFM is available. Widths and Kerning are now also read from the AFM. Fallbacks for missing values are in place. If both AFM and PFM are available, both are used to get the best possible result for certain metrics.

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

src/codegen/fonts/code-point-mapping.xsl
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
src/java/org/apache/fop/fonts/type1/AFMFile.java
src/java/org/apache/fop/fonts/type1/AFMParser.java
src/java/org/apache/fop/fonts/type1/Type1FontLoader.java

index 80b62dd17fbe8b8e8fe9348c00b7fa0be3bd396d..c82ca010438a87db5f35f61986f399696fbeed54 100644 (file)
@@ -63,14 +63,17 @@ public class CodePointMapping {
         unicodeMap = new char[256];
         Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
         for (int i = 0; i &lt; table.length; i += 2) {
-           if (table[i + 1] &lt; 256) {
-               latin1Map[table[i + 1]] = (char) table[i];
-           } else {
-               ++nonLatin1;
-           }
-           if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
-               unicodeMap[table[i]] = (char)table[i + 1];
-           }
+            char unicode = (char)table[i + 1];
+            if (unicode &lt; 256) {
+                if (latin1Map[unicode] == 0) {
+                    latin1Map[unicode] = (char) table[i];
+                }
+            } else {
+                ++nonLatin1;
+            }
+            if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
+                unicodeMap[table[i]] = unicode;
+            }
         }
         characters = new char[nonLatin1];
         codepoints = new char[nonLatin1];
index b78ec49c4682f025d0ab36a04246b03fd57dd3b5..9910f1ce7ec7dcbf8d6e86ed1d5e96e523296e76 100644 (file)
@@ -1153,7 +1153,7 @@ public final class FOPropertyMapping implements Constants {
         m.useGeneric(genericSpace);
         corr = new SpacePropertyMaker(m);
         corr.setCorresponding(PR_MARGIN_BOTTOM, PR_MARGIN_BOTTOM, PR_MARGIN_LEFT);
-        corr.setUseParent(true);
+        corr.setUseParent(false);
         corr.setRelative(true);
         addPropertyMaker("space-after", m);
 
@@ -1163,7 +1163,7 @@ public final class FOPropertyMapping implements Constants {
         m.setDefault("0pt");
         IndentPropertyMaker sCorr = new IndentPropertyMaker(m);
         sCorr.setCorresponding(PR_MARGIN_LEFT, PR_MARGIN_RIGHT, PR_MARGIN_TOP);
-        sCorr.setUseParent(true);
+        sCorr.setUseParent(false);
         sCorr.setRelative(true);
         sCorr.setPaddingCorresponding(new int[] {
              PR_PADDING_LEFT, PR_PADDING_RIGHT, PR_PADDING_TOP
@@ -1179,7 +1179,7 @@ public final class FOPropertyMapping implements Constants {
         m.setDefault("0pt");
         IndentPropertyMaker eCorr = new IndentPropertyMaker(m);
         eCorr.setCorresponding(PR_MARGIN_RIGHT, PR_MARGIN_LEFT, PR_MARGIN_BOTTOM);
-        eCorr.setUseParent(true);
+        eCorr.setUseParent(false);
         eCorr.setRelative(true);
         eCorr.setPaddingCorresponding(new int[] {
             PR_PADDING_RIGHT, PR_PADDING_LEFT, PR_PADDING_BOTTOM
index 918d9ee085114b016c6836a6c9e05c8d69056a65..fbbff567e15dc3905aba9651360dcd72aec5770a 100644 (file)
 
 package org.apache.fop.fonts.type1;
 
+import java.awt.geom.RectangularShape;
+
 
 /**
  * Holds the metrics of a single character from an AFM file.
  */
 public class AFMCharMetrics {
 
-    private int charCode;
+    private int charCode = -1;
     private String unicodeChars;
     private String charName;
     private double widthX;
     private double widthY;
+    private RectangularShape bBox;
     
     /**
      * Returns the character code.
-     * @return the charCode
+     * @return the charCode (-1 if not part of the encoding)
      */
     public int getCharCode() {
         return charCode;
     }
     
+    /**
+     * Indicates whether the character has a character code, i.e. is part of the default encoding.
+     * @return true if there is a character code.
+     */
+    public boolean hasCharCode() {
+        return charCode >= 0;
+    }
+    
     /**
      * Sets the character code.
      * @param charCode the charCode to set
@@ -113,6 +124,22 @@ public class AFMCharMetrics {
         this.widthY = widthY;
     }
     
+    /**
+     * Returns the character's bounding box.
+     * @return the bounding box (or null if it isn't available)
+     */
+    public RectangularShape getBBox() {
+        return bBox;
+    }
+
+    /**
+     * Sets the character's bounding box.
+     * @param box the bounding box
+     */
+    public void setBBox(RectangularShape box) {
+        bBox = box;
+    }
+
     /** {@inheritDoc} */
     public String toString() {
         StringBuffer sb = new StringBuffer("AFM Char: ");
index af912975c2e3df2e30170af0a4287448d2f629ed..0b7b8d3c2e67acd0f656bbec9913146bce4b2570 100644 (file)
 
 package org.apache.fop.fonts.type1;
 
+import java.awt.geom.Dimension2D;
 import java.awt.geom.RectangularShape;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -56,9 +59,11 @@ public class AFMFile {
     //List<AFMCharMetrics>
     private Map charNameToMetrics = new java.util.HashMap();
     //Map<String, AFMCharMetrics>
+    private int firstChar = -1;
+    private int lastChar = -1;
     
     private Map kerningMap;
-    //Map<String, Map<String, Dimension>>
+    //Map<String, Map<String, Dimension2D>>
     
     /**
      * Default constructor.
@@ -314,6 +319,13 @@ public class AFMFile {
             if (name != null) {
                 String u = Glyphs.getUnicodeCodePointsForGlyphName(metrics.getCharName());
                 if (u != null) {
+                    if (u.length() > 1) {
+                        //Lower values (ex. space) are most probably more interesting than
+                        //higher values (ex. non-break-space), so sort just to be sure:
+                        char[] chars = u.toCharArray(); 
+                        Arrays.sort(chars);
+                        u = String.valueOf(chars);
+                    }
                     metrics.setUnicodeChars(u);
                 }
             } else {
@@ -325,6 +337,15 @@ public class AFMFile {
         if (name != null) {
             this.charNameToMetrics.put(name, metrics);
         }
+        int idx = metrics.getCharCode();
+        if (idx >= 0) { //Only if the character is part of the encoding
+            if (firstChar < 0 || idx < firstChar) {
+                firstChar = idx;
+            }
+            if (lastChar < 0 || idx > lastChar) {
+                lastChar = idx;
+            }
+        }
     }
     
     /**
@@ -335,6 +356,22 @@ public class AFMFile {
         return this.charMetrics.size();
     }
     
+    /**
+     * Returns the first character index in the encoding that has a glyph.
+     * @return the first character index with a glyph
+     */
+    public int getFirstChar() {
+        return this.firstChar;
+    }
+    
+    /**
+     * Returns the last character index in the encoding that has a glyph.
+     * @return the last character index with a glyph
+     */
+    public int getLastChar() {
+        return this.lastChar;
+    }
+    
     /**
      * Returns the character metrics associated with the character name.
      * @param name the character name
@@ -370,6 +407,57 @@ public class AFMFile {
         entries.put(name2, new Dimension2DDouble(kx, 0));
     }
     
+    /**
+     * Indicates whether the font has kerning information.
+     * @return true if there is kerning information
+     */
+    public boolean hasKerning() {
+        return this.kerningMap != null;
+    }
+    
+    /**
+     * Creates and returns a kerning map for writing mode 0 (ltr) with character codes.
+     * @return the kerning map or null if there is no kerning information.
+     */
+    public Map createXKerningMapEncoded() {
+        if (!hasKerning()) {
+            return null;
+        }
+        Map m = new java.util.HashMap();
+        Iterator iterFrom = this.kerningMap.entrySet().iterator();
+        while (iterFrom.hasNext()) {
+            Map.Entry entryFrom = (Map.Entry)iterFrom.next();
+            String name1 = (String)entryFrom.getKey();
+            AFMCharMetrics chm1 = getChar(name1);
+            if (!chm1.hasCharCode()) {
+                continue;
+            }
+            Map container = null;
+            Map entriesTo = (Map)entryFrom.getValue();
+            Iterator iterTo = entriesTo.entrySet().iterator();
+            while (iterTo.hasNext()) {
+                Map.Entry entryTo = (Map.Entry)iterTo.next();
+                String name2 = (String)entryTo.getKey();
+                AFMCharMetrics chm2 = getChar(name2);
+                if (!chm2.hasCharCode()) {
+                    continue;
+                }
+                if (container == null) {
+                    Integer k1 = new Integer(chm1.getCharCode());
+                    container = (Map)m.get(k1);
+                    if (container == null) {
+                        container = new java.util.HashMap();
+                        m.put(k1, container);
+                    }
+                }
+                Dimension2D dim = (Dimension2D)entryTo.getValue();
+                container.put(new Integer(chm2.getCharCode()),
+                        new Integer((int)Math.round(dim.getWidth())));
+            }
+        }
+        return m;
+    }
+    
     /** {@inheritDoc} */
     public String toString() {
         return "AFM: " + getFullName();
index 4d852058c251a2c0e4f442afde4d8a631d9b7f2f..bb7ea3d307082894e60a318ec28ad696098c4358 100644 (file)
@@ -73,6 +73,7 @@ public class AFMParser {
     private static final String W0 = "W0";
     private static final String W1 = "W1";
     private static final String N = "N";
+    private static final String B = "B";
     private static final String START_TRACK_KERN = "StartTrackKern";
     private static final String END_TRACK_KERN = "EndTrackKern";
     //private static final String START_KERN_PAIRS = "StartKernPairs";
@@ -126,6 +127,7 @@ public class AFMParser {
         VALUE_PARSERS.put(W0, new NotImplementedYet(W0));
         VALUE_PARSERS.put(W1, new NotImplementedYet(W1));
         VALUE_PARSERS.put(N, new StringSetter("CharName"));
+        VALUE_PARSERS.put(B, new CharBBox());
         VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN));
         VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN));
         VALUE_PARSERS.put(START_KERN_PAIRS1, new NotImplementedYet(START_KERN_PAIRS1));
@@ -497,7 +499,13 @@ public class AFMParser {
     
     private static class FontBBox extends AbstractValueHandler {
         public void parse(String line, int startpos, Stack stack) throws IOException {
+            Rectangle rect = parseBBox(line, startpos);
+            
             AFMFile afm = (AFMFile)stack.peek();
+            afm.setFontBBox(rect);
+        }
+
+        protected Rectangle parseBBox(String line, int startpos) {
             Rectangle rect = new Rectangle();
             int endpos;
             
@@ -518,11 +526,19 @@ public class AFMParser {
             v = Integer.parseInt(line.substring(startpos, endpos));
             rect.height = v - rect.y;
             startpos = skipToNonWhiteSpace(line, endpos);
-            
-            afm.setFontBBox(rect);
+            return rect;
         }
     }
     
+    private static class CharBBox extends FontBBox {
+        public void parse(String line, int startpos, Stack stack) throws IOException {
+            Rectangle rect = parseBBox(line, startpos);
+            
+            AFMCharMetrics metrics = (AFMCharMetrics)stack.peek();
+            metrics.setBBox(rect);
+        }
+    }
+
     private static class IsBaseFont extends AbstractValueHandler {
         public void parse(String line, int startpos, Stack stack) throws IOException {
             if (getBooleanValue(line, startpos).booleanValue()) {
index f5b04442b45408d198602d238a4426e73da0be7a..57383dc76a330de96236bd970495ed6393f0ee7f 100644 (file)
@@ -19,6 +19,7 @@
 
 package org.apache.fop.fonts.type1;
 
+import java.awt.geom.RectangularShape;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Iterator;
@@ -107,11 +108,6 @@ public class Type1FontLoader extends FontLoader {
             throw new java.io.FileNotFoundException(
                     "Neither an AFM nor a PFM file was found for " + this.fontFileURI);
         }
-        if (pfm == null) {
-            //Cannot do without for now
-            throw new java.io.FileNotFoundException(
-                    "No PFM file was found for " + this.fontFileURI);
-        }
         buildFont(afm, pfm);
         this.loaded = true;
     }
@@ -122,33 +118,10 @@ public class Type1FontLoader extends FontLoader {
         }
         singleFont = new SingleByteFont();
         singleFont.setFontType(FontType.TYPE1);
-        if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
-            singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
-        } else {
-            log.warn("The PFM reports an unsupported encoding (" 
-                    + pfm.getCharSetName() + "). The font may not work as expected.");
-            singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
-        }
         singleFont.setResolver(this.resolver);
+        singleFont.setEmbedFileName(this.fontFileURI);
         returnFont = singleFont;
         
-        //Font name
-        if (afm != null) {
-            returnFont.setFontName(afm.getFontName()); //PostScript font name
-            returnFont.setFullName(afm.getFullName());
-            Set names = new java.util.HashSet();
-            names.add(afm.getFamilyName());
-            returnFont.setFamilyNames(names);
-        } else {
-            returnFont.setFontName(pfm.getPostscriptName());
-            String fullName = pfm.getPostscriptName();
-            fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
-            returnFont.setFullName(fullName); //emulate afm.getFullName()
-            Set names = new java.util.HashSet();
-            names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
-            returnFont.setFamilyNames(names);
-        }
-        
         //Encoding
         if (afm != null) {
             String encoding = afm.getEncodingScheme();
@@ -169,6 +142,31 @@ public class Type1FontLoader extends FontLoader {
                 CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm);
                 singleFont.setEncoding(mapping);
             }
+        } else {
+            if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
+                singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
+            } else {
+                log.warn("The PFM reports an unsupported encoding (" 
+                        + pfm.getCharSetName() + "). The font may not work as expected.");
+                singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
+            }
+        }
+        
+        //Font name
+        if (afm != null) {
+            returnFont.setFontName(afm.getFontName()); //PostScript font name
+            returnFont.setFullName(afm.getFullName());
+            Set names = new java.util.HashSet();
+            names.add(afm.getFamilyName());
+            returnFont.setFamilyNames(names);
+        } else {
+            returnFont.setFontName(pfm.getPostscriptName());
+            String fullName = pfm.getPostscriptName();
+            fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
+            returnFont.setFullName(fullName); //emulate afm.getFullName()
+            Set names = new java.util.HashSet();
+            names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
+            returnFont.setFamilyNames(names);
         }
         
         //Basic metrics
@@ -185,6 +183,7 @@ public class Type1FontLoader extends FontLoader {
             if (afm.getDescender() != null) {
                 returnFont.setDescender(afm.getDescender().intValue());
             }
+            
             returnFont.setFontBBox(afm.getFontBBoxAsIntArray());
             if (afm.getStdVW() != null) {
                 returnFont.setStemV(afm.getStdVW().intValue());
@@ -198,28 +197,81 @@ public class Type1FontLoader extends FontLoader {
             returnFont.setItalicAngle(pfm.getItalicAngle());
         }
         if (pfm != null) {
-            if (returnFont.getCapHeight() == 0) {
-                returnFont.setCapHeight(pfm.getCapHeight());
+            //Sometimes the PFM has these metrics while the AFM doesn't (ex. Symbol)
+            returnFont.setCapHeight(pfm.getCapHeight());
+            returnFont.setXHeight(pfm.getXHeight());
+            returnFont.setAscender(pfm.getLowerCaseAscent());
+            returnFont.setDescender(pfm.getLowerCaseDescent());
+        }
+        
+        //Fallbacks when some crucial font metrics aren't available
+        //(the following are all optional in AFM, but FontBBox is always available)
+        if (returnFont.getXHeight(1) == 0) {
+            int xHeight = 0;
+            AFMCharMetrics chm = afm.getChar("x");
+            if (chm != null) {
+                RectangularShape rect = chm.getBBox();
+                if (rect != null) {
+                    xHeight = (int)Math.round(rect.getMinX());
+                }
             }
-            if (returnFont.getXHeight(1) == 0) {
-                returnFont.setXHeight(pfm.getXHeight());
+            if (xHeight == 0) {
+                xHeight = Math.round(returnFont.getFontBBox()[3] * 0.6f);
             }
-            if (returnFont.getAscender() == 0) {
-                returnFont.setAscender(pfm.getLowerCaseAscent());
+            returnFont.setXHeight(xHeight);
+        }
+        if (returnFont.getAscender() == 0) {
+            int asc = 0;
+            AFMCharMetrics chm = afm.getChar("d");
+            if (chm != null) {
+                RectangularShape rect = chm.getBBox();
+                if (rect != null) {
+                    asc = (int)Math.round(rect.getMinX());
+                }
             }
-            if (returnFont.getDescender() == 0) {
-                returnFont.setDescender(pfm.getLowerCaseDescent());
+            if (asc == 0) {
+                asc = Math.round(returnFont.getFontBBox()[3] * 0.9f);
             }
+            returnFont.setAscender(asc);
         }
-        returnFont.setFirstChar(pfm.getFirstChar());
-        returnFont.setLastChar(pfm.getLastChar());
-        returnFont.setFlags(pfm.getFlags());
-        returnFont.setMissingWidth(0);
-        for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
-            singleFont.setWidth(i, pfm.getCharWidth(i));
+        if (returnFont.getDescender() == 0) {
+            int desc = 0;
+            AFMCharMetrics chm = afm.getChar("p");
+            if (chm != null) {
+                RectangularShape rect = chm.getBBox();
+                if (rect != null) {
+                    desc = (int)Math.round(rect.getMinX());
+                }
+            }
+            if (desc == 0) {
+                desc = returnFont.getFontBBox()[1];
+            }
+            returnFont.setDescender(desc);
+        }
+        if (returnFont.getCapHeight() == 0) {
+            returnFont.setCapHeight(returnFont.getAscender());
+        }
+        
+        if (afm != null) {
+            returnFont.setFirstChar(afm.getFirstChar());
+            returnFont.setLastChar(afm.getLastChar());
+            Iterator iter = afm.getCharMetrics().iterator();
+            while (iter.hasNext()) {
+                AFMCharMetrics chm = (AFMCharMetrics)iter.next();
+                if (chm.hasCharCode()) {
+                    singleFont.setWidth(chm.getCharCode(), (int)Math.round(chm.getWidthX()));
+                }
+            }
+            returnFont.replaceKerningMap(afm.createXKerningMapEncoded());
+        } else {
+            returnFont.setFirstChar(pfm.getFirstChar());
+            returnFont.setLastChar(pfm.getLastChar());
+            returnFont.setFlags(pfm.getFlags());
+            for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
+                singleFont.setWidth(i, pfm.getCharWidth(i));
+            }
+            returnFont.replaceKerningMap(pfm.getKerning());
         }
-        returnFont.replaceKerningMap(pfm.getKerning());
-        singleFont.setEmbedFileName(this.fontFileURI);
     }
 
     private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) {