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/16 00:54:56 UTC

svn commit: r1645796 [2/2] - in /pdfbox/trunk: examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ fontbox/src/main/java/org/apache/fontbox/ttf/ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/...

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java?rev=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java Mon Dec 15 23:54:55 2014
@@ -22,6 +22,9 @@ import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.IOException;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSInteger;
@@ -38,8 +41,11 @@ import org.apache.pdfbox.pdmodel.common.
  */
 final class PDCIDFontType2Embedder extends TrueTypeEmbedder
 {
+    private final PDDocument document;
     private final PDType0Font parent;
+    private final COSDictionary dict;
     private final COSDictionary cidFont;
+    private final Map<Integer, Integer> gidToUni;
 
     /**
      * Creates a new TrueType font embedder for the given TTF as a PDCIDFontType2.
@@ -54,6 +60,8 @@ final class PDCIDFontType2Embedder exten
                            PDType0Font parent) throws IOException
     {
         super(document, dict, ttfStream);
+        this.document = document;
+        this.dict = dict;
         this.parent = parent;
 
         // parent Type 0 font
@@ -67,25 +75,78 @@ final class PDCIDFontType2Embedder exten
         descendantFonts.add(cidFont);
         dict.setItem(COSName.DESCENDANT_FONTS, descendantFonts);
 
+        // build GID -> Unicode map
+        gidToUni = new HashMap<Integer, Integer>();
+        for (int gid = 1, max = ttf.getMaximumProfile().getNumGlyphs(); gid <= max; gid++)
+        {
+            // skip composite glyph components that have no code point
+            Integer codePoint = cmap.getCharacterCode(gid);
+            if (codePoint != null)
+            {
+                gidToUni.put(gid, codePoint); // CID = GID
+            }
+        }
+
         // ToUnicode CMap
-        dict.setItem(COSName.TO_UNICODE, createToUnicodeCMap(document));
+        buildToUnicodeCMap(null);
     }
 
-    private PDStream createToUnicodeCMap(PDDocument document) throws IOException
+    /**
+     * Rebuild a font subset.
+     */
+    protected void buildSubset(InputStream ttfSubset, String tag, Map<Integer, Integer> gidToCid)
+            throws IOException
+    {
+        // build CID2GIDMap, because the content stream has been written with the old GIDs
+        Map<Integer, Integer> cidToGid = new HashMap<Integer, Integer>();
+        for (Map.Entry<Integer, Integer> entry : gidToCid.entrySet())
+        {
+            int newGID = entry.getKey();
+            int oldGID = entry.getValue();
+            cidToGid.put(oldGID, newGID);
+        }
+
+        // buildSubset the relevant part of the font
+        buildFontFile2(ttfSubset);
+        addNameTag(tag);
+        buildWidths(cidToGid);
+        buildCIDToGIDMap(cidToGid);
+        buildToUnicodeCMap(gidToCid);
+    }
+
+    private void buildToUnicodeCMap(Map<Integer, Integer> newGIDToOldCID) throws IOException
     {
         ToUnicodeWriter toUniWriter = new ToUnicodeWriter();
         boolean hasSurrogates = false;
         for (int gid = 1, max = ttf.getMaximumProfile().getNumGlyphs(); gid <= max; gid++)
         {
-            Integer codePoint = cmap.getCharacterCode(gid);
+            // optional CID2GIDMap for subsetting
+            int cid;
+            if (newGIDToOldCID != null)
+            {
+                if (!newGIDToOldCID.containsKey(gid))
+                {
+                    continue;
+                }
+                else
+                {
+                    cid = newGIDToOldCID.get(gid);
+                }
+            }
+            else
+            {
+                cid = gid;
+            }
+
             // skip composite glyph components that have no code point
+            Integer codePoint = gidToUni.get(cid); // old GID -> Unicode
             if (codePoint != null)
             {
                 if (codePoint > 0xFFFF)
                 {
                     hasSurrogates = true;
                 }
-                toUniWriter.add(gid, new String(new int[]{ codePoint }, 0, 1));
+                toUniWriter.add(cid, new String(new int[]{ codePoint }, 0, 1));
             }
         }
 
@@ -106,7 +167,7 @@ final class PDCIDFontType2Embedder exten
             }
         }
 
