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

svn commit: r1645072 - in /pdfbox/trunk: fontbox/src/main/java/org/apache/fontbox/ttf/ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/

Author: jahewson
Date: Fri Dec 12 21:02:49 2014
New Revision: 1645072

URL: http://svn.apache.org/r1645072
Log:
PDFBOX-2524: Refactor common functionality of PDTrueTypeFont into super class

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java   (with props)
Modified:
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OS2WindowsMetricsTable.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFSubsetter.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontEmbedder.java

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OS2WindowsMetricsTable.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OS2WindowsMetricsTable.java?rev=1645072&r1=1645071&r2=1645072&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OS2WindowsMetricsTable.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OS2WindowsMetricsTable.java Fri Dec 12 21:02:49 2014
@@ -483,19 +483,19 @@ public class OS2WindowsMetricsTable exte
     }
 
     /**
-     * @return Returns the typeLineGap.
+     * @return Returns the typoLineGap.
      */
-    public int getTypeLineGap()
+    public int getTypoLineGap()
     {
-        return typeLineGap;
+        return typoLineGap;
     }
 
     /**
-     * @param typeLineGapValue The typeLineGap to set.
+     * @param typeLineGapValue The typoLineGap to set.
      */
-    public void setTypeLineGap(int typeLineGapValue)
+    public void setTypoLineGap(int typeLineGapValue)
     {
-        this.typeLineGap = typeLineGapValue;
+        this.typoLineGap = typeLineGapValue;
     }
 
     /**
@@ -674,6 +674,46 @@ public class OS2WindowsMetricsTable exte
         this.winDescent = winDescentValue;
     }
 
+    /**
+     * Returns the sxHeight.
+     */
+    public int getHeight()
+    {
+        return sxHeight;
+    }
+
+    /**
+     * Returns the sCapHeight.
+     */
+    public int getCapHeight()
+    {
+        return sCapHeight;
+    }
+
+    /**
+     * Returns the usDefaultChar.
+     */
+    public int getDefaultChar()
+    {
+        return usDefaultChar;
+    }
+
+    /**
+     * Returns the usBreakChar.
+     */
+    public int getBreakChar()
+    {
+        return usBreakChar;
+    }
+
+    /**
+     * Returns the usMaxContext.
+     */
+    public int getMaxContext()
+    {
+        return usMaxContext;
+    }
+
     private int version;
     private short averageCharWidth;
     private int weightClass;
@@ -702,11 +742,16 @@ public class OS2WindowsMetricsTable exte
     private int lastCharIndex;
     private int typoAscender;
     private int typoDescender;
-    private int typeLineGap;
+    private int typoLineGap;
     private int winAscent;
     private int winDescent;
     private long codePageRange1 = -1;
     private long codePageRange2 = -1;
+    private int sxHeight;
+    private int sCapHeight;
+    private int usDefaultChar;
+    private int usBreakChar;
+    private int usMaxContext;
 
     /**
      * A tag that identifies this table type.
@@ -750,7 +795,7 @@ public class OS2WindowsMetricsTable exte
         lastCharIndex = data.readUnsignedShort();
         typoAscender = data.readSignedShort();
         typoDescender = data.readSignedShort();
-        typeLineGap = data.readSignedShort();
+        typoLineGap = data.readSignedShort();
         winAscent = data.readUnsignedShort();
         winDescent = data.readUnsignedShort();
         if (version >= 1)
@@ -758,6 +803,14 @@ public class OS2WindowsMetricsTable exte
             codePageRange1 = data.readUnsignedInt();
             codePageRange2 = data.readUnsignedInt();
         }
+        if (version >= 1.2)
+        {
+            sxHeight = data.readSignedShort();
+            sCapHeight = data.readSignedShort();
+            usDefaultChar = data.readUnsignedShort();
+            usBreakChar = data.readUnsignedShort();
+            usMaxContext = data.readUnsignedShort();
+        }
         initialized = true;
     }
 }

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFSubsetter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFSubsetter.java?rev=1645072&r1=1645071&r2=1645072&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFSubsetter.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFSubsetter.java Fri Dec 12 21:02:49 2014
@@ -536,7 +536,7 @@ public class TTFSubsetter
          */
         writeUint16(dos,os2.getTypoAscender());
         writeUint16(dos,os2.getTypoDescender());
