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 2013/07/24 21:24:43 UTC

svn commit: r1506667 [1/2] - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox: pdfviewer/ pdfviewer/font/ pdmodel/font/ util/

Author: lehmi
Date: Wed Jul 24 19:24:43 2013
New Revision: 1506667

URL: http://svn.apache.org/r1506667
Log:
PDFBOX-490: added rendering for TTF fonts

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java   (with props)
Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/PageDrawer.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFStreamEngine.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/PageDrawer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/PageDrawer.java?rev=1506667&r1=1506666&r2=1506667&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/PageDrawer.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/PageDrawer.java Wed Jul 24 19:24:43 2013
@@ -34,13 +34,19 @@ import java.awt.geom.GeneralPath;
 import java.awt.geom.NoninvertibleTransformException;
 import java.awt.geom.Point2D;
 import java.io.IOException;
+import java.util.HashMap;
+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.fontbox.cmap.CMap;
+import org.apache.fontbox.ttf.TrueTypeFont;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdfviewer.font.Glyph2D;
+import org.apache.pdfbox.pdfviewer.font.TTFGlyph2D;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.PDResources;
 import org.apache.pdfbox.pdmodel.common.PDMatrix;
@@ -48,6 +54,7 @@ import org.apache.pdfbox.pdmodel.common.
 import org.apache.pdfbox.pdmodel.font.PDCIDFontType2Font;
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
+import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
 import org.apache.pdfbox.pdmodel.font.PDType0Font;
 import org.apache.pdfbox.pdmodel.font.PDType3Font;
 import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState;
@@ -70,7 +77,7 @@ import org.apache.pdfbox.util.TextPositi
  * This will paint a page in a PDF document to a graphics context.
  * 
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.22 $
+ * 
  */
 public class PageDrawer extends PDFStreamEngine
 {
@@ -98,11 +105,12 @@ public class PageDrawer extends PDFStrea
 
     private GeneralPath linePath = new GeneralPath();
 
+    private HashMap<PDFont, Glyph2D> fontGlyph2D = new HashMap<PDFont, Glyph2D>();
+
     /**
      * Default constructor, loads properties from file.
      * 
-     * @throws IOException
-     *             If there is an error loading properties from the file.
+     * @throws IOException If there is an error loading properties from the file.
      */
     public PageDrawer() throws IOException
     {
@@ -112,15 +120,11 @@ public class PageDrawer extends PDFStrea
     /**
      * This will draw the page to the requested context.
      * 
-     * @param g
-     *            The graphics context to draw onto.
-     * @param p
-     *            The page to draw.
-     * @param pageDimension
-     *            The size of the page to draw.
+     * @param g The graphics context to draw onto.
+     * @param p The page to draw.
+     * @param pageDimension The size of the page to draw.
      * 
-     * @throws IOException
-     *             If there is an IO error while drawing the page.
+     * @throws IOException If there is an IO error while drawing the page.
      */
     public void drawPage(Graphics g, PDPage p, Dimension pageDimension) throws IOException
     {
@@ -170,14 +174,33 @@ public class PageDrawer extends PDFStrea
                 }
             }
         }
+    }
 
+    /**
+     * Remove all cached resources.
+     */
+    public void dispose()
+    {
+        if (fontGlyph2D != null)
+        {
+            Iterator<Glyph2D> iter = fontGlyph2D.values().iterator();
+            while (iter.hasNext())
+            {
+                iter.next().dispose();
+            }
+            fontGlyph2D.clear();
+            fontGlyph2D = null;
+        }
+        graphics = null;
+        linePath = null;
+        page = null;
+        pageSize = null;
     }
 
     /**
      * You should override this method if you want to perform an action when a text is being processed.
      * 
-     * @param text
-     *            The text to process
+     * @param text The text to process
      */
     protected void processTextPosition(TextPosition text)
     {
@@ -254,11 +277,22 @@ public class PageDrawer extends PDFStrea
             // use different methods to draw the string
             if (font.isType3Font())
             {
-                drawType3String((PDType3Font) font, text.getCharacter(), text.getCodePoints(), at);
+                // Type3 fonts are using streams for each character
+                drawType3String((PDType3Font) font, text.getCharacter(), at);
             }
             else
             {
-                drawString((PDSimpleFont) font, text.getCharacter(), text.getCodePoints(), graphics, at, x, y);
+                Glyph2D glyph2D = createGlyph2D(font);
+                if (glyph2D != null)
+                {
+                    // Let PDFBox render the font if supported
+                    drawGlyph2D(glyph2D, text.getCodePoints(), graphics, at, x, y);
+                }
+                else
+                {
+                    // Use AWT to render the font
+                    drawString((PDSimpleFont) font, text.getCharacter(), text.getCodePoints(), graphics, at, x, y);
+                }
             }
         }
         catch (IOException io)
@@ -267,9 +301,74 @@ public class PageDrawer extends PDFStrea
         }
     }
 
-    private void drawType3String(PDType3Font font, String string, int[] codePoints, AffineTransform at)
+    /**
+     * Render the font using the Glyph2d interface.
+     * 
+     * @param glyph2D the Glyph2D implementation provided a GeneralPath for each glyph
+     * @param codePoints the string to be rendered
+     * @param graphics the graphics object to be used for rendering
+     * @param at the transformation
+     * @param x the x coordinate of the text
+     * @param y the y coordinate of the text
+     * @throws IOException if something went wrong
+     */
+    private void drawGlyph2D(Glyph2D glyph2D, int[] codePoints, Graphics graphics, AffineTransform at, float x, float y)
             throws IOException
     {
+        Graphics2D g2d = (Graphics2D) graphics;
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        for (int i = 0; i < codePoints.length; i++)
+        {
+            if (!at.isIdentity())
+            {
+                try
+                {
+                    AffineTransform atInv = at.createInverse();
+                    // do only apply the size of the transform, rotation will be realized by rotating the graphics,
+                    // otherwise the hp printers will not render the font
+                    // apply the transformation to the graphics, which should be the same as applying the
+                    // transformation itself to the text
+                    g2d.transform(at);
+                    // translate the coordinates
+                    Point2D.Float newXy = new Point2D.Float(x, y);
+                    atInv.transform(new Point2D.Float(x, y), newXy);
+
+                    GeneralPath path = glyph2D.getPathForCharactercode(codePoints[i]);
+                    if (path != null)
+                    {
+                        g2d.translate(newXy.getX(), newXy.getY());
+                        g2d.fill(path);
+                        g2d.translate(-newXy.getX(), -newXy.getY());
+                    }
+                    // restore the original transformation
+                    g2d.transform(atInv);
+                }
+                catch (NoninvertibleTransformException e)
+                {
+                    LOG.error("Error in " + getClass().getName() + ".drawGlyph2D", e);
+                }
+            }
+            else
+            {
+                GeneralPath path = glyph2D.getPathForCharactercode(codePoints[i]);
+                if (path != null)
+                {
+                    g2d.draw(path);
+                }
+            }
+        }
+    }
+
+    /**
+     * Render the text using a type 3 font.
+     * 
+     * @param font the type3 font
+     * @param string the string to be rendered
+     * @param at the transformation
+     * @throws IOException if something went wrong
+     */
+    private void drawType3String(PDType3Font font, String string, AffineTransform at) throws IOException
+    {
         int stringLength = string.length();
         for (int i = 0; i < stringLength; i++)
         {
@@ -297,23 +396,15 @@ public class PageDrawer extends PDFStrea
     /**
      * This will draw a string on a canvas using the font.
      * 
-     * @param font
-     *            the font to be used to draw the string
-     * @param string
-     *            The string to draw.
-     * @param codePoints
-     *            The codePoints of the given string.
-     * @param g
-     *            The graphics to draw onto.
-     * @param at
-     *            The transformation matrix with all information for scaling and shearing of the font.
-     * @param x
-     *            The x coordinate to draw at.
-     * @param y
-     *            The y coordinate to draw at.
+     * @param font the font to be used to draw the string
+     * @param string The string to draw.
+     * @param codePoints The codePoints of the given string.
+     * @param g The graphics to draw onto.
+     * @param at The transformation matrix with all information for scaling and shearing of the font.
+     * @param x The x coordinate to draw at.
+     * @param y The y coordinate to draw at.
      * 
-     * @throws IOException
-     *             If there is an error drawing the specific string.
+     * @throws IOException If there is an error drawing the specific string.
      */
     private void drawString(PDSimpleFont font, String string, int[] codePoints, Graphics g, AffineTransform at,
             float x, float y) throws IOException
@@ -395,6 +486,86 @@ public class PageDrawer extends PDFStrea
     }
 
     /**
+     * Provide a Glyh2d for the given font if supported.
+     * 
+     * @param font the font
+     * @return the implementation of the Glyph2D interface for the given font if supported
+     * @throws IOException if something went wrong
+     */
+    private Glyph2D createGlyph2D(PDFont font) throws IOException
+    {
+        Glyph2D glyph2D = null;
+        // Is there already a Glyph2D for the given font?
+        if (fontGlyph2D.containsKey(font))
+        {
+            glyph2D = fontGlyph2D.get(font);
+        }
+        else
+        {
+            // check if the given font is supported
+
+            // TTF fonts are supported
+            if (font instanceof PDTrueTypeFont)
+            {
+                PDTrueTypeFont ttfFont = (PDTrueTypeFont) font;
+                // does the font have an optional toUnicode mapping
+                CMap toUnicodeCMap = null;
+                if (ttfFont.hasToUnicode())
+                {
+                    toUnicodeCMap = ttfFont.getToUnicodeCMap();
+                }
+                // get the true type font raw data
+                TrueTypeFont ttf = ttfFont.getTTFFont();
+                if (ttf != null)
+                {
+                    glyph2D = new TTFGlyph2D(ttf, font.getBaseFont(), ttfFont.isSymbolicFont(), toUnicodeCMap);
+                }
+                // cache the Glyph2D instance
+                if (glyph2D != null)
+                {
+                    fontGlyph2D.put(font, glyph2D);
+                }
+            }
+            else if (font instanceof PDType0Font)
+            {
+                PDType0Font type0Font = (PDType0Font) font;
+                CMap toUnicodeCMap = null;
+                if (type0Font.hasToUnicode())
+                {
+                    toUnicodeCMap = type0Font.getToUnicodeCMap();
+                }
+                if (type0Font.getDescendantFont() instanceof PDCIDFontType2Font)
+                {
+                    // a CIDFontType2Font contains TTF font
+                    PDCIDFontType2Font cidType2Font = (PDCIDFontType2Font) type0Font.getDescendantFont();
+                    // get the true type font raw data
+                    TrueTypeFont ttf = cidType2Font.getTTFFont();
+                    if (ttf != null)
+                    {
+                        if (cidType2Font.hasCIDToGIDMap())
+                        {
+                            glyph2D = new TTFGlyph2D(ttf, font.getBaseFont(), cidType2Font.isSymbolicFont(),
+                                    toUnicodeCMap, cidType2Font.getCID2GID());
+                        }
+                        else
+                        {
+                            glyph2D = new TTFGlyph2D(ttf, font.getBaseFont(), cidType2Font.isSymbolicFont(),
+                                    toUnicodeCMap);
+                        }
+                    }
+                    // cache the Glyph2D instance
+                    if (glyph2D != null)
+                    {
+                        fontGlyph2D.put(font, glyph2D);
+                    }
+
+                }
+            }
+        }
+        return glyph2D;
+    }
+
+    /**
      * Get the graphics that we are currently drawing on.
      * 
      * @return The graphics we are drawing on.
@@ -427,8 +598,7 @@ public class PageDrawer extends PDFStrea
     /**
      * Fix the y coordinate.
      * 
-     * @param y
-     *            The y coordinate.
+     * @param y The y coordinate.
      * @return The updated y coordinate.
      */
     public double fixY(double y)
@@ -449,8 +619,7 @@ public class PageDrawer extends PDFStrea
     /**
      * Set the line path to draw.
      * 
-     * @param newLinePath
-     *            Set the line path to draw.
+     * @param newLinePath Set the line path to draw.
      */
     public void setLinePath(GeneralPath newLinePath)
     {
@@ -467,11 +636,9 @@ public class PageDrawer extends PDFStrea
     /**
      * Fill the path.
      * 
-     * @param windingRule
-     *            The winding rule this path will use.
+     * @param windingRule The winding rule this path will use.
      * 
-     * @throws IOException
-     *             If there is an IO error while filling the path.
+     * @throws IOException If there is an IO error while filling the path.
      */
     public void fillPath(int windingRule) throws IOException
     {
@@ -498,8 +665,7 @@ public class PageDrawer extends PDFStrea
     /**
      * This will set the current stroke.
      * 
-     * @param newStroke
-     *            The current stroke.
+     * @param newStroke The current stroke.
      * 
      */
     public void setStroke(BasicStroke newStroke)
@@ -521,8 +687,7 @@ public class PageDrawer extends PDFStrea
     /**
      * Stroke the path.
      * 
-     * @throws IOException
-     *             If there is an IO error while stroking the path.
+     * @throws IOException If there is an IO error while stroking the path.
      */
     public void strokePath() throws IOException
     {
@@ -549,10 +714,8 @@ public class PageDrawer extends PDFStrea
     /**
      * Called when the color changed.
      * 
-     * @param bStroking
-     *            true for the stroking color, false for the non-stroking color
-     * @throws IOException
-     *             if an I/O error occurs
+     * @param bStroking true for the stroking color, false for the non-stroking color
+     * @throws IOException if an I/O error occurs
      */
     @Deprecated
     public void colorChanged(boolean bStroking) throws IOException
@@ -564,10 +727,8 @@ public class PageDrawer extends PDFStrea
     /**
      * use the current transformation matrix to transform a single point.
      * 
-     * @param x
-     *            x-coordinate of the point to be transform
-     * @param y
-     *            y-coordinate of the point to be transform
+     * @param x x-coordinate of the point to be transform
+     * @param y y-coordinate of the point to be transform
      * @return the transformed coordinates as Point2D.Double
      */
     public java.awt.geom.Point2D.Double transformedPoint(double x, double y)
@@ -582,8 +743,7 @@ public class PageDrawer extends PDFStrea
     /**
      * Set the clipping Path.
      * 
-     * @param windingRule
-     *            The winding rule this path will use.
+     * @param windingRule The winding rule this path will use.
      * 
      * @deprecated use {@link #setClippingWindingRule(int)} instead
      * 
@@ -596,8 +756,7 @@ public class PageDrawer extends PDFStrea
     /**
      * Set the clipping winding rule.
      * 
-     * @param windingRule
-     *            The winding rule which will be used for clipping.
+     * @param windingRule The winding rule which will be used for clipping.
      * 
      */
     public void setClippingWindingRule(int windingRule)
@@ -637,10 +796,8 @@ public class PageDrawer extends PDFStrea
      * Draw the AWT image. Called by Invoke. Moved into PageDrawer so that Invoke doesn't have to reach in here for
      * Graphics as that breaks extensibility.
      * 
-     * @param awtImage
-     *            The image to draw.
-     * @param at
-     *            The transformation to use when drawing.
+     * @param awtImage The image to draw.
+     * @param at The transformation to use when drawing.
      * 
      */
     public void drawImage(Image awtImage, AffineTransform at)
@@ -653,11 +810,9 @@ public class PageDrawer extends PDFStrea
     /**
      * Fill with Shading. Called by SHFill operator.
      * 
-     * @param ShadingName
-     *            The name of the Shading Dictionary to use for this fill instruction.
+     * @param ShadingName The name of the Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      * 
      * @deprecated use {@link #shFill(COSName)) instead.
      */
@@ -669,11 +824,9 @@ public class PageDrawer extends PDFStrea
     /**
      * Fill with Shading. Called by SHFill operator.
      * 
-     * @param shadingName
-     *            The name of the Shading Dictionary to use for this fill instruction.
+     * @param shadingName The name of the Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the clipping area.
+     * @throws IOException If there is an IO error while shade-filling the clipping area.
      */
     public void shFill(COSName shadingName) throws IOException
     {
@@ -714,11 +867,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Function-based gradient / shading. If extending the class, override this and its siblings, not the
      * public SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_Function(PDShading Shading) throws IOException
     {
@@ -728,11 +879,9 @@ public class PageDrawer extends PDFStrea
     /**
      * Fill with an Axial Shading. If extending the class, override this and its siblings, not the public SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_Axial(PDShading Shading) throws IOException
     {
@@ -744,11 +893,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Radial gradient / shading. If extending the class, override this and its siblings, not the public
      * SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_Radial(PDShading Shading) throws IOException
     {
@@ -759,11 +906,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Free-form Gourad-shaded triangle mesh. If extending the class, override this and its siblings, not
      * the public SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_FreeGourad(PDShading Shading) throws IOException
     {
@@ -774,11 +919,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Lattice-form Gourad-shaded triangle mesh. If extending the class, override this and its siblings, not
      * the public SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_LatticeGourad(PDShading Shading) throws IOException
     {
@@ -789,11 +932,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Coons patch mesh If extending the class, override this and its siblings, not the public SHFill
      * method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_CoonsPatch(PDShading Shading) throws IOException
     {
@@ -804,11 +945,9 @@ public class PageDrawer extends PDFStrea
      * Fill with a Tensor-product patch mesh. If extending the class, override this and its siblings, not the public
      * SHFill method.
      * 
-     * @param Shading
-     *            The Shading Dictionary to use for this fill instruction.
+     * @param Shading The Shading Dictionary to use for this fill instruction.
      * 
-     * @throws IOException
-     *             If there is an IO error while shade-filling the path/clipping area.
+     * @throws IOException If there is an IO error while shade-filling the path/clipping area.
      */
     protected void SHFill_TensorPatch(PDShading Shading) throws IOException
     {

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java?rev=1506667&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java Wed Jul 24 19:24:43 2013
@@ -0,0 +1,62 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.pdfbox.pdfviewer.font;
+
+import java.awt.geom.GeneralPath;
+
+/**
+ * This interface is implemented by several font specific classes which is called to get the general path of a single
+ * glyph of the represented font most likely to render it.
+ * 
+ */
+public interface Glyph2D
+{
+
+    /**
+     * Returns the path describing the glyph for the given glyphId.
+     * 
+     * @param glyphId the glyphId
+     * 
+     * @return the GeneralPath for the given glyphId
+     */
+    public GeneralPath getPathForGlyphId(int glyphId);
+
+    /**
+     * Returns the path describing the glyph for the given character code.
+     * 
+     * @param code the character code
+     * 
+     * @return the GeneralPath for the given character code
+     */
+    public GeneralPath getPathForCharactercode(int code);
+
+    /**
+     * Returns the number of glyphs provided by the given font.
+     * 
+     * @return the number of glyphs
+     */
+    public int getNumberOfGlyphs();
+
+    /**
+     * Remove all cached resources.
+     */
+    public void dispose();
+
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/Glyph2D.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java?rev=1506667&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java Wed Jul 24 19:24:43 2013
@@ -0,0 +1,434 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.pdfbox.pdfviewer.font;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.cmap.CMap;
+import org.apache.fontbox.ttf.CMAPEncodingEntry;
+import org.apache.fontbox.ttf.CMAPTable;
+import org.apache.fontbox.ttf.GlyfDescript;
+import org.apache.fontbox.ttf.GlyphData;
+import org.apache.fontbox.ttf.GlyphDescription;
+import org.apache.fontbox.ttf.HeaderTable;
+import org.apache.fontbox.ttf.TrueTypeFont;
+
+/**
+ * This class provides a glyph to GeneralPath conversion for true type fonts.
+ * 
+ * This class is based on code from Apache Batik a subproject of Apache XMLGraphics. see
+ * http://xmlgraphics.apache.org/batik/ for further details.
+ */
+public class TTFGlyph2D implements Glyph2D
+{
+
+    /**
+     * Log instance.
+     */
+    private static final Log LOG = LogFactory.getLog(TTFGlyph2D.class);
+
+    private TrueTypeFont font;
+    private String name;
+    private float scale = 0.001f;
+    private CMAPEncodingEntry cmapMiscUnicode = null;
+    private CMAPEncodingEntry cmapWinUnicode = null;
+    private CMAPEncodingEntry cmapWinSymbol = null;
+    private CMAPEncodingEntry cmapMacintoshSymbol = null;
+    private boolean isSymbol = false;
+    private HashMap<Integer, GeneralPath> glyphs = new HashMap<Integer, GeneralPath>();
+    private CMap toUnicode = null;
+    private int[] cid2gid = null;
+
+    /**
+     * Constructor.
+     * 
+     * @param trueTypeFont the true type font containing the glyphs
+     * @param fontname the name of the given font
+     * @param symbolFont indicates if the font is a symbolic font
+     * 
+     */
+    public TTFGlyph2D(TrueTypeFont trueTypeFont, String fontname, boolean symbolFont)
+    {
+        this(trueTypeFont, fontname, symbolFont, null);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param trueTypeFont the true type font containing the glyphs
+     * @param fontname the name of the given font
+     * @param symbolFont indicates if the font is a symbolic font
+     * @param toUnicodeCMap an optional toUnicode mapping
+     * 
+     */
+    public TTFGlyph2D(TrueTypeFont trueTypeFont, String fontname, boolean symbolFont, CMap toUnicodeCMap)
+    {
+        this(trueTypeFont, fontname, symbolFont, toUnicodeCMap, null);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param trueTypeFont the true type font containing the glyphs
+     * @param fontname the name of the given font
+     * @param symbolFont indicates if the font is a symbolic font
+     * @param toUnicodeCMap an optional toUnicode mapping
+     * @param cid2gidMapping an optional CID2GIC mapping
+     * 
+     */
+    public TTFGlyph2D(TrueTypeFont trueTypeFont, String fontname, boolean symbolFont, CMap toUnicodeCMap,
+            int[] cid2gidMapping)
+    {
+        font = trueTypeFont;
+        isSymbol = symbolFont;
+        name = fontname;
+        toUnicode = toUnicodeCMap;
+        cid2gid = cid2gidMapping;
+        // get units per em, which is used as scaling factor
+        HeaderTable header = font.getHeader();
+        if (header != null)
+        {
+            scale = 1f / header.getUnitsPerEm();
+        }
+        CMAPTable cmapTable = font.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];
+                    }
+                }
+                else if (CMAPTable.PLATFORM_MISC == cmaps[i].getPlatformId())
+                {
+                    if (CMAPTable.ENCODING_UNICODE == cmaps[i].getPlatformEncodingId())
+                    {
+                        cmapMiscUnicode = cmaps[i];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Set the points of a glyph from the GlyphDescription.
+     */
+    private Point[] describe(GlyphDescription gd)
+    {
+        int endPtIndex = 0;
+        Point[] points = new Point[gd.getPointCount()];
+        for (int i = 0; i < gd.getPointCount(); i++)
+        {
+            boolean endPt = gd.getEndPtOfContours(endPtIndex) == i;
+            if (endPt)
+            {
+                endPtIndex++;
+            }
+            points[i] = new Point(gd.getXCoordinate(i), -gd.getYCoordinate(i),
+                    (gd.getFlags(i) & GlyfDescript.ON_CURVE) != 0, endPt);
+        }
+        return points;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public GeneralPath getPathForGlyphId(int glyphId)
+    {
+        GeneralPath glyphPath = null;
+        if (glyphs.containsKey(glyphId))
+        {
+            glyphPath = glyphs.get(glyphId);
+        }
+        else
+        {
+            GlyphData[] glyphData = font.getGlyph().getGlyphs();
+            if (glyphId < glyphData.length && glyphData[glyphId] != null)
+            {
+                GlyphData glyph = glyphData[glyphId];
+                GlyphDescription gd = glyph.getDescription();
+                Point[] points = describe(gd);
+                glyphPath = calculatePath(points);
+                AffineTransform atScale = AffineTransform.getScaleInstance(scale, scale);
+                glyphPath.transform(atScale);
+                glyphs.put(glyphId, glyphPath);
+            }
+            else
+            {
+                LOG.debug(name + ": Glyph not found:" + glyphId);
+            }
+        }
+        return glyphPath != null ? (GeneralPath) glyphPath.clone() : null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public GeneralPath getPathForCharactercode(int code)
+    {
+        if (isSymbol)
+        {
+            // symbol fonts
+            if (cmapWinSymbol != null)
+            {
+                int glyphId = cmapWinSymbol.getGlyphId(code);
+                // microsoft sometimes uses PUA unicode values for symbol fonts
+                // the range 0x0020 - 0x00FF maps to 0xF020 - 0xF0FF
+                if (glyphId == 0 && code >= 0x0020 && code <= 0x00FF)
+                {
+                    glyphId = cmapWinSymbol.getGlyphId(code + 0xF000);
+                }
+                return getPathForGlyphId(glyphId);
+            }
+        }
+        else
+        {
+            // non symbol fonts
+            // Unicode mapping
+            if (cmapWinUnicode != null)
+            {
+                return getPathForGlyphId(cmapWinUnicode.getGlyphId(code));
+            }
+            // some fonts provide a custom CMap
+            if (cmapMiscUnicode != null)
+            {
+                int unicode = code;
+                // map the given code to a valid unicode value, if necessary
+                if (toUnicode != null)
+                {
+                    String unicodeStr = toUnicode.lookup(code, 1);
+                    if (unicodeStr != null)
+                    {
+                        unicode = unicodeStr.codePointAt(0);
+                    }
+                }
+                return getPathForGlyphId(cmapMiscUnicode.getGlyphId(unicode));
+            }
+            // use a mac related mapping
+            if (cmapMacintoshSymbol != null)
+            {
+                return getPathForGlyphId(cmapMacintoshSymbol.getGlyphId(code));
+            }
+        }
+        // there isn't any mpping, but propably an optional CID2GID mapping
+        if (cid2gid != null && code <= cid2gid.length)
+        {
+            code = cid2gid[code];
+        }
+        return getPathForGlyphId(code);
+    }
+
+    /**
+     * Use the given points to calculate a GeneralPath.
+     * 
+     * @param points the points to be used to generate the GeneralPath
+     * 
+     * @return the calculated GeneralPath
+     */
+    private GeneralPath calculatePath(Point[] points)
+    {
+        GeneralPath path = new GeneralPath();
+        int numberOfPoints = points.length;
+        int i = 0;
+        boolean endOfContour = true;
+        Point startingPoint = null;
+        Point lastCtrlPoint = null;
+        while (i < numberOfPoints)
+        {
+            Point point = points[i % numberOfPoints];
+            Point nextPoint1 = points[(i + 1) % numberOfPoints];
+            Point nextPoint2 = points[(i + 2) % numberOfPoints];
+            // new contour
+            if (endOfContour)
+            {
+                // skip endOfContour points
+                if (point.endOfContour)
+                {
+                    i++;
+                    continue;
+                }
+                // move to the starting point
+                path.moveTo(point.x, point.y);
+                endOfContour = false;
+                startingPoint = point;
+            }
+            // lineTo
+            if (point.onCurve && nextPoint1.onCurve)
+            {
+                path.lineTo(nextPoint1.x, nextPoint1.y);
+                i++;
+                if (point.endOfContour || nextPoint1.endOfContour)
+                {
+                    endOfContour = true;
+                    path.closePath();
+                }
+                continue;
+            }
+            // quadratic bezier
+            if (point.onCurve && !nextPoint1.onCurve && nextPoint2.onCurve)
+            {
+                if (nextPoint1.endOfContour)
+                {
+                    // use the starting point as end point
+                    path.quadTo(nextPoint1.x, nextPoint1.y, startingPoint.x, startingPoint.y);
+                }
+                else
+                {
+                    path.quadTo(nextPoint1.x, nextPoint1.y, nextPoint2.x, nextPoint2.y);
+                }
+                if (nextPoint1.endOfContour || nextPoint2.endOfContour)
+                {
+                    endOfContour = true;
+                    path.closePath();
+                }
+                i += 2;
+                lastCtrlPoint = nextPoint1;
+                continue;
+            }
+            if (point.onCurve && !nextPoint1.onCurve && !nextPoint2.onCurve)
+            {
+                // interpolate endPoint
+                int endPointX = midValue(nextPoint1.x, nextPoint2.x);
+                int endPointY = midValue(nextPoint1.y, nextPoint2.y);
+                path.quadTo(nextPoint1.x, nextPoint1.y, endPointX, endPointY);
+                if (point.endOfContour || nextPoint1.endOfContour || nextPoint2.endOfContour)
+                {
+                    path.quadTo(nextPoint2.x, nextPoint2.y, startingPoint.x, startingPoint.y);
+                    endOfContour = true;
+                    path.closePath();
+                }
+                i += 2;
+                lastCtrlPoint = nextPoint1;
+                continue;
+            }
+            if (!point.onCurve && !nextPoint1.onCurve)
+            {
+                Point2D lastEndPoint = path.getCurrentPoint();
+                // calculate new control point using the previous control point
+                lastCtrlPoint = new Point(midValue(lastCtrlPoint.x, (int) lastEndPoint.getX()), midValue(
+                        lastCtrlPoint.y, (int) lastEndPoint.getY()));
+                // interpolate endPoint
+                int endPointX = midValue((int) lastEndPoint.getX(), nextPoint1.x);
+                int endPointY = midValue((int) lastEndPoint.getY(), nextPoint1.y);
+                path.quadTo(lastCtrlPoint.x, lastCtrlPoint.y, endPointX, endPointY);
+                if (point.endOfContour || nextPoint1.endOfContour)
+                {
+                    endOfContour = true;
+                    path.closePath();
+                }
+                i++;
+                continue;
+            }
+            if (!point.onCurve && nextPoint1.onCurve)
+            {
+                path.quadTo(point.x, point.y, nextPoint1.x, nextPoint1.y);
+                if (point.endOfContour || nextPoint1.endOfContour)
+                {
+                    endOfContour = true;
+                    path.closePath();
+                }
+                i++;
+                lastCtrlPoint = point;
+                continue;
+            }
+            System.err.println("Unknown glyph command!!");
+            break;
+        }
+        return path;
+    }
+
+    private int midValue(int a, int b)
+    {
+        return a + (b - a) / 2;
+    }
+
+    /**
+     * This class represents one point of a glyph.
+     * 
+     */
+    private class Point
+    {
+
+        public int x = 0;
+        public int y = 0;
+        public boolean onCurve = true;
+        public boolean endOfContour = false;
+
+        public Point(int xValue, int yValue, boolean onCurveValue, boolean endOfContourValue)
+        {
+            x = xValue;
+            y = yValue;
+            onCurve = onCurveValue;
+            endOfContour = endOfContourValue;
+        }
+
+        public Point(int xValue, int yValue)
+        {
+            this(xValue, yValue, false, false);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumberOfGlyphs()
+    {
+        return font != null ? font.getGlyph().getGlyphs().length : 0;
+    }
+
+    @Override
+    public void dispose()
+    {
+        cid2gid = null;
+        cmapMacintoshSymbol = null;
+        cmapMiscUnicode = null;
+        cmapWinSymbol = null;
+        cmapWinUnicode = null;
+        font = null;
+        toUnicode = null;
+        if (glyphs != null)
+        {
+            glyphs.clear();
+        }
+    }
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfviewer/font/TTFGlyph2D.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java?rev=1506667&r1=1506666&r2=1506667&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java Wed Jul 24 19:24:43 2013
@@ -16,12 +16,12 @@
  */
 package org.apache.pdfbox.pdmodel.font;
 
-import java.awt.Font;
-import java.awt.FontFormatException;
 import java.io.IOException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TrueTypeFont;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
@@ -31,7 +31,7 @@ import org.apache.pdfbox.pdmodel.common.
 
 /**
  * This is implementation of the CIDFontType2 Font.
- *
+ * 
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * 
  */
@@ -45,56 +45,24 @@ public class PDCIDFontType2Font extends 
 
     private Boolean hasCIDToGIDMap = null;
     private int[] cid2gid = null;
-    
+
     /**
      * Constructor.
      */
     public PDCIDFontType2Font()
     {
         super();
-        font.setItem( COSName.SUBTYPE, COSName.CID_FONT_TYPE2 );
+        font.setItem(COSName.SUBTYPE, COSName.CID_FONT_TYPE2);
     }
 
     /**
      * Constructor.
-     *
+     * 
      * @param fontDictionary The font dictionary according to the PDF specification.
      */
-    public PDCIDFontType2Font( COSDictionary fontDictionary )
+    public PDCIDFontType2Font(COSDictionary fontDictionary)
     {
-        super( fontDictionary );
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    public Font getawtFont() throws IOException
-    {
-        Font awtFont = null;
-        PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor();
-        PDStream ff2Stream = fd.getFontFile2();
-        if( ff2Stream != null )
-        {
-            try
-            {
-                // create a font with the embedded data
-                awtFont = Font.createFont( Font.TRUETYPE_FONT, ff2Stream.createInputStream() );
-            }
-            catch( FontFormatException f )
-            {
-                LOG.info("Can't read the embedded font " + fd.getFontName() );
-            }
-            if (awtFont == null)
-            {
-                awtFont = FontManager.getAwtFont(fd.getFontName());
-                if (awtFont != null)
-                {
-                    LOG.info("Using font "+awtFont.getName()+ " instead");
-                }
-                setIsFontSubstituted(true);
-            }
-        }
-        return awtFont;
+        super(fontDictionary);
     }
 
     /**
@@ -103,28 +71,28 @@ public class PDCIDFontType2Font extends 
     private void readCIDToGIDMapping()
     {
         COSBase map = font.getDictionaryObject(COSName.CID_TO_GID_MAP);
-        if (map instanceof COSStream) 
+        if (map instanceof COSStream)
         {
-            COSStream stream = (COSStream)map;
-            try 
+            COSStream stream = (COSStream) map;
+            try
             {
                 byte[] mapAsBytes = IOUtils.toByteArray(stream.getUnfilteredStream());
                 int numberOfInts = mapAsBytes.length / 2;
                 cid2gid = new int[numberOfInts];
                 int offset = 0;
-                for(int index = 0;index < numberOfInts;index++)
+                for (int index = 0; index < numberOfInts; index++)
                 {
                     cid2gid[index] = getCodeFromArray(mapAsBytes, offset, 2);
-                    offset+=2;
+                    offset += 2;
                 }
             }
-            catch(IOException exception)
+            catch (IOException exception)
             {
                 LOG.error("Can't read the CIDToGIDMap", exception);
             }
         }
     }
-    
+
     /**
      * Indicates if this font has a CIDToGIDMap.
      * 
@@ -146,16 +114,16 @@ public class PDCIDFontType2Font extends 
         }
         return hasCIDToGIDMap.booleanValue();
     }
-    
+
     /**
      * Maps the given CID to the correspondent GID.
      * 
      * @param cid the given CID
      * @return the mapped GID, or -1 if something went wrong.
      */
-    public int mapCIDToGID(int cid) 
+    public int mapCIDToGID(int cid)
     {
-        if (hasCIDToGIDMap()) 
+        if (hasCIDToGIDMap())
         {
             if (cid2gid == null)
             {
@@ -173,4 +141,41 @@ public class PDCIDFontType2Font extends 
             return cid;
         }
     }
+
+    /**
+     * Returns the CID2GID mapping if present.
+     * 
+     * @return the CID2GID mapping
+     */
+    public int[] getCID2GID()
+    {
+        if (hasCIDToGIDMap())
+        {
+            if (cid2gid == null)
+            {
+                readCIDToGIDMapping();
+            }
+        }
+        return cid2gid;
+    }
+
+    /**
+     * Returns the embedded true type font.
+     * 
+     * @return the true type font
+     * @throws IOException exception if something went wrong
+     */
+    public TrueTypeFont getTTFFont() throws IOException
+    {
+        PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary) getFontDescriptor();
+        PDStream ff2Stream = fd.getFontFile2();
+        TrueTypeFont trueTypeFont = null;
+        if (ff2Stream != null)
+        {
+            TTFParser ttfParser = new TTFParser(true);
+            trueTypeFont = ttfParser.parseTTF(ff2Stream.createInputStream());
+        }
+        return trueTypeFont;
+    }
+
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java?rev=1506667&r1=1506666&r2=1506667&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java Wed Jul 24 19:24:43 2013
@@ -16,13 +16,18 @@
  */
 package org.apache.pdfbox.pdmodel.font;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.fontbox.afm.AFMParser;
 import org.apache.fontbox.afm.FontMetric;
-import org.apache.fontbox.cmap.CMapParser;
 import org.apache.fontbox.cmap.CMap;
-
-import org.apache.pdfbox.encoding.Encoding;
-
+import org.apache.fontbox.cmap.CMapParser;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
@@ -30,28 +35,18 @@ import org.apache.pdfbox.cos.COSFloat;
 import org.apache.pdfbox.cos.COSInteger;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSString;
-
+import org.apache.pdfbox.encoding.Encoding;
 import org.apache.pdfbox.pdmodel.common.COSArrayList;
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.pdmodel.common.PDMatrix;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
-
 import org.apache.pdfbox.util.ResourceLoader;
 
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 /**
  * This is the base class for all PDF fonts.
- *
+ * 
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.46 $
+ * 
  */
 public abstract class PDFont implements COSObjectable
 {
@@ -67,18 +62,17 @@ public abstract class PDFont implements 
     private Encoding fontEncoding = null;
 
     /**
-     *  The descriptor of the font.
+     * The descriptor of the font.
      */
     private PDFontDescriptor fontDescriptor = null;
 
     /**
-     *  The font matrix.
+     * The font matrix.
      */
     protected PDMatrix fontMatrix = null;
 
     /**
-     * This is only used if this is a font object and it has an encoding and it is
-     * a type0 font with a cmap.
+     * This is only used if this is a font object and it has an encoding and it is a type0 font with a cmap.
      */
     protected CMap cmap = null;
 
@@ -86,62 +80,59 @@ public abstract class PDFont implements 
      * The CMap holding the ToUnicode mapping.
      */
     protected CMap toUnicodeCmap = null;
-    
+
     private boolean hasToUnicode = false;
 
-    protected static Map<String, CMap> cmapObjects =
-        Collections.synchronizedMap( new HashMap<String, CMap>() );
+    protected static Map<String, CMap> cmapObjects = Collections.synchronizedMap(new HashMap<String, CMap>());
 
     /**
-     *  A list a floats representing the widths.
+     * A list a floats representing the widths.
      */
     private List<Float> widths = null;
 
     /**
      * The static map of the default Adobe font metrics.
      */
-    private static final Map<String, FontMetric> afmObjects =
-        Collections.unmodifiableMap( getAdobeFontMetrics() );
+    private static final Map<String, FontMetric> afmObjects = Collections.unmodifiableMap(getAdobeFontMetrics());
 
     // TODO move the Map to PDType1Font as these are the 14 Standard fonts
     // which are definitely Type 1 fonts
     private static Map<String, FontMetric> getAdobeFontMetrics()
     {
         Map<String, FontMetric> metrics = new HashMap<String, FontMetric>();
-        addAdobeFontMetric( metrics, "Courier-Bold" );
-        addAdobeFontMetric( metrics, "Courier-BoldOblique" );
-        addAdobeFontMetric( metrics, "Courier" );
-        addAdobeFontMetric( metrics, "Courier-Oblique" );
-        addAdobeFontMetric( metrics, "Helvetica" );
-        addAdobeFontMetric( metrics, "Helvetica-Bold" );
-        addAdobeFontMetric( metrics, "Helvetica-BoldOblique" );
-        addAdobeFontMetric( metrics, "Helvetica-Oblique" );
-        addAdobeFontMetric( metrics, "Symbol" );
-        addAdobeFontMetric( metrics, "Times-Bold" );
-        addAdobeFontMetric( metrics, "Times-BoldItalic" );
-        addAdobeFontMetric( metrics, "Times-Italic" );
-        addAdobeFontMetric( metrics, "Times-Roman" );
-        addAdobeFontMetric( metrics, "ZapfDingbats" );
+        addAdobeFontMetric(metrics, "Courier-Bold");
+        addAdobeFontMetric(metrics, "Courier-BoldOblique");
+        addAdobeFontMetric(metrics, "Courier");
+        addAdobeFontMetric(metrics, "Courier-Oblique");
+        addAdobeFontMetric(metrics, "Helvetica");
+        addAdobeFontMetric(metrics, "Helvetica-Bold");
+        addAdobeFontMetric(metrics, "Helvetica-BoldOblique");
+        addAdobeFontMetric(metrics, "Helvetica-Oblique");
+        addAdobeFontMetric(metrics, "Symbol");
+        addAdobeFontMetric(metrics, "Times-Bold");
+        addAdobeFontMetric(metrics, "Times-BoldItalic");
+        addAdobeFontMetric(metrics, "Times-Italic");
+        addAdobeFontMetric(metrics, "Times-Roman");
+        addAdobeFontMetric(metrics, "ZapfDingbats");
         return metrics;
     }
 
     protected static final String resourceRootCMAP = "org/apache/pdfbox/resources/cmap/";
     private static final String resourceRootAFM = "org/apache/pdfbox/resources/afm/";
 
-    private static void addAdobeFontMetric(
-            Map<String, FontMetric> metrics, String name )
+    private static void addAdobeFontMetric(Map<String, FontMetric> metrics, String name)
     {
         try
         {
             String resource = resourceRootAFM + name + ".afm";
-            InputStream afmStream = ResourceLoader.loadResource( resource );
-            if( afmStream != null )
+            InputStream afmStream = ResourceLoader.loadResource(resource);
+            if (afmStream != null)
             {
                 try
                 {
-                    AFMParser parser = new AFMParser( afmStream );
+                    AFMParser parser = new AFMParser(afmStream);
                     parser.parse();
-                    metrics.put( name, parser.getResult() );
+                    metrics.put(name, parser.getResult());
                 }
                 finally
                 {
@@ -156,15 +147,13 @@ public abstract class PDFont implements 
     }
 
     /**
-     * This will clear AFM resources that are stored statically.
-     * This is usually not a problem unless you want to reclaim
-     * resources for a long running process.
-     *
-     * SPECIAL NOTE: The font calculations are currently in COSObject, which
-     * is where they will reside until PDFont is mature enough to take them over.
-     * PDFont is the appropriate place for them and not in COSObject but we need font
-     * calculations for text extraction.  THIS METHOD WILL BE MOVED OR REMOVED
-     * TO ANOTHER LOCATION IN A FUTURE VERSION OF PDFBOX.
+     * This will clear AFM resources that are stored statically. This is usually not a problem unless you want to
+     * reclaim resources for a long running process.
+     * 
+     * SPECIAL NOTE: The font calculations are currently in COSObject, which is where they will reside until PDFont is
+     * mature enough to take them over. PDFont is the appropriate place for them and not in COSObject but we need font
+     * calculations for text extraction. THIS METHOD WILL BE MOVED OR REMOVED TO ANOTHER LOCATION IN A FUTURE VERSION OF
+     * PDFBOX.
      */
     public static void clearResources()
     {
@@ -177,15 +166,15 @@ public abstract class PDFont implements 
     public PDFont()
     {
         font = new COSDictionary();
-        font.setItem( COSName.TYPE, COSName.FONT );
+        font.setItem(COSName.TYPE, COSName.FONT);
     }
 
     /**
      * Constructor.
-     *
+     * 
      * @param fontDictionary The font dictionary according to the PDF specification.
      */
-    public PDFont( COSDictionary fontDictionary )
+    public PDFont(COSDictionary fontDictionary)
     {
         font = fontDictionary;
         determineEncoding();
@@ -193,25 +182,25 @@ public abstract class PDFont implements 
 
     /**
      * This will get the font descriptor for this font.
-     *
+     * 
      * @return The font descriptor for this font.
-     *
+     * 
      */
     public PDFontDescriptor getFontDescriptor()
     {
-        if(fontDescriptor == null)
+        if (fontDescriptor == null)
         {
-            COSDictionary fd = (COSDictionary)font.getDictionaryObject( COSName.FONT_DESC );
+            COSDictionary fd = (COSDictionary) font.getDictionaryObject(COSName.FONT_DESC);
             if (fd != null)
             {
-                fontDescriptor = new PDFontDescriptorDictionary( fd );
+                fontDescriptor = new PDFontDescriptorDictionary(fd);
             }
             else
             {
                 getAFM();
-                if( afm != null )
+                if (afm != null)
                 {
-                    fontDescriptor = new PDFontDescriptorAFM( afm );
+                    fontDescriptor = new PDFontDescriptorAFM(afm);
                 }
             }
         }
@@ -220,24 +209,23 @@ public abstract class PDFont implements 
 
     /**
      * This will set the font descriptor.
-     *
+     * 
      * @param fdDictionary The font descriptor.
      */
-    public void setFontDescriptor( PDFontDescriptorDictionary fdDictionary )
+    public void setFontDescriptor(PDFontDescriptorDictionary fdDictionary)
     {
         COSDictionary dic = null;
-        if( fdDictionary != null )
+        if (fdDictionary != null)
         {
             dic = fdDictionary.getCOSDictionary();
         }
-        font.setItem( COSName.FONT_DESC, dic );
+        font.setItem(COSName.FONT_DESC, dic);
         fontDescriptor = fdDictionary;
     }
 
     /**
-     * Determines the encoding for the font.
-     * This method as to be overwritten, as there are different
-     * possibilities to define a mapping.
+     * Determines the encoding for the font. This method as to be overwritten, as there are different possibilities to
+     * define a mapping.
      */
     protected abstract void determineEncoding();
 
@@ -251,112 +239,112 @@ public abstract class PDFont implements 
 
     /**
      * This will get the font width for a character.
-     *
+     * 
      * @param c The character code to get the width for.
      * @param offset The offset into the array.
      * @param length The length of the data.
-     *
+     * 
      * @return The width is in 1000 unit of text space, ie 333 or 777
-     *
+     * 
      * @throws IOException If an error occurs while parsing.
      */
-    public abstract float getFontWidth( byte[] c, int offset, int length ) throws IOException;
+    public abstract float getFontWidth(byte[] c, int offset, int length) throws IOException;
 
     /**
      * This will get the font width for a character.
-     *
+     * 
      * @param c The character code to get the width for.
      * @param offset The offset into the array.
      * @param length The length of the data.
-     *
+     * 
      * @return The width is in 1000 unit of text space, ie 333 or 777
-     *
+     * 
      * @throws IOException If an error occurs while parsing.
      */
-    public abstract float getFontHeight( byte[] c, int offset, int length ) throws IOException;
+    public abstract float getFontHeight(byte[] c, int offset, int length) throws IOException;
 
     /**
      * This will get the width of this string for this font.
-     *
+     * 
      * @param string The string to get the width of.
-     *
+     * 
      * @return The width of the string in 1000 units of text space, ie 333 567...
-     *
+     * 
      * @throws IOException If there is an error getting the width information.
      */
-    public float getStringWidth( String string ) throws IOException
+    public float getStringWidth(String string) throws IOException
     {
         byte[] data = string.getBytes("ISO-8859-1");
         float totalWidth = 0;
-        for( int i=0; i<data.length; i++ )
+        for (int i = 0; i < data.length; i++)
         {
-            totalWidth+=getFontWidth( data, i, 1 );
+            totalWidth += getFontWidth(data, i, 1);
         }
         return totalWidth;
     }
 
     /**
      * This will get the average font width for all characters.
-     *
+     * 
      * @return The width is in 1000 unit of text space, ie 333 or 777
-     *
+     * 
      * @throws IOException If an error occurs while parsing.
      */
     public abstract float getAverageFontWidth() throws IOException;
 
     /**
      * Used for multibyte encodings.
-     *
+     * 
      * @param data The array of data.
      * @param offset The offset into the array.
      * @param length The number of bytes to use.
-     *
+     * 
      * @return The int value of data from the array.
      */
-    public int getCodeFromArray( byte[] data, int offset, int length )
+    public int getCodeFromArray(byte[] data, int offset, int length)
     {
         int code = 0;
-        for( int i=0; i<length; i++ )
+        for (int i = 0; i < length; i++)
         {
             code <<= 8;
-            code |= (data[offset+i]+256)%256;
+            code |= (data[offset + i] + 256) % 256;
         }
         return code;
     }
 
     /**
      * This will attempt to get the font width from an AFM file.
-     *
+     * 
      * @param code The character code we are trying to get.
-     *
+     * 
      * @return The font width from the AFM file.
-     *
+     * 
      * @throws IOException if we cannot find the width.
      */
-    protected float getFontWidthFromAFMFile( int code ) throws IOException
+    protected float getFontWidthFromAFMFile(int code) throws IOException
     {
         float retval = 0;
         FontMetric metric = getAFM();
-        if( metric != null )
+        if (metric != null)
         {
-            String characterName = fontEncoding.getName( code );
-            retval = metric.getCharacterWidth( characterName );
+            String characterName = fontEncoding.getName(code);
+            retval = metric.getCharacterWidth(characterName);
         }
         return retval;
     }
 
     /**
      * This will attempt to get the average font width from an AFM file.
-     *
+     * 
      * @return The average font width from the AFM file.
-     *
+     * 
      * @throws IOException if we cannot find the width.
      */
     protected float getAverageFontWidthFromAFMFile() throws IOException
     {
         float retval = 0;
         FontMetric metric = getAFM();
-        if( metric != null )
+        if (metric != null)
         {
             retval = metric.getAverageCharacterWidth();
         }
@@ -365,33 +353,33 @@ public abstract class PDFont implements 
 
     /**
      * This will get an AFM object if one exists.
-     *
+     * 
      * @return The afm object from the name.
-     *
+     * 
      */
     protected FontMetric getAFM()
     {
         if (isType1Font() && afm == null)
         {
-            COSBase baseFont = font.getDictionaryObject( COSName.BASE_FONT );
+            COSBase baseFont = font.getDictionaryObject(COSName.BASE_FONT);
             String name = null;
-            if( baseFont instanceof COSName )
+            if (baseFont instanceof COSName)
             {
-                name = ((COSName)baseFont).getName();
+                name = ((COSName) baseFont).getName();
                 if (name.indexOf("+") > -1)
                 {
-                    name = name.substring(name.indexOf("+")+1);
+                    name = name.substring(name.indexOf("+") + 1);
                 }
 
             }
-            else if ( baseFont instanceof COSString )
+            else if (baseFont instanceof COSString)
             {
-                COSString string = (COSString)baseFont;
+                COSString string = (COSString) baseFont;
                 name = string.getString();
             }
-            if ( name != null )
+            if (name != null)
             {
-                afm = afmObjects.get( name );
+                afm = afmObjects.get(name);
             }
         }
         return afm;
@@ -400,46 +388,49 @@ public abstract class PDFont implements 
     private FontMetric afm = null;
 
     private COSBase encoding = null;
+
     /**
-     * cache the {@link COSName#ENCODING} object from
-     * the font's dictionary since it is called so often.
+     * cache the {@link COSName#ENCODING} object from the font's dictionary since it is called so often.
      * <p>
      * Use this method instead of
+     * 
      * <pre>
-     *   font.getDictionaryObject(COSName.ENCODING);
+     * font.getDictionaryObject(COSName.ENCODING);
      * </pre>
+     * 
      * @return the encoding
      */
     protected COSBase getEncoding()
     {
         if (encoding == null)
         {
-            encoding = font.getDictionaryObject( COSName.ENCODING );
+            encoding = font.getDictionaryObject(COSName.ENCODING);
         }
         return encoding;
     }
 
     /**
      * Set the encoding object from the fonts dictionary.
+     * 
      * @param encodingValue the given encoding.
      */
     protected void setEncoding(COSBase encodingValue)
     {
-        font.setItem( COSName.ENCODING, encodingValue );
+        font.setItem(COSName.ENCODING, encodingValue);
         encoding = encodingValue;
     }
 
     /**
      * Encode the given value using the CMap of the font.
-     *
+     * 
      * @param code the code to encode.
      * @param length the byte length of the given code.
      * @param isCIDFont indicates that the used font is a CID font.
-     *
+     * 
      * @return The value of the encoded character.
      * @throws IOException if something went wrong
      */
-    protected String cmapEncoding( int code, int length, boolean isCIDFont, CMap sourceCmap ) throws IOException
+    protected String cmapEncoding(int code, int length, boolean isCIDFont, CMap sourceCmap) throws IOException
     {
         String retval = null;
         // there is not sourceCmap if this is a descendant font
@@ -457,75 +448,76 @@ public abstract class PDFont implements 
         }
         return retval;
     }
+
     /**
      * This will perform the encoding of a character if needed.
-     *
+     * 
      * @param c The character to encode.
      * @param offset The offset into the array to get the data
      * @param length The number of bytes to read.
-     *
+     * 
      * @return The value of the encoded character.
-     *
+     * 
      * @throws IOException If there is an error during the encoding.
      */
-    public String encode( byte[] c, int offset, int length ) throws IOException
+    public String encode(byte[] c, int offset, int length) throws IOException
     {
         String retval = null;
-        int code = getCodeFromArray( c, offset, length );
-        if( toUnicodeCmap != null )
+        int code = getCodeFromArray(c, offset, length);
+        if (toUnicodeCmap != null)
         {
             retval = cmapEncoding(code, length, false, toUnicodeCmap);
         }
-        if( retval == null && cmap != null )
+        if (retval == null && cmap != null)
         {
             retval = cmapEncoding(code, length, false, cmap);
         }
 
         // there is no cmap but probably an encoding with a suitable mapping
-        if( retval == null )
+        if (retval == null)
         {
-            if( fontEncoding != null )
+            if (fontEncoding != null)
             {
-                retval = fontEncoding.getCharacter( code );
+                retval = fontEncoding.getCharacter(code);
             }
-            if( retval == null && (cmap == null || length == 2))
+            if (retval == null && (cmap == null || length == 2))
             {
-                retval = getStringFromArray( c, offset, length );
+                retval = getStringFromArray(c, offset, length);
             }
         }
         return retval;
     }
 
-    public int encodeToCID( byte[] c, int offset, int length ) throws IOException
+    public int encodeToCID(byte[] c, int offset, int length) throws IOException
     {
         int code = -1;
-        if (encode(c, offset, length) != null) 
+        if (encode(c, offset, length) != null)
         {
-            code = getCodeFromArray( c, offset, length );
+            code = getCodeFromArray(c, offset, length);
         }
         return code;
     }
-    
+
     private static final String[] SINGLE_CHAR_STRING = new String[256];
     private static final String[][] DOUBLE_CHAR_STRING = new String[256][256];
     static
     {
-        for( int i=0; i<256; i++ )
+        for (int i = 0; i < 256; i++)
         {
             try
             {
-                SINGLE_CHAR_STRING[i] = new String( new byte[] {(byte)i}, "ISO-8859-1" );
+                SINGLE_CHAR_STRING[i] = new String(new byte[] { (byte) i }, "ISO-8859-1");
             }
             catch (UnsupportedEncodingException e)
             {
                 // Nothing should happen here
                 e.printStackTrace();
             }
-            for( int j=0; j<256; j++ )
+            for (int j = 0; j < 256; j++)
             {
                 try
                 {
-                    DOUBLE_CHAR_STRING[i][j] = new String( new byte[] {(byte)i, (byte)j}, "UTF-16BE" );
+                    DOUBLE_CHAR_STRING[i][j] = new String(new byte[] { (byte) i, (byte) j }, "UTF-16BE");
                 }
                 catch (UnsupportedEncodingException e)
                 {
@@ -536,57 +528,59 @@ public abstract class PDFont implements 
         }
     }
 
-    private static String getStringFromArray( byte[] c, int offset, int length ) throws IOException
+    private static String getStringFromArray(byte[] c, int offset, int length) throws IOException
     {
         String retval = null;
-        if( length == 1 )
+        if (length == 1)
         {
-            retval = SINGLE_CHAR_STRING[(c[offset]+256)%256];
+            retval = SINGLE_CHAR_STRING[(c[offset] + 256) % 256];
         }
-        else if( length == 2 )
+        else if (length == 2)
         {
-            retval = DOUBLE_CHAR_STRING[(c[offset]+256)%256][(c[offset+1]+256)%256];
+            retval = DOUBLE_CHAR_STRING[(c[offset] + 256) % 256][(c[offset + 1] + 256) % 256];
         }
         else
         {
-            throw new IOException( "Error:Unknown character length:" + length );
+            throw new IOException("Error:Unknown character length:" + length);
         }
         return retval;
     }
 
-    protected CMap parseCmap( String cmapRoot, InputStream cmapStream)
+    protected CMap parseCmap(String cmapRoot, InputStream cmapStream)
     {
         CMap targetCmap = null;
-        if( cmapStream != null )
+        if (cmapStream != null)
         {
             CMapParser parser = new CMapParser();
             try
             {
-                targetCmap = parser.parse( cmapRoot, cmapStream );
+                targetCmap = parser.parse(cmapRoot, cmapStream);
                 // limit the cache to external CMaps
                 if (cmapRoot != null)
                 {
-                    cmapObjects.put( targetCmap.getName(), targetCmap );
+                    cmapObjects.put(targetCmap.getName(), targetCmap);
                 }
             }
-            catch (IOException exception) {}
+            catch (IOException exception)
+            {
+            }
         }
         return targetCmap;
     }
 
     /**
      * The will set the encoding for this font.
-     *
+     * 
      * @param enc The font encoding.
      */
-    public void setFontEncoding( Encoding enc )
+    public void setFontEncoding(Encoding enc)
     {
         fontEncoding = enc;
     }
 
     /**
      * This will get or create the encoder.
-     *
+     * 
      * @return The encoding to use.
      */
     public Encoding getFontEncoding()
@@ -596,12 +590,12 @@ public abstract class PDFont implements 
 
     /**
      * This will always return "Font" for fonts.
-     *
+     * 
      * @return The type of object that this is.
      */
     public String getType()
     {
-        return font.getNameAsString( COSName.TYPE );
+        return font.getNameAsString(COSName.TYPE);
     }
 
     // Memorized values to avoid repeated dictionary lookups
@@ -613,14 +607,14 @@ public abstract class PDFont implements 
 
     /**
      * This will get the subtype of font, Type1, Type3, ...
-     *
+     * 
      * @return The type of font that this is.
      */
     public String getSubType()
     {
         if (subtype == null)
         {
-            subtype = font.getNameAsString( COSName.SUBTYPE );
+            subtype = font.getNameAsString(COSName.SUBTYPE);
             type1Font = "Type1".equals(subtype);
             trueTypeFont = "TrueType".equals(subtype);
             type0Font = "Type0".equals(subtype);
@@ -631,6 +625,7 @@ public abstract class PDFont implements 
 
     /**
      * Determines if the font is a type 1 font.
+     * 
      * @return returns true if the font is a type 1 font
      */
     public boolean isType1Font()
@@ -641,6 +636,7 @@ public abstract class PDFont implements 
 
     /**
      * Determines if the font is a type 3 font.
+     * 
      * @return returns true if the font is a type 3 font
      */
     public boolean isType3Font()
@@ -648,9 +644,10 @@ public abstract class PDFont implements 
         getSubType();
         return type3Font;
     }
-    
+
     /**
      * Determines if the font is a type 0 font.
+     * 
      * @return returns true if the font is a type 0 font
      */
     public boolean isType0Font()
@@ -661,6 +658,7 @@ public abstract class PDFont implements 
 
     /**
      * Determines if the font is a true type font.
+     * 
      * @return returns true if the font is a true type font
      */
     public boolean isTrueTypeFont()
@@ -670,78 +668,92 @@ public abstract class PDFont implements 
     }
 
     /**
+     * Determines if the font is a symbolic font.
+     * 
+     * @return returns true if the font is a symbolic font
+     */
+    public boolean isSymbolicFont()
+    {
+        if (getFontDescriptor() != null)
+        {
+            return getFontDescriptor().isSymbolic();
+        }
+        return false;
+    }
+
+    /**
      * The PostScript name of the font.
-     *
+     * 
      * @return The postscript name of the font.
      */
     public String getBaseFont()
     {
-        return font.getNameAsString( COSName.BASE_FONT );
+        return font.getNameAsString(COSName.BASE_FONT);
     }
 
     /**
      * Set the PostScript name of the font.
-     *
+     * 
      * @param baseFont The postscript name for the font.
      */
-    public void setBaseFont( String baseFont )
+    public void setBaseFont(String baseFont)
     {
-        font.setName( COSName.BASE_FONT, baseFont );
+        font.setName(COSName.BASE_FONT, baseFont);
     }
 
     /**
      * The code for the first char or -1 if there is none.
-     *
+     * 
      * @return The code for the first character.
      */
     public int getFirstChar()
     {
-        return font.getInt( COSName.FIRST_CHAR, -1 );
+        return font.getInt(COSName.FIRST_CHAR, -1);
     }
 
     /**
      * Set the first character this font supports.
-     *
+     * 
      * @param firstChar The first character.
      */
-    public void setFirstChar( int firstChar )
+    public void setFirstChar(int firstChar)
     {
-        font.setInt( COSName.FIRST_CHAR, firstChar );
+        font.setInt(COSName.FIRST_CHAR, firstChar);
     }
 
     /**
      * The code for the last char or -1 if there is none.
-     *
+     * 
      * @return The code for the last character.
      */
     public int getLastChar()
     {
-        return font.getInt( COSName.LAST_CHAR, -1 );
+        return font.getInt(COSName.LAST_CHAR, -1);
     }
 
     /**
      * Set the last character this font supports.
-     *
+     * 
      * @param lastChar The last character.
      */
-    public void setLastChar( int lastChar )
+    public void setLastChar(int lastChar)
     {
-        font.setInt( COSName.LAST_CHAR, lastChar );
+        font.setInt(COSName.LAST_CHAR, lastChar);
     }
 
     /**
-     * The widths of the characters.  This will be null for the standard 14 fonts.
-     *
+     * The widths of the characters. This will be null for the standard 14 fonts.
+     * 
      * @return The widths of the characters.
      */
     public List<Float> getWidths()
     {
         if (widths == null)
         {
-            COSArray array = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
+            COSArray array = (COSArray) font.getDictionaryObject(COSName.WIDTHS);
             if (array != null)
             {
-                widths = COSArrayList.convertFloatCOSArrayToList( array );
+                widths = COSArrayList.convertFloatCOSArrayToList(array);
             }
         }
         return widths;
@@ -749,39 +761,38 @@ public abstract class PDFont implements 
 
     /**
      * Set the widths of the characters code.
-     *
+     * 
      * @param widthsList The widths of the character codes.
      */
-    public void setWidths( List<Float> widthsList )
+    public void setWidths(List<Float> widthsList)
     {
         widths = widthsList;
-        font.setItem( COSName.WIDTHS, COSArrayList.converterToCOSArray( widths ) );
+        font.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths));
     }
 
     /**
-     * This will get the matrix that is used to transform glyph space to
-     * text space.  By default there are 1000 glyph units to 1 text space
-     * unit, but type3 fonts can use any value.
-     *
-     * Note:If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, otherwise this
-     * is a read-only property.
-     *
+     * This will get the matrix that is used to transform glyph space to text space. By default there are 1000 glyph
+     * units to 1 text space unit, but type3 fonts can use any value.
+     * 
+     * Note:If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, otherwise this is a
+     * read-only property.
+     * 
      * @return The matrix to transform from glyph space to text space.
      */
     public PDMatrix getFontMatrix()
     {
         if (fontMatrix == null)
         {
-            COSArray array = (COSArray)font.getDictionaryObject( COSName.FONT_MATRIX );
-            if( array == null )
+            COSArray array = (COSArray) font.getDictionaryObject(COSName.FONT_MATRIX);
+            if (array == null)
             {
                 array = new COSArray();
-                array.add( new COSFloat( 0.001f ) );
-                array.add( COSInteger.ZERO );
-                array.add( COSInteger.ZERO );
-                array.add( new COSFloat( 0.001f ) );
-                array.add( COSInteger.ZERO );
-                array.add( COSInteger.ZERO );
+                array.add(new COSFloat(0.001f));
+                array.add(COSInteger.ZERO);
+                array.add(COSInteger.ZERO);
+                array.add(new COSFloat(0.001f));
+                array.add(COSInteger.ZERO);
+                array.add(COSInteger.ZERO);
             }
             fontMatrix = new PDMatrix(array);
         }
@@ -790,9 +801,9 @@ public abstract class PDFont implements 
 
     /**
      * This will get the fonts bounding box.
-     *
+     * 
      * @return The fonts bounding box.
-     *
+     * 
      * @throws IOException If there is an error getting the bounding box.
      */
     public abstract PDRectangle getFontBoundingBox() throws IOException;
@@ -800,9 +811,9 @@ public abstract class PDFont implements 
     /**
      * {@inheritDoc}
      */
-    public boolean equals( Object other )
+    public boolean equals(Object other)
     {
-        return other instanceof PDFont && ((PDFont)other).getCOSObject() == this.getCOSObject();
+        return other instanceof PDFont && ((PDFont) other).getCOSObject() == this.getCOSObject();
     }
 
     /**
@@ -815,10 +826,11 @@ public abstract class PDFont implements 
 
     /**
      * Determines the width of the given character.
+     * 
      * @param charCode the code of the given character
      * @return the width of the character
      */
-    public float getFontWidth( int charCode )
+    public float getFontWidth(int charCode)
     {
         float width = -1;
         int firstChar = getFirstChar();
@@ -829,7 +841,7 @@ public abstract class PDFont implements 
             getWidths();
             if (widths != null)
             {
-                width = widths.get(charCode-firstChar).floatValue();
+                width = widths.get(charCode - firstChar).floatValue();
             }
         }
         else
@@ -845,26 +857,38 @@ public abstract class PDFont implements 
 
     /**
      * Determines if a font as a ToUnicode entry.
+     * 
      * @return true if the font has a ToUnicode entry
      */
-    protected boolean hasToUnicode()
+    public boolean hasToUnicode()
     {
         return hasToUnicode;
     }
 
     /**
      * Sets hasToUnicode to the given value.
+     * 
      * @param hasToUnicodeValue the given value for hasToUnicode
      */
     protected void setHasToUnicode(boolean hasToUnicodeValue)
     {
         hasToUnicode = hasToUnicodeValue;
     }
-    
+
     /**
      * Determines the width of the space character.
+     * 
      * @return the width of the space character
      */
     public abstract float getSpaceWidth();
-    
+
+    /**
+     * Returns the toUnicode mapping if present.
+     * 
+     * @return the CMap representing the toUnicode mapping
+     */
+    public CMap getToUnicodeCMap()
+    {
+        return toUnicodeCmap;
+    }
 }