You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2014/06/05 22:59:05 UTC

svn commit: r1600761 - /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java

Author: lehmi
Date: Thu Jun  5 20:59:05 2014
New Revision: 1600761

URL: http://svn.apache.org/r1600761
Log:
PDFBOX-62: extract the missing width values from the true type font

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java?rev=1600761&r1=1600760&r2=1600761&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java Thu Jun  5 20:59:05 2014
@@ -26,8 +26,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Properties;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.fontbox.ttf.CMAPEncodingEntry;
 import org.apache.fontbox.ttf.CMAPTable;
 import org.apache.fontbox.ttf.GlyphData;
@@ -41,9 +42,11 @@ import org.apache.fontbox.ttf.OS2Windows
 import org.apache.fontbox.ttf.PostScriptTable;
 import org.apache.fontbox.ttf.TTFParser;
 import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.fontbox.util.FontManager;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.encoding.MacOSRomanEncoding;
 import org.apache.pdfbox.encoding.WinAnsiEncoding;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
@@ -59,6 +62,27 @@ public class PDTrueTypeFont extends PDSi
 {
 
     /**
+     * Log instance.
+     */
+    private static final Log LOG = LogFactory.getLog(PDTrueTypeFont.class);
+
+    /**
+     * Start of coderanges.
+     */
+    private static final int START_RANGE_F000 = 0xF000;
+    private static final int START_RANGE_F100 = 0xF100;
+    private static final int START_RANGE_F200 = 0xF200;
+
+    private CMAPEncodingEntry cmapWinUnicode = null;
+    private CMAPEncodingEntry cmapWinSymbol = null;
+    private CMAPEncodingEntry cmapMacintoshSymbol = null;
+    private boolean cmapInitialized = false;
+    
+    private TrueTypeFont trueTypeFont = null;
+
+    private HashMap<Integer, Float> advanceWidths = new HashMap<Integer, Float> (); 
+    
+    /**
      * Constructor.
      */
     public PDTrueTypeFont()
@@ -378,22 +402,200 @@ public class PDTrueTypeFont extends PDSi
      */
     public TrueTypeFont getTTFFont() throws IOException
     {
-        TrueTypeFont trueTypeFont = null;
-        PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary) getFontDescriptor();
-        if (fd != null)
+        if (trueTypeFont == null)
         {
-            PDStream ff2Stream = fd.getFontFile2();
-            if (ff2Stream != null)
+            PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary) getFontDescriptor();
+            if (fd != null)
+            {
+                PDStream ff2Stream = fd.getFontFile2();
+                if (ff2Stream != null)
+                {
+                    TTFParser ttfParser = new TTFParser(true);
+                    trueTypeFont = ttfParser.parseTTF(ff2Stream.createInputStream());
+                }
+            }
+            if (trueTypeFont == null)
             {
-                TTFParser ttfParser = new TTFParser(true);
-                trueTypeFont = ttfParser.parseTTF(ff2Stream.createInputStream());
+                // check if there is a font mapping for an external font file
+                trueTypeFont = FontManager.findTTFont(getBaseFont());
             }
         }