-        writeUint16(dos,os2.getTypeLineGap());
+        writeUint16(dos,os2.getTypoLineGap());
         writeUint16(dos,os2.getWinAscent());
         writeUint16(dos,os2.getWinDescent());
         

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontEmbedder.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontEmbedder.java?rev=1645072&r1=1645071&r2=1645072&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontEmbedder.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFontEmbedder.java Fri Dec 12 21:02:49 2014
@@ -14,39 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.pdfbox.pdmodel.font;
 
-import org.apache.fontbox.ttf.CmapSubtable;
-import org.apache.fontbox.ttf.CmapTable;
-import org.apache.fontbox.ttf.GlyphData;
-import org.apache.fontbox.ttf.GlyphTable;
-import org.apache.fontbox.ttf.HeaderTable;
-import org.apache.fontbox.ttf.HorizontalHeaderTable;
-import org.apache.fontbox.ttf.HorizontalMetricsTable;
-import org.apache.fontbox.ttf.NameRecord;
-import org.apache.fontbox.ttf.NamingTable;
-import org.apache.fontbox.ttf.OS2WindowsMetricsTable;
-import org.apache.fontbox.ttf.PostScriptTable;
-import org.apache.fontbox.ttf.TTFParser;
-import org.apache.fontbox.ttf.TrueTypeFont;
-import org.apache.pdfbox.cos.COSDictionary;
-import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
-import org.apache.pdfbox.pdmodel.font.encoding.GlyphList;
-import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
-import org.apache.pdfbox.io.IOUtils;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.pdmodel.common.COSArrayList;
-import org.apache.pdfbox.pdmodel.common.PDRectangle;
-import org.apache.pdfbox.pdmodel.common.PDStream;
+package org.apache.pdfbox.pdmodel.font;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import org.apache.fontbox.ttf.HorizontalMetricsTable;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.COSArrayList;
+import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
+import org.apache.pdfbox.pdmodel.font.encoding.GlyphList;
+import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
 
 /**
  * Embedded PDTrueTypeFont builder. Helper class to populate a PDTrueTypeFont from a TTF.
@@ -54,209 +38,42 @@ import java.util.Map;
  * @author John Hewson
  * @author Ben Litchfield
  */