-        return stream;
+        dict.setItem(COSName.TO_UNICODE, stream);
     }
 
     private COSDictionary toCIDSystemInfo(String registry, String ordering, int supplement)
@@ -137,21 +198,101 @@ final class PDCIDFontType2Embedder exten
         cidFont.setItem(COSName.FONT_DESC, fontDescriptor.getCOSObject());
 
         // W - widths
-        int numGlyphs = ttf.getNumberOfGlyphs();
-        int[] gidwidths = new int[numGlyphs * 2];
-        for (int i = 0; i < numGlyphs; i++)
-        {
-            gidwidths[i * 2] = i;
-            gidwidths[i * 2 + 1] = ttf.getHorizontalMetrics().getAdvanceWidth(i);
-        }
-        cidFont.setItem(COSName.W, getWidths(gidwidths));
+        buildWidths(cidFont);
 
-        // CIDToGIDMap - todo: optional (can be used for easy sub-setting)
+        // CIDToGIDMap
         cidFont.setItem(COSName.CID_TO_GID_MAP, COSName.IDENTITY);
 
         return cidFont;
     }
 
+    private void addNameTag(String tag) throws IOException
+    {
+        String name = fontDescriptor.getFontName();
+        String newName = tag + name;
+
+        dict.setName(COSName.BASE_FONT, newName);
+        fontDescriptor.setFontName(newName);
+        cidFont.setName(COSName.BASE_FONT, newName);
+    }
+
+    private void buildCIDToGIDMap(Map<Integer, Integer> cidToGid) throws IOException
+    {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int cidMax = Collections.max(cidToGid.keySet());
+        for (int i = 0; i <= cidMax; i++)
+        {
+            int gid;
+            if (cidToGid.containsKey(i))
+            {
+                gid = cidToGid.get(i);
+            }
+            else
+            {
+                gid = 0;
+            }
+            out.write(new byte[] { (byte)(gid >> 8 & 0xff), (byte)(gid & 0xff) });
+        }
+
+        InputStream input = new ByteArrayInputStream(out.toByteArray());
+        PDStream stream = new PDStream(document, input, false);
+        stream.getStream().setInt(COSName.LENGTH1, stream.getByteArray().length);
+        stream.addCompression();
+
+        cidFont.setItem(COSName.CID_TO_GID_MAP, stream);
+    }
+
+    /**
+     * Builds withs with a custom CIDToGIDMap (for embedding font subset).
+     */
+    private void buildWidths(Map<Integer, Integer> cidToGid) throws IOException
+    {
+        float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
+
+        COSArray widths = new COSArray();
+        COSArray ws = new COSArray();
+        int prev = -1;
+
+        for (int cid : cidToGid.keySet())
+        {
+            if (!cidToGid.containsKey(cid))
+            {
+                continue;
+            }
+
+            int gid = cidToGid.get(cid);
+            float width = ttf.getHorizontalMetrics().getAdvanceWidth(gid) * scaling;
+
+            // c [w1 w2 ... wn]
+            if (prev != cid - 1)
+            {
+                ws = new COSArray();
+                widths.add(COSInteger.get(cid)); // c
+                widths.add(ws);
+            }
+            ws.add(COSInteger.get(Math.round(width))); // wi
+            prev = cid;
+        }
+
+        cidFont.setItem(COSName.W, widths);
+    }
+
+    /**
+     * Build widths with Identity CIDToGIDMap (for embedding full font).
+     */
+    private void buildWidths(COSDictionary cidFont) throws IOException
+    {
+        int cidMax = ttf.getNumberOfGlyphs();
+        int[] gidwidths = new int[cidMax * 2];
+        for (int cid = 0; cid < cidMax; cid++)
+        {
+            gidwidths[cid * 2] = cid;
+            gidwidths[cid * 2 + 1] = ttf.getHorizontalMetrics().getAdvanceWidth(cid);
+        }
+
+        cidFont.setItem(COSName.W, getWidths(gidwidths));
+    }
+
     private COSArray getWidths(int[] widths) throws IOException
     {
         if (widths.length == 0)

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=1645796&r1=1645795&r2=1645796&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 Mon Dec 15 23:54:55 2014
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
 
+import java.util.Set;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.fontbox.afm.FontMetrics;
@@ -565,6 +566,14 @@ public abstract class PDFont implements
         return Standard14Fonts.containsName(getName());
     }
 
