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

svn commit: r627702 - in /xmlgraphics/fop/trunk/src: codegen/fonts/ java/org/apache/fop/fo/ java/org/apache/fop/fonts/type1/

Author: jeremias
Date: Thu Feb 14 02:41:26 2008
New Revision: 627702

URL: http://svn.apache.org/viewvc?rev=627702&view=rev
Log:
Fix problem with alternate Unicode code point overriding existing better ones in CodePointMapping (ex. a char code for NBSP was used in place of SPACE for non-standard encodings).
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.

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

Modified: xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl (original)
+++ xmlgraphics/fop/trunk/src/codegen/fonts/code-point-mapping.xsl Thu Feb 14 02:41:26 2008
@@ -63,14 +63,17 @@
         unicodeMap = new char[256];
         Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
         for (int i = 0; i < table.length; i += 2) {
-           if (table[i + 1] < 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 < 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];

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fo/FOPropertyMapping.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fo/FOPropertyMapping.java?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fo/FOPropertyMapping.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fo/FOPropertyMapping.java Thu Feb 14 02:41:26 2008
@@ -1153,7 +1153,7 @@
         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 @@
         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 @@
         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

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java Thu Feb 14 02:41:26 2008
@@ -19,27 +19,38 @@
 
 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 @@
         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: ");

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMFile.java Thu Feb 14 02:41:26 2008
@@ -19,8 +19,11 @@
 
 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 @@
     //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 @@
             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 @@
         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;
+            }
+        }
     }
     
     /**
@@ -336,6 +357,22 @@
     }
     
     /**
+     * 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
      * @return the character metrics or null if there's no such character
@@ -368,6 +405,57 @@
             this.kerningMap.put(name1, entries);
         }
         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} */

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/AFMParser.java Thu Feb 14 02:41:26 2008
@@ -73,6 +73,7 @@
     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 @@
         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 @@
     
     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 @@
             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()) {

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java?rev=627702&r1=627701&r2=627702&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java Thu Feb 14 02:41:26 2008
@@ -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 @@
             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 @@
         }
         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 @@
                 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 @@
             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 @@
             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) {



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