-class PDTrueTypeFontEmbedder
+final class PDTrueTypeFontEmbedder extends TrueTypeEmbedder
 {
     private final Encoding fontEncoding;
-    private final TrueTypeFont ttf;
-    private final PDFontDescriptor fontDescriptor;
 
     /**
-     * Creates a new TrueType font for embedding.
+     * Creates a new TrueType font embedder for the given TTF as a PDTrueTypeFont.
+     *
+     * @param document parent document
+     * @param dict font dictionary
+     * @param ttfStream TTF stream
+     * @throws IOException if the TTF could not be read
      */
     PDTrueTypeFontEmbedder(PDDocument document, COSDictionary dict, InputStream ttfStream)
             throws IOException
     {
+        super(document, dict, ttfStream);
         dict.setItem(COSName.SUBTYPE, COSName.TRUE_TYPE);
 
-        PDStream stream = new PDStream(document, ttfStream, false);
-        stream.getStream().setInt(COSName.LENGTH1, stream.getByteArray().length); // todo: wrong?
-        stream.addCompression();
-
-        // only support winansi encoding right now, should really
-        // just use Identity-H with unicode mapping
+        // only support WinAnsiEncoding encoding right now
         Encoding encoding = new WinAnsiEncoding(); // fixme: read encoding from TTF
-
         this.fontEncoding = encoding;
         dict.setItem(COSName.ENCODING, encoding.getCOSObject());
 
-        // as the stream was close within the PDStream constructor, we have to recreate it
-        InputStream stream2 = null;
-        PDFontDescriptor fd;
-        try
-        {
-            stream2 = stream.createInputStream();
-            ttf = new TTFParser().parse(stream2);
-            fd = createFontDescriptor(dict, ttf);
-        }
-        finally
-        {
-            IOUtils.closeQuietly(stream2);
-        }
+        // add the font descriptor
+        dict.setItem(COSName.FONT_DESC, fontDescriptor);
 
-        fd.setFontFile2(stream);
-        dict.setItem(COSName.FONT_DESC, fd);
-        fontDescriptor = fd;
+        // set the glyph widths
+        setWidths(dict);
     }
 
-    // creates a new font descriptor dictionary for the given TTF
-    private PDFontDescriptor createFontDescriptor(COSDictionary dict, TrueTypeFont ttf)
-            throws IOException
+    /**
+     * Sets the glyph widths in the font dictionary.
+     */
+    private void setWidths(COSDictionary font) throws IOException
     {
-        PDFontDescriptor fd = new PDFontDescriptor();
-
-        NamingTable naming = ttf.getNaming();
-        List<NameRecord> records = naming.getNameRecords();
-        for (NameRecord nr : records)
-        {
-            if (nr.getNameId() == NameRecord.NAME_POSTSCRIPT_NAME)
-            {
-                dict.setName(COSName.BASE_FONT, nr.getString());
-                fd.setFontName(nr.getString());
-            }
-            else if (nr.getNameId() == NameRecord.NAME_FONT_FAMILY_NAME)
-            {
-                fd.setFontFamily(nr.getString());
-            }
-        }
-
-        OS2WindowsMetricsTable os2 = ttf.getOS2Windows();
-        boolean isSymbolic = false;
-        switch (os2.getFamilyClass())
-        {
-            case OS2WindowsMetricsTable.FAMILY_CLASS_SYMBOLIC:
-                isSymbolic = true;
-                break;
-            case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS:
-                fd.setScript(true);
-                break;
-            case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS:
-            case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS:
-            case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS:
-            case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS:
-            case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS:
-                fd.setSerif(true);
-                break;
-        }
-
-        switch (os2.getWidthClass())
-        {
-            case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_CONDENSED:
-                fd.setFontStretch("UltraCondensed");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_CONDENSED:
-                fd.setFontStretch("ExtraCondensed");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_CONDENSED:
-                fd.setFontStretch("Condensed");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_CONDENSED:
-                fd.setFontStretch("SemiCondensed");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_MEDIUM:
-                fd.setFontStretch("Normal");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_EXPANDED:
-                fd.setFontStretch("SemiExpanded");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_EXPANDED:
-                fd.setFontStretch("Expanded");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_EXPANDED:
-                fd.setFontStretch("ExtraExpanded");
-                break;
-            case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_EXPANDED:
-                fd.setFontStretch("UltraExpanded");
-                break;
-        }
-        fd.setFontWeight(os2.getWeightClass());
-        fd.setSymbolic(isSymbolic);
-        fd.setNonSymbolic(!isSymbolic);
-
-        // todo retval.setItalic
-        // todo retval.setAllCap
-        // todo retval.setSmallCap
-        // todo retval.setForceBold
-
-        HeaderTable header = ttf.getHeader();
-        PDRectangle rect = new PDRectangle();
-        float scaling = 1000f / header.getUnitsPerEm();
-        rect.setLowerLeftX(header.getXMin() * scaling);
-        rect.setLowerLeftY(header.getYMin() * scaling);
-        rect.setUpperRightX(header.getXMax() * scaling);
-        rect.setUpperRightY(header.getYMax() * scaling);
-        fd.setFontBoundingBox(rect);
-
-        HorizontalHeaderTable hHeader = ttf.getHorizontalHeader();
-        fd.setAscent(hHeader.getAscender() * scaling);
-        fd.setDescent(hHeader.getDescender() * scaling);
-
-        GlyphTable glyphTable = ttf.getGlyph();
-        GlyphData[] glyphs = glyphTable.getGlyphs();
-
-        PostScriptTable ps = ttf.getPostScript();
-        fd.setFixedPitch(ps.getIsFixedPitch() > 0);
-        fd.setItalicAngle(ps.getItalicAngle());
-
-        String[] names = ps.getGlyphNames();
-
-        if (names != null)
-        {
-            for (int i = 0; i < names.length; i++)
-            {
-                // if we have a capital H then use that, otherwise use the tallest letter
-                if (names[i].equals("H"))
-                {
-                    fd.setCapHeight(glyphs[i].getBoundingBox().getUpperRightY() / scaling);
-                }
-                if (names[i].equals("x"))
-                {
-                    fd.setXHeight(glyphs[i].getBoundingBox().getUpperRightY() / scaling);
-                }
-            }
-        }
-
-        // hmm there does not seem to be a clear definition for StemV,
-        // this is close enough and I am told it doesn't usually get used.
-        fd.setStemV(fd.getFontBoundingBox().getWidth() * .13f);
-
-        CmapTable cmapTable = ttf.getCmap();
-        CmapSubtable uniMap = cmapTable.getSubtable(CmapTable.PLATFORM_UNICODE,
-                CmapTable.ENCODING_UNICODE_2_0_FULL);
-        if (uniMap == null)
-        {
-            uniMap = cmapTable.getSubtable(CmapTable.PLATFORM_UNICODE,
-                    CmapTable.ENCODING_UNICODE_2_0_BMP);
-        }
-        if (uniMap == null)
-        {
-            uniMap = cmapTable.getSubtable(CmapTable.PLATFORM_WINDOWS,
-                    CmapTable.ENCODING_WIN_UNICODE);
-        }
-        if (uniMap == null)
-        {
-            // Microsoft's "Recommendations for OpenType Fonts" says that "Symbol" encoding
-            // actually means "Unicode, non-standard character set"
-            uniMap = cmapTable.getSubtable(CmapTable.PLATFORM_WINDOWS,
-                    CmapTable.ENCODING_WIN_SYMBOL);
-        }
-        if (uniMap == null)
-        {
-            // there should always be a usable cmap, if this happens we haven't tried hard enough
-            // to find one. Furthermore, if we loaded the font from disk then we should've checked
-            // first to see that it had a suitable cmap before calling createFontDescriptor
-            throw new IllegalArgumentException("ttf: no suitable cmap for font '" +
-                    ttf.getNaming().getFontFamily() + "', found: " +
-                    Arrays.toString(cmapTable.getCmaps()));
-        }
-
-        if (this.getFontEncoding() == null)
-        {
-            // todo: calling this.getFontEncoding() doesn't work if the font is loaded
-            //       from the local system, because it relies on the FontDescriptor!
-            //       We make do for now by returning an incomplete descriptor pending further
-            //       refactoring of PDFont#determineEncoding().
-            return fd;
-        }
+        float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
 
         Map<Integer, String> codeToName = this.getFontEncoding().getCodeToNameMap();
 
@@ -265,33 +82,35 @@ class PDTrueTypeFontEmbedder
 
         HorizontalMetricsTable hMet = ttf.getHorizontalMetrics();
         int[] widthValues = hMet.getAdvanceWidth();
+
         // some monospaced fonts provide only one value for the width
-        // instead of an array containing the same value for every glyphid
-        boolean isMonospaced = fd.isFixedPitch() || widthValues.length == 1;
-        int nWidths = lastChar - firstChar + 1;
-        List<Integer> widths = new ArrayList<Integer>(nWidths);
+        // instead of an array containing the same value for every glyph id
+        boolean isMonospaced = ttf.getHorizontalHeader().getNumberOfHMetrics() == 1;
+
+        int numWidths = lastChar - firstChar + 1;
+        List<Integer> widths = new ArrayList<Integer>(numWidths);
+
         // use the first width as default
         // proportional fonts -> width of the .notdef character
         // monospaced-fonts -> the first width
         int defaultWidth = Math.round(widthValues[0] * scaling);
-        for (int i = 0; i < nWidths; i++)
+        for (int i = 0; i < numWidths; i++)
         {
             widths.add(defaultWidth);
         }
 
-        // A character code is mapped to a glyph name via the provided font encoding
-        // Afterwards, the glyph name is translated to a glyph ID.
-        // For details, see PDFReference16.pdf, Section 5.5.5, p.401
-        //
+        // a character code is mapped to a glyph name via the provided font encoding
+        // afterwards, the glyph name is translated to a glyph ID.
         for (Map.Entry<Integer, String> e : codeToName.entrySet())
         {
             String name = e.getValue();
             // pdf code to unicode by glyph list.
             if (!name.equals(".notdef"))
             {
-                String c = GlyphList.getAdobeGlyphList().toUnicode(name); // todo: we're supposed to use the 'provided font encoding'
+                // todo: we're supposed to use the 'provided font encoding'
+                String c = GlyphList.getAdobeGlyphList().toUnicode(name);
                 int charCode = c.codePointAt(0);
-                int gid = uniMap.getGlyphId(charCode);
+                int gid = cmap.getGlyphId(charCode);
                 if (gid != 0)
                 {
                     if (isMonospaced)
@@ -306,11 +125,10 @@ class PDTrueTypeFontEmbedder
                 }
             }
         }
-        dict.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths));
-        dict.setInt(COSName.FIRST_CHAR, firstChar);
-        dict.setInt(COSName.LAST_CHAR, lastChar);
 