+    /**
+     * Replaces this font with a subset containing only the given Unicode characters.
+     *
+     * @param codePoints Unicode code points to keep
+     * @throws IOException if the subset could not be written
+     */
+    public abstract void subset(Set<Integer> codePoints) throws IOException;
+
     @Override
     public abstract boolean isDamaged();
 

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java?rev=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java Mon Dec 15 23:54:55 2014
@@ -349,4 +349,11 @@ public abstract class PDSimpleFont exten
         }
         return super.isStandard14();
     }
+
+    @Override
+    public void subset(Set<Integer> codePoints) throws IOException
+    {
+        // only TTF subsetting via PDType0Font is currently supported
+        throw new UnsupportedOperationException();
+    }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java?rev=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java Mon Dec 15 23:54:55 2014
@@ -72,7 +72,10 @@ public class PDTrueTypeFont extends PDSi
      * @param file a ttf file.
      * @return a PDTrueTypeFont instance.
      * @throws IOException If there is an error loading the data.
+     *
+     * @deprecated Use {@link PDType0Font#load(PDDocument, File)} instead.
      */
+    @Deprecated
     public static PDTrueTypeFont loadTTF(PDDocument doc, File file) throws IOException
     {
         return new PDTrueTypeFont(doc, new FileInputStream(file));
@@ -85,7 +88,10 @@ public class PDTrueTypeFont extends PDSi
      * @param input a ttf file stream
      * @return a PDTrueTypeFont instance.
      * @throws IOException If there is an error loading the data.
+     *
+     * @deprecated Use {@link PDType0Font#load(PDDocument, InputStream)} instead.
      */
+    @Deprecated
     public static PDTrueTypeFont loadTTF(PDDocument doc, InputStream input) throws IOException
     {
         return new PDTrueTypeFont(doc, input);
@@ -382,7 +388,7 @@ public class PDTrueTypeFont extends PDSi
             {
                 if (CmapTable.PLATFORM_WINDOWS == cmap.getPlatformId())
                 {
-                    if (CmapTable.ENCODING_WIN_UNICODE == cmap.getPlatformEncodingId())
+                    if (CmapTable.ENCODING_WIN_UNICODE_BMP == cmap.getPlatformEncodingId())
                     {
                         cmapWinUnicode = cmap;
                     }

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=1645796&r1=1645795&r2=1645796&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 Mon Dec 15 23:54:55 2014
@@ -117,4 +117,12 @@ final class PDTrueTypeFontEmbedder exten
     {
         return fontEncoding;
     }
+
+    @Override
+    protected void buildSubset(InputStream ttfSubset, String tag,
+                            Map<Integer, Integer> gidToCid) throws IOException
+    {
+        // use PDType0Font instead
+        throw new UnsupportedOperationException();
+    }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java?rev=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java Mon Dec 15 23:54:55 2014
@@ -21,6 +21,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import java.util.Set;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.fontbox.cmap.CMap;
@@ -47,6 +48,7 @@ public class PDType0Font extends PDFont
     private final PDCIDFont descendantFont;
     private CMap cMap, cMapUCS2;
     private boolean isCMapPredefined;
+    private PDCIDFontType2Embedder embedder;
 
     /**
     * Loads a TTF to be embedded into a document.
@@ -106,13 +108,18 @@ public class PDType0Font extends PDFont
     */
     private PDType0Font(PDDocument document, InputStream ttfStream) throws IOException
     {
-        PDCIDFontType2Embedder embedder =
-                new PDCIDFontType2Embedder(document, dict, ttfStream, this);
+        embedder = new PDCIDFontType2Embedder(document, dict, ttfStream, this);
         descendantFont = embedder.getCIDFont();
         readEncoding();
         fetchCMapUCS2();
     }
 
+    @Override
+    public void subset(Set<Integer> codePoints) throws IOException
+    {
+        embedder.subset(codePoints);
+    }
+
     /**
      * Reads the font's Encoding entry, which should be a CMap name/stream.
      */

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/Subsetter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/Subsetter.java?rev=1645796&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/Subsetter.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/Subsetter.java Mon Dec 15 23:54:55 2014
@@ -0,0 +1,29 @@
+/*
+ * 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 java.io.IOException;
+import java.util.Set;
+
+/**
+ * Interface for a font subsetter.
+ */
+interface Subsetter
+{
+    public void subset(Set<Integer> codePoints) throws IOException;
+}

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

Modified: 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=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/TrueTypeEmbedder.java Mon Dec 15 23:54:55 2014
@@ -17,6 +17,12 @@
 
 package org.apache.pdfbox.pdmodel.font;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.fontbox.ttf.CmapSubtable;
 import org.apache.fontbox.ttf.CmapTable;
 import org.apache.fontbox.ttf.HeaderTable;
@@ -24,6 +30,7 @@ import org.apache.fontbox.ttf.Horizontal
 import org.apache.fontbox.ttf.OS2WindowsMetricsTable;
 import org.apache.fontbox.ttf.PostScriptTable;
 import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TTFSubsetter;
 import org.apache.fontbox.ttf.TrueTypeFont;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
@@ -41,13 +48,15 @@ import java.io.InputStream;
  * @author Ben Litchfield
  * @author John Hewson
  */
-abstract class TrueTypeEmbedder
+abstract class TrueTypeEmbedder implements Subsetter
 {
     private static final int ITALIC = 1;
     private static final int OBLIQUE = 256;
+    private static final String BASE25 = "BCDEFGHIJKLMNOPQRSTUVWXYZ";
 
-    protected final TrueTypeFont ttf;
-    protected final PDFontDescriptor fontDescriptor;
+    private final PDDocument document;
+    protected TrueTypeFont ttf;
+    protected PDFontDescriptor fontDescriptor;
     protected final CmapSubtable cmap;
 
     /**
@@ -56,35 +65,42 @@ abstract class TrueTypeEmbedder
     TrueTypeEmbedder(PDDocument document, COSDictionary dict, InputStream ttfStream)
                            throws IOException
     {
+        this.document = document;
+
+        buildFontFile2(ttfStream);
+        dict.setName(COSName.BASE_FONT, ttf.getName());
+
+        // choose a Unicode "cmap"
+        cmap = getUnicodeCmap(ttf.getCmap());
+    }
+
+    public void buildFontFile2(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;
+        // as the stream was closed within the PDStream constructor, we have to recreate it
+        InputStream input = null;
         try
         {
-            stream2 = stream.createInputStream();
-            ttf = new TTFParser().parse(stream2);
+            input = stream.createInputStream();
+            ttf = new TTFParser().parseEmbedded(input);
             if (!isEmbeddingPermitted(ttf))
             {
                 throw new IOException("This font does not permit embedding");
             }
-            fd = createFontDescriptor(ttf);
+            if (fontDescriptor == null)
+            {
+                fontDescriptor = createFontDescriptor(ttf);
+            }
         }
         finally
         {
-            IOUtils.closeQuietly(stream2);
+            IOUtils.closeQuietly(input);
         }
 
-        fd.setFontFile2(stream);
-        dict.setName(COSName.BASE_FONT, ttf.getName());
-
-        fontDescriptor = fd;
-
-        // choose a Unicode "cmap"
-        cmap = getUnicodeCmap(ttf.getCmap());
+        fontDescriptor.setFontFile2(stream);
     }
 
     /**
@@ -114,6 +130,23 @@ abstract class TrueTypeEmbedder
     }
 
     /**
+     * Returns true if the fsType in the OS/2 table permits subsetting.
+     */
+    private boolean isSubsettingPermitted(TrueTypeFont ttf) throws IOException
+    {
+        if (ttf.getOS2Windows() != null)
+        {
+            int fsType = ttf.getOS2Windows().getFsType();
+            if ((fsType & OS2WindowsMetricsTable.FSTYPE_NO_SUBSETTING) ==
+                          OS2WindowsMetricsTable.FSTYPE_NO_SUBSETTING)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Creates a new font descriptor dictionary for the given TTF.
      */
     private PDFontDescriptor createFontDescriptor(TrueTypeFont ttf) throws IOException
@@ -180,7 +213,7 @@ abstract class TrueTypeEmbedder
             // estimate by summing the typographical +ve ascender and -ve descender
             fd.setCapHeight((os2.getTypoAscender() + os2.getTypoDescender()) / scaling);
 
-            // estimate by halfing the typographical ascender
+            // estimate by halving the typographical ascender
             fd.setXHeight((os2.getTypoAscender() / 2) / scaling);
         }
 
@@ -205,7 +238,7 @@ abstract class TrueTypeEmbedder
         if (cmap == null)
         {
             cmap = cmapTable.getSubtable(CmapTable.PLATFORM_WINDOWS,
-                                         CmapTable.ENCODING_WIN_UNICODE);
+                                         CmapTable.ENCODING_WIN_UNICODE_BMP);
         }
         if (cmap == null)
         {
@@ -221,7 +254,6 @@ abstract class TrueTypeEmbedder
         return cmap;
     }
 
-
     /**
      * Returns the FontBox font.
      */
@@ -237,4 +269,77 @@ abstract class TrueTypeEmbedder
     {
         return fontDescriptor;
     }
+
+    @Override
+    public void subset(Set<Integer> codePoints) throws IOException
+    {
+        if (!isSubsettingPermitted(ttf))
+        {
+            throw new IOException("This font does not permit subsetting");
+        }
+
+        // PDF spec required tables (if present), all others will be removed
+        List<String> tables = new ArrayList<String>();
+        tables.add("head");
+        tables.add("hhea");
+        tables.add("loca");
+        tables.add("maxp");
+        tables.add("cvt");
+        tables.add("prep");
+        tables.add("glyf");
+        tables.add("hmtx");
+        tables.add("fpgm");
+        // Windows ClearType
+        tables.add("gasp");
+
+        // set the GIDs to subset
+        TTFSubsetter subsetter = new TTFSubsetter(getTrueTypeFont(), tables);
+        subsetter.addAll(codePoints);
+
+        // calculate deterministic tag based on the chosen subset
+        Map<Integer, Integer> gidToCid = subsetter.getGIDMap();
+        String tag = getTag(gidToCid);
+        subsetter.setPrefix(tag);
+
+        // save the subset font
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        subsetter.writeToStream(out);
+
+        // re-build the embedded font
+        buildSubset(new ByteArrayInputStream(out.toByteArray()), tag, gidToCid);
+    }
+
+    /**
+     * Rebuild a font subset.
+     */
+    protected abstract void buildSubset(InputStream ttfSubset, String tag,
+                                     Map<Integer, Integer> gidToCid) throws IOException;
+
+    /**
+     * Returns an uppercase 6-character unique tag for the given subset.
+     */
+    public String getTag(Map<Integer, Integer> gidToCid)
+    {
+        // deterministic
+        long num = gidToCid.hashCode();
+
+        // base25 encode
+        StringBuilder sb = new StringBuilder();
+        do
+        {
+            long div = num / 25;
+            int mod = (int)(num % 25);
+            sb.append(BASE25.charAt(mod));
+            num = div;
+        } while (num != 0 && sb.length() < 6);
+
+        // pad
+        while (sb.length() < 6)
+        {
+            sb.insert(0, 'A');
+        }
+
+        sb.append('+');
+        return sb.toString();
+    }
 }

Modified: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/TextToPDF.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/TextToPDF.java?rev=1645796&r1=1645795&r2=1645796&view=diff
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/TextToPDF.java (original)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/TextToPDF.java Mon Dec 15 23:54:55 2014
@@ -29,10 +29,9 @@ import org.apache.pdfbox.pdmodel.PDPage;
 
 import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
 import org.apache.pdfbox.pdmodel.font.PDFont;
-import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
 import org.apache.pdfbox.pdmodel.font.PDType1Font;
 
-
 /**
  * This will take a text file and ouput a pdf with that text.
  *
@@ -225,7 +224,7 @@ public class TextToPDF
                     else if( args[i].equals( "-ttf" ))
                     {
                         i++;
-                        PDTrueTypeFont font = PDTrueTypeFont.loadTTF( doc, new File( args[i]));
+                        PDFont font = PDType0Font.load( doc, new File( args[i]) );
                         app.setFont( font );
                     }
                     else if( args[i].equals( "-fontSize" ))