-        if (trueTypeFont == null)
+        return trueTypeFont;
+    }
+    
+
+    @Override
+    public void clear()
+    {
+        super.clear();
+        cmapWinUnicode = null;
+        cmapWinSymbol = null;
+        cmapMacintoshSymbol = null;
+        trueTypeFont = null;
+        if (advanceWidths != null)
         {
-            // check if there is a font mapping for an external font file
-            trueTypeFont = org.apache.fontbox.util.FontManager.findTTFont(getBaseFont());
+            advanceWidths.clear();
+        }
+    }
+
+    @Override
+    public float getFontWidth(int charCode)
+    {
+        float width = super.getFontWidth(charCode);
+        if (width < 0)
+        {
+            if (advanceWidths.containsKey(charCode))
+            {
+                width = advanceWidths.get(charCode);
+            }
+            else
+            {
+                TrueTypeFont ttf = null;
+                try
+                {
+                    ttf = getTTFFont();
+                    if (ttf != null)
+                    {
+                        int code = getGlyphcode(charCode);
+                        width = ttf.getAdvanceWidth(code);
+                        int unitsPerEM = ttf.getUnitsPerEm();
+                        // do we have to scale the width
+                        if (unitsPerEM != 1000)
+                        {
+                            width *= 1000f/unitsPerEM;
+                        }
+                    }
+                }
+                catch (IOException exception)
+                {
+                    width = 250;
+                }
+                advanceWidths.put(charCode, width);
+            }
+        }
+        return width;
+    }
+
+    // TODO I had to copy the following code from TTFGlyph2D as I haven't any idea
+    // where to put it else.
+    private int getGlyphcode(int code)
+    {
+        extractCMaps();
+        int result = 0;
+        if (getFontEncoding() != null && !isSymbolicFont())
+        {
+            try
+            {
+                String charactername = getFontEncoding().getName(code);
+                if (charactername != null)
+                {
+                    if (cmapWinUnicode != null)
+                    {
+                        String unicode = Encoding.getCharacterForName(charactername);
+                        if (unicode != null)
+                        {
+                            result = unicode.codePointAt(0);
+                        }
+                        result = cmapWinUnicode.getGlyphId(result);
+                    }
+                    else if (cmapMacintoshSymbol != null && MacOSRomanEncoding.INSTANCE.hasCodeForName(charactername))
+                    {
+                        result = MacOSRomanEncoding.INSTANCE.getCode(charactername);
+                        result = cmapMacintoshSymbol.getGlyphId(result);
+                    }
+                    else if (cmapWinSymbol != null)
+                    {
+                        // fallback scenario if the glyph can't be found yet
+                        // maybe the 3,0 cmap provides a suitable mapping
+                        // see PDFBOX-2091
+                        result = cmapWinSymbol.getGlyphId(code);
+                    }
+                }
+            }
+            catch (IOException exception)
+            {
+                LOG.error("Caught an exception getGlyhcode: " + exception);
+            }
+        }
+        else if (getFontEncoding() == null || isSymbolicFont())
+        {
+            if (cmapWinSymbol != null)
+            {
+                result = cmapWinSymbol.getGlyphId(code);
+                if (code >= 0 && code <= 0xFF)
+                {
+                    // the CMap may use one of the following code ranges,
+                    // so that we have to add the high byte to get the
+                    // mapped value
+                    if (result == 0)
+                    {
+                        // F000 - F0FF
+                        result = cmapWinSymbol.getGlyphId(code + START_RANGE_F000);
+                    }
+                    if (result == 0)
+                    {
+                        // F100 - F1FF
+                        result = cmapWinSymbol.getGlyphId(code + START_RANGE_F100);
+                    }
+                    if (result == 0)
+                    {
+                        // F200 - F2FF
+                        result = cmapWinSymbol.getGlyphId(code + START_RANGE_F200);
+                    }
+                }
+            }
+            else if (cmapMacintoshSymbol != null)
+            {
+                result = cmapMacintoshSymbol.getGlyphId(code);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * extract all useful CMaps.
+     */
+    private void extractCMaps()
+    {
+        if (!cmapInitialized)
+        {
+            try 
+            {
+                getTTFFont();
+            }
+            catch(IOException exception)
+            {
+                LOG.error("Can't read the true type font", exception);
+            }
+            CMAPTable cmapTable = trueTypeFont.getCMAP();
+            if (cmapTable != null)
+            {
+                // get all relevant CMaps
+                CMAPEncodingEntry[] cmaps = cmapTable.getCmaps();
+                for (int i = 0; i < cmaps.length; i++)
+                {
+                    if (CMAPTable.PLATFORM_WINDOWS == cmaps[i].getPlatformId())
+                    {
+                        if (CMAPTable.ENCODING_UNICODE == cmaps[i].getPlatformEncodingId())
+                        {
+                            cmapWinUnicode = cmaps[i];
+                        }
+                        else if (CMAPTable.ENCODING_SYMBOL == cmaps[i].getPlatformEncodingId())
+                        {
+                            cmapWinSymbol = cmaps[i];
+                        }
+                    }
+                    else if (CMAPTable.PLATFORM_MACINTOSH == cmaps[i].getPlatformId())
+                    {
+                        if (CMAPTable.ENCODING_SYMBOL == cmaps[i].getPlatformEncodingId())
+                        {
+                            cmapMacintoshSymbol = cmaps[i];
+                        }
+                    }
+                }
+            }
+            cmapInitialized = true;
         }
-        return trueTypeFont;
     }
 }
+