-        return fd;
+        font.setInt(COSName.FIRST_CHAR, firstChar);
+        font.setInt(COSName.LAST_CHAR, lastChar);
+        font.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths));
     }
 
     /**
@@ -320,20 +138,4 @@ class PDTrueTypeFontEmbedder
     {
         return fontEncoding;
     }
-
-    /**
-     * Returns the FontBox font.
-     */
-    public TrueTypeFont getTrueTypeFont()
-    {
-        return ttf;
-    }
-
-    /**
-     * Returns the font descriptor.
-     */
-    public PDFontDescriptor getFontDescriptor()
-    {
-        return fontDescriptor;
-    }
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java?rev=1645072&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java Fri Dec 12 21:02:49 2014
@@ -0,0 +1,210 @@
+/*
+ * 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.pdmodel.font;
+
+import org.apache.fontbox.ttf.CmapSubtable;
+import org.apache.fontbox.ttf.CmapTable;
+import org.apache.fontbox.ttf.HeaderTable;
+import org.apache.fontbox.ttf.HorizontalHeaderTable;
+import org.apache.fontbox.ttf.OS2WindowsMetricsTable;
+import org.apache.fontbox.ttf.PostScriptTable;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.IOUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Common functionality for embedding TrueType fonts.
+ *
+ * @author Ben Litchfield
+ * @author John Hewson
+ */
+abstract class TrueTypeEmbedder
+{
+    private static final int ITALIC = 1;
+    private static final int OBLIQUE = 256;
+
+    protected final TrueTypeFont ttf;
+    protected final PDFontDescriptor fontDescriptor;
+    protected final CmapSubtable cmap;
+
+    /**
+     * Creates a new TrueType font for embedding.
+     */
+    TrueTypeEmbedder(PDDocument document, COSDictionary dict, InputStream ttfStream)
+                           throws IOException
+    {
+        PDStream stream = new PDStream(document, ttfStream, false);
+        stream.getStream().setInt(COSName.LENGTH1, stream.getByteArray().length);
+        stream.addCompression();
+
+        // as the stream was close within the PDStream constructor, we have to recreate it
+        InputStream stream2 = null;
+        PDFontDescriptor fd;
+        try
+        {
+            stream2 = stream.createInputStream();
+            ttf = new TTFParser().parse(stream2);
+            fd = createFontDescriptor(ttf);
+        }
+        finally
+        {
+            IOUtils.closeQuietly(stream2);
+        }
+
+        fd.setFontFile2(stream);
+        dict.setName(COSName.BASE_FONT, ttf.getName());
+
+        fontDescriptor = fd;
+
+        // choose a Unicode "cmap"
+        cmap = getUnicodeCmap(ttf.getCmap());
+    }
+
+    /**
+     * Creates a new font descriptor dictionary for the given TTF.
+     */
+    private PDFontDescriptor createFontDescriptor(TrueTypeFont ttf) throws IOException
+    {
+        PDFontDescriptor fd = new PDFontDescriptor();
+        fd.setFontName(ttf.getName());
+
+        OS2WindowsMetricsTable os2 = ttf.getOS2Windows();
+        PostScriptTable post = ttf.getPostScript();
+
+        // Flags
+        fd.setFixedPitch(post.getIsFixedPitch() > 0 ||
+                         ttf.getHorizontalHeader().getNumberOfHMetrics() == 1);
+
+        int fsSelection = os2.getFsSelection();
+        fd.setItalic((fsSelection & ITALIC) == fsSelection ||
+                     (fsSelection & OBLIQUE) == fsSelection);
+
+        switch (os2.getFamilyClass())
+        {
+            case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS:
+            case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS:
+            case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS:
+            case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS:
+            case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS:
+                fd.setSerif(true);
+                break;
+            case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS:
+                fd.setScript(true);
+                break;
+        }
+
+        fd.setFontWeight(os2.getWeightClass());
+
+        fd.setSymbolic(true);
+        fd.setNonSymbolic(false);
+
+        // ItalicAngle
+        fd.setItalicAngle(post.getItalicAngle());
+
+        // FontBBox
+        HeaderTable header = ttf.getHeader();
+        PDRectangle rect = new PDRectangle();
+        float scaling = 1000f / header.getUnitsPerEm();
+        rect.setLowerLeftX(header.getXMin() * scaling);
+        rect.setLowerLeftY(header.getYMin() * scaling);
+        rect.setUpperRightX(header.getXMax() * scaling);
+        rect.setUpperRightY(header.getYMax() * scaling);
+        fd.setFontBoundingBox(rect);
+
+        // Ascent, Descent
+        HorizontalHeaderTable hHeader = ttf.getHorizontalHeader();
+        fd.setAscent(hHeader.getAscender() * scaling);
+        fd.setDescent(hHeader.getDescender() * scaling);
+
+        // CapHeight, XHeight
+        if (os2.getVersion() >= 1.2)
+        {
+            fd.setCapHeight(os2.getCapHeight() / scaling);
+            fd.setXHeight(os2.getHeight() / scaling);
+        }
+        else
+        {
+            // estimate by summing the typographical +ve ascender and -ve descender
+            fd.setCapHeight((os2.getTypoAscender() + os2.getTypoDescender()) / scaling);
+
+            // estimate by halfing the typographical ascender
+            fd.setXHeight((os2.getTypoAscender() / 2) / scaling);
+        }
+
+        // StemV - there's no true TTF equivalent of this, so we estimate it
+        fd.setStemV(fd.getFontBoundingBox().getWidth() * .13f);
+
+        return fd;
+    }
+
+    /**
+     * Returns the best Unicode from the font (the most general).
+     */
+    private CmapSubtable getUnicodeCmap(CmapTable cmapTable) throws IOException
+    {
+        CmapSubtable cmap = cmapTable.getSubtable(CmapTable.PLATFORM_UNICODE,
+                                                  CmapTable.ENCODING_UNICODE_2_0_FULL);
+        if (cmap == null)
+        {
+            cmap = cmapTable.getSubtable(CmapTable.PLATFORM_UNICODE,
+                                         CmapTable.ENCODING_UNICODE_2_0_BMP);
+        }
+        if (cmap == null)
+        {
+            cmap = cmapTable.getSubtable(CmapTable.PLATFORM_WINDOWS,
+                                         CmapTable.ENCODING_WIN_UNICODE);
+        }
+        if (cmap == null)
+        {
+            // Microsoft's "Recommendations for OpenType Fonts" says that "Symbol" encoding
+            // actually means "Unicode, non-standard character set"
+            cmap = cmapTable.getSubtable(CmapTable.PLATFORM_WINDOWS,
+                                         CmapTable.ENCODING_WIN_SYMBOL);
+        }
+        if (cmap == null)
+        {
+            throw new IOException("The TrueType font does not contain a Unicode cmap");
+        }
+        return cmap;
+    }
+
+
+    /**
+     * Returns the FontBox font.
+     */
+    public TrueTypeFont getTrueTypeFont()
+    {
+        return ttf;
+    }
+
+    /**
+     * Returns the font descriptor.
+     */
+    public PDFontDescriptor getFontDescriptor()
+    {
+        return fontDescriptor;
+    }
+}

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