Description: AFMFile sometimes indirectly caused wrong assignments of char widths to code points in a single-byte encoding due to mapping multiple characters to the same code points (see overridePrimaryEncoding).  Fixed some FindBugs issues.  Added some generics.
Bug: https://issues.apache.org/bugzilla/show_bug.cgi?id=50605
Last-Update: 2011-09-11
Origin: http://svn.apache.org/viewvc?view=revision&revision=1142189

http://svn.apache.org/viewvc?view=revision&revision=1142189
Index: fop-1.0/src/java/org/apache/fop/fonts/type1/AFMFile.java
===================================================================
--- fop-1.0.orig/src/java/org/apache/fop/fonts/type1/AFMFile.java	2010-07-12 21:34:34.000000000 +0200
+++ fop-1.0/src/java/org/apache/fop/fonts/type1/AFMFile.java	2011-09-11 14:49:37.000000000 +0200
@@ -22,10 +22,12 @@
 import java.awt.geom.Dimension2D;
 import java.awt.geom.RectangularShape;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import org.apache.xmlgraphics.java2d.Dimension2DDouble;
 
 import org.apache.fop.fonts.NamedCharacter;
@@ -36,6 +38,9 @@
  */
 public class AFMFile {
 
+    /** logging instance */
+    private static final Log LOG = LogFactory.getLog(AFMFile.class);
+
     private String fontName;
     private String fullName;
     private String familyName;
@@ -56,15 +61,13 @@
     private AFMWritingDirectionMetrics[] writingDirectionMetrics
         = new AFMWritingDirectionMetrics[3];
 
-    private List charMetrics = new java.util.ArrayList();
-    //List<AFMCharMetrics>
-    private Map charNameToMetrics = new java.util.HashMap();
-    //Map<String, AFMCharMetrics>
+    private List<AFMCharMetrics> charMetrics = new java.util.ArrayList<AFMCharMetrics>();
+    private Map<String, AFMCharMetrics> charNameToMetrics
+                = new java.util.HashMap<String, AFMCharMetrics>();
     private int firstChar = -1;
     private int lastChar = -1;
 
-    private Map kerningMap;
-    //Map<String, Map<String, Dimension2D>>
+    private Map<String, Map<String, Dimension2D>> kerningMap;
 
     /**
      * Default constructor.
@@ -365,14 +368,14 @@
      * @return the character metrics or null if there's no such character
      */
     public AFMCharMetrics getChar(String name) {
-        return (AFMCharMetrics)this.charNameToMetrics.get(name);
+        return this.charNameToMetrics.get(name);
     }
 
     /**
      * Returns the list of AFMCharMetrics instances representing all the available characters.
      * @return a List of AFMCharMetrics instances
      */
-    public List getCharMetrics() {
+    public List<AFMCharMetrics> getCharMetrics() {
         return Collections.unmodifiableList(this.charMetrics);
     }
 
@@ -384,11 +387,11 @@
      */
     public void addXKerning(String name1, String name2, double kx) {
         if (this.kerningMap == null) {
-            this.kerningMap = new java.util.HashMap();
+            this.kerningMap = new java.util.HashMap<String, Map<String, Dimension2D>>();
         }
-        Map entries = (Map)this.kerningMap.get(name1);
+        Map<String, Dimension2D> entries = this.kerningMap.get(name1);
         if (entries == null) {
-            entries = new java.util.HashMap();
+            entries = new java.util.HashMap<String, Dimension2D>();
             this.kerningMap.put(name1, entries);
         }
         entries.put(name2, new Dimension2DDouble(kx, 0));
@@ -406,40 +409,37 @@
      * 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() {
+    public Map<Integer, Map<Integer, Integer>> 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();
+        Map<Integer, Map<Integer, Integer>> m
+                    = new java.util.HashMap<Integer, Map<Integer, Integer>>();
+        for (Map.Entry<String, Map<String, Dimension2D>> entryFrom : this.kerningMap.entrySet()) {
+            String name1 = entryFrom.getKey();
             AFMCharMetrics chm1 = getChar(name1);
             if (chm1 == null || !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();
+            Map<Integer, Integer> container = null;
+            Map<String, Dimension2D> entriesTo = entryFrom.getValue();
+            for (Map.Entry<String, Dimension2D> entryTo : entriesTo.entrySet()) {
+                String name2 = entryTo.getKey();
                 AFMCharMetrics chm2 = getChar(name2);
                 if (chm2 == null || !chm2.hasCharCode()) {
                     continue;
                 }
                 if (container == null) {
-                    Integer k1 = new Integer(chm1.getCharCode());
-                    container = (Map)m.get(k1);
+                    Integer k1 = Integer.valueOf(chm1.getCharCode());
+                    container = m.get(k1);
                     if (container == null) {
-                        container = new java.util.HashMap();
+                        container = new java.util.HashMap<Integer, Integer>();
                         m.put(k1, container);
                     }
                 }
-                Dimension2D dim = (Dimension2D)entryTo.getValue();
-                container.put(new Integer(chm2.getCharCode()),
-                        new Integer((int)Math.round(dim.getWidth())));
+                Dimension2D dim = entryTo.getValue();
+                container.put(Integer.valueOf(chm2.getCharCode()),
+                        Integer.valueOf((int)Math.round(dim.getWidth())));
             }
         }
         return m;
@@ -451,14 +451,41 @@
      * @param encoding the encoding to replace the one given in the AFM
      */
     public void overridePrimaryEncoding(SingleByteEncoding encoding) {
-        Iterator iter = this.charMetrics.iterator();
-        while (iter.hasNext()) {
-            AFMCharMetrics cm = (AFMCharMetrics)iter.next();
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Overriding primary encoding of " + getFontName() + " with: " + encoding);
+        }
+        AFMCharMetrics[] mapped = new AFMCharMetrics[256];
+        for (AFMCharMetrics cm : this.charMetrics) {
             NamedCharacter nc = cm.getCharacter();
             if (nc.hasSingleUnicodeValue()) {
-                int mapped = encoding.mapChar(nc.getSingleUnicodeValue());
-                if (mapped > 0) {
-                    cm.setCharCode(mapped);
+                int codePoint = encoding.mapChar(nc.getSingleUnicodeValue());
+                if (codePoint > 0) {
+                    if (mapped[codePoint] != null) {
+                        if (LOG.isDebugEnabled()) {
+                            AFMCharMetrics other = mapped[codePoint];
+                            String msg = "Not mapping character " + nc + " to code point "
+                                + codePoint + " (" + Integer.toHexString(codePoint) + ") in "
+                                + encoding + ". "
+                                + other + " has already been assigned that code point.";
+                            if (other.getUnicodeSequence()
+                                    .equals(nc.getUnicodeSequence())) {
+                                msg += " This is a specialized glyph for the"
+                                    + " same Unicode character.";
+                                //TODO should these be mapped to a private Unicode area to make
+                                //them accessible?
+                            } else {
+                                msg += " This is a similar character.";
+                            }
+                            if (cm.getWidthX() != other.getWidthX()) {
+                                msg += " They have differing widths: "
+                                    + cm.getWidthX() + " vs. " + other.getWidthX();
+                            }
+                            LOG.debug(msg);
+                        }
+                    } else {
+                        cm.setCharCode(codePoint);
+                        mapped[codePoint] = cm;
+                    }
                 } else {
                     cm.setCharCode(-1);
                 }
@@ -470,6 +497,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public String toString() {
         return "AFM: " + getFullName();
     }
