]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
integrate (very) preliminary cff support Temp_CFF Temp_CFF
authorGlenn Adams <gadams@apache.org>
Tue, 28 Feb 2012 09:38:21 +0000 (09:38 +0000)
committerGlenn Adams <gadams@apache.org>
Tue, 28 Feb 2012 09:38:21 +0000 (09:38 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_CFF@1294537 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/fonts/truetype/CFFUtil.java [new file with mode: 0644]
src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java

diff --git a/src/java/org/apache/fop/fonts/truetype/CFFUtil.java b/src/java/org/apache/fop/fonts/truetype/CFFUtil.java
new file mode 100644 (file)
index 0000000..6b2d704
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * A utility class for working with Adobe Compact Font Format (CFF) data. See
+ * Adobe Technical Note #5176 "The Compact Font Format Specification" for more
+ * information.
+ */
+final class CFFUtil {
+
+    private CFFUtil() {
+    }
+
+    /**
+     * Extract glyph subset of CFF table ENTRY from input font IN according to
+     * the specified GLYPHS.
+     * @param in input font file reader
+     * @param entry directory entry describing CFF table in input file
+     * @param glyphs map of original glyph indices to subset indices
+     * @returns an array of bytes representing a well formed CFF table containing
+     * the specified glyph subset
+     * @throws IOException in case of an I/O exception when reading from input font
+     */
+    public static byte[] extractGlyphSubset
+        ( FontFileReader in, TTFDirTabEntry entry, Map<Integer, Integer> glyphs )
+        throws IOException {
+
+        // 1. read CFF data from IN, where ENTRY points at start of CFF table
+        // 2. while reading CFF data, accumulate necessary information to output subset
+        //    of glyphs, where GLYPHS.keySet() enumerates the desired glyphs, and
+        //    GLYPHS.values() (for the keys) enumerates the new (output) glyph indices
+        //    for the desired glyph subset
+        // 3. return a BLOB containing a well-formed CFF font according to the Adobe
+        //    spec and constrained as needed by http://www.microsoft.com/typography/otspec/cff.htm
+
+        long cffOffset = entry.getOffset();
+
+        // HEADER
+
+        in.seekSet ( cffOffset );
+        int major = in.readTTFUByte();
+        int minor = in.readTTFUByte();
+        int hdrSize = in.readTTFUByte();
+        int hdrOffSize = in.readTTFUByte();
+
+        // Name INDEX
+
+        in.seekSet ( cffOffset + hdrSize );
+        int nameIndexCount = in.readTTFUShort();
+        if ( nameIndexCount > 0 ) {
+            int nameIndexOffsetSize = in.readTTFUByte();
+            long nameIndexOffsets[] = new long [ nameIndexCount + 1 ];
+            if ( nameIndexOffsetSize == 1 ) {
+                for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
+                    nameIndexOffsets [ i ] = in.readTTFUByte();
+                }
+            } else if ( nameIndexOffsetSize == 2 ) {
+                for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
+                    nameIndexOffsets [ i ] = in.readTTFUShort();
+                }
+            } else if ( nameIndexOffsetSize == 4 ) {
+                for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
+                    nameIndexOffsets [ i ] = in.readTTFULong();
+                }
+            } else {
+                throw new RuntimeException ( "invalid offset size, got " + nameIndexOffsetSize + ", expected 1, 2, or 4" );
+            }
+            int nameIndexDataOffset = in.getCurrentPos() - 1;
+            String[] names = new String [ nameIndexCount ];
+            for ( int i = 0, n = names.length, nOffsets = nameIndexOffsets.length; i < n; i++ ) {
+                assert ( i + 1 ) < nOffsets;
+                long offCurrent = nameIndexOffsets [ i ];
+                long offNext = nameIndexOffsets [ i + 1 ];
+                long numBytes = offNext - offCurrent;
+                String name;
+                if ( numBytes > 0 ) {
+                    if ( numBytes < Integer.MAX_VALUE ) {
+                        long nameOffset = nameIndexDataOffset + offCurrent;
+                        if ( nameOffset < Integer.MAX_VALUE ) {
+                            byte[] nameBytes = in.getBytes ( (int) nameOffset, (int) numBytes );
+                            name = new String ( nameBytes, 0, (int) numBytes, "US-ASCII" );
+                        } else {
+                            throw new UnsupportedOperationException ( "unsupported index offset value, got " + nameOffset + ", expected less than " + Integer.MAX_VALUE );
+                        }
+                    } else {
+                        throw new UnsupportedOperationException ( "unsupported indexed data length, got " + numBytes + ", expected less than " + Integer.MAX_VALUE );
+                    }
+                } else {
+                    name = "";
+                }
+                names [ i ] = name;
+            }
+            in.seekSet ( nameIndexDataOffset + nameIndexOffsets [ nameIndexCount ] );
+        }
+
+        // Top Dict INDEX
+
+        int topDictIndexCount = in.readTTFUShort();
+        if ( topDictIndexCount > 0 ) {
+            int topDictIndexOffsetSize = in.readTTFUByte();
+            long topDictIndexOffsets[] = new long [ topDictIndexCount + 1 ];
+            if ( topDictIndexOffsetSize == 1 ) {
+                for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
+                    topDictIndexOffsets [ i ] = in.readTTFUByte();
+                }
+            } else if ( topDictIndexOffsetSize == 2 ) {
+                for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
+                    topDictIndexOffsets [ i ] = in.readTTFUShort();
+                }
+            } else if ( topDictIndexOffsetSize == 4 ) {
+                for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
+                    topDictIndexOffsets [ i ] = in.readTTFULong();
+                }
+            } else {
+                throw new RuntimeException ( "invalid offset size, got " + topDictIndexOffsetSize + ", expected 1, 2, or 4" );
+            }
+            int topDictIndexDataOffset = in.getCurrentPos() - 1;
+            byte[][] topDicts = new byte [ topDictIndexCount ][];
+            for ( int i = 0, n = topDicts.length, nOffsets = topDictIndexOffsets.length; i < n; i++ ) {
+                assert ( i + 1 ) < nOffsets;
+                long offCurrent = topDictIndexOffsets [ i ];
+                long offNext = topDictIndexOffsets [ i + 1 ];
+                long numBytes = offNext - offCurrent;
+                byte[] topDict;
+                if ( numBytes > 0 ) {
+                    if ( numBytes < Integer.MAX_VALUE ) {
+                        long topDictOffset = topDictIndexDataOffset + offCurrent;
+                        if ( topDictOffset < Integer.MAX_VALUE ) {
+                            byte[] topDictBytes = in.getBytes ( (int) topDictOffset, (int) numBytes );
+                            topDict = topDictBytes;
+                        } else {
+                            throw new UnsupportedOperationException ( "unsupported index offset value, got " + topDictOffset + ", expected less than " + Integer.MAX_VALUE );
+                        }
+                    } else {
+                        throw new UnsupportedOperationException ( "unsupported indexed data length, got " + numBytes + ", expected less than " + Integer.MAX_VALUE );
+                    }
+                } else {
+                    topDict = new byte [ 0 ];
+                }
+                topDicts [ i ] = topDict;
+            }
+            in.seekSet ( topDictIndexDataOffset + topDictIndexOffsets [ topDictIndexCount ] );
+        }
+
+        return new byte[] {};
+    }
+
+}
index 54324be52e2dd2d8957c167362ebddc9e63a024c..9612dee00de92a314d91073693d17cb9fc2a5dbc 100644 (file)
@@ -109,11 +109,6 @@ public class TTFFontLoader extends FontLoader {
 
 
     private void buildFont(TTFFile ttf, String ttcFontName) {
-        if (ttf.isCFF()) {
-            throw new UnsupportedOperationException(
-                    "OpenType fonts with CFF data are not supported, yet");
-        }
-
         boolean isCid = this.embedded;
         if (this.encodingMode == EncodingMode.SINGLE_BYTE) {
             isCid = false;
index e081734be42d4551407d0fe9ee9ec8be015c35e4..5d12fd1e93046dd1d3cf1f6471c8856a3e1d1de8 100644 (file)
@@ -43,6 +43,7 @@ public class TTFSubSetFile extends TTFFile {
      * Offsets in name table to be filled out by table.
      * The offsets are to the checkSum field
      */
+    private int cffDirOffset = 0;
     private int cvtDirOffset = 0;
     private int fpgmDirOffset = 0;
     private int glyfDirOffset = 0;
@@ -86,10 +87,8 @@ public class TTFSubSetFile extends TTFFile {
     private int determineTableCount() {
         int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
         if (isCFF()) {
-            throw new UnsupportedOperationException(
-                    "OpenType fonts with CFF glyphs are not supported");
+            numTables += 1; //1 req'd table: CFF
         } else {
-            numTables += 2; //1 req'd table: glyf,loca
             if (hasCvt()) {
                 numTables++;
             }
@@ -99,6 +98,7 @@ public class TTFSubSetFile extends TTFFile {
             if (hasPrep()) {
                 numTables++;
             }
+            numTables += 2; //1 req'd table: loca,glyf
         }
         return numTables;
     }
@@ -109,10 +109,17 @@ public class TTFSubSetFile extends TTFFile {
     private void createDirectory() {
         int numTables = determineTableCount();
         // Create the TrueType header
-        writeByte((byte)0);
-        writeByte((byte)1);
-        writeByte((byte)0);
-        writeByte((byte)0);
+        if (isCFF() ) {
+            writeByte((byte)'O');
+            writeByte((byte)'T');
+            writeByte((byte)'T');
+            writeByte((byte)'O');
+        } else {
+            writeByte((byte)0);
+            writeByte((byte)1);
+            writeByte((byte)0);
+            writeByte((byte)0);
+        }
         realSize += 4;
 
         writeUShort(numTables);
@@ -131,24 +138,6 @@ public class TTFSubSetFile extends TTFFile {
         realSize += 2;
 
         // Create space for the table entries
-        if (hasCvt()) {
-            writeString("cvt ");
-            cvtDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
-        }
-
-        if (hasFpgm()) {
-            writeString("fpgm");
-            fpgmDirOffset = currentPos;
-            currentPos += 12;
-            realSize += 16;
-        }
-
-        writeString("glyf");
-        glyfDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
 
         writeString("head");
         headDirOffset = currentPos;
@@ -165,22 +154,49 @@ public class TTFSubSetFile extends TTFFile {
         currentPos += 12;
         realSize += 16;
 
-        writeString("loca");
-        locaDirOffset = currentPos;
-        currentPos += 12;
-        realSize += 16;
-
         writeString("maxp");
         maxpDirOffset = currentPos;
         currentPos += 12;
         realSize += 16;
 
+        if (hasCvt()) {
+            writeString("cvt ");
+            cvtDirOffset = currentPos;
+            currentPos += 12;
+            realSize += 16;
+        }
+
+        if (hasFpgm()) {
+            writeString("fpgm");
+            fpgmDirOffset = currentPos;
+            currentPos += 12;
+            realSize += 16;
+        }
+
         if (hasPrep()) {
             writeString("prep");
             prepDirOffset = currentPos;
             currentPos += 12;
             realSize += 16;
         }
+
+        if (isCFF()) {
+            writeString("CFF ");
+            cffDirOffset = currentPos;
+            currentPos += 12;
+            realSize += 16;
+        } else {
+            writeString("loca");
+            locaDirOffset = currentPos;
+            currentPos += 12;
+            realSize += 16;
+
+            writeString("glyf");
+            glyfDirOffset = currentPos;
+            currentPos += 12;
+            realSize += 16;
+        }
+
     }
 
 
@@ -441,6 +457,35 @@ public class TTFSubSetFile extends TTFFile {
         }
     }
 
+    /**
+     * Create the CFF table
+     */
+    private void createCff(FontFileReader in, Map glyphs) throws IOException {
+        TTFDirTabEntry entry = (TTFDirTabEntry) dirTabs.get("CFF ");
+        int size = 0;
+        int start = 0;
+        if ( entry != null ) {
+            pad4();
+            start = currentPos;
+
+            byte[] cffData = CFFUtil.extractGlyphSubset ( in, entry, glyphs );
+            int cffDataLength = cffData.length;
+            System.arraycopy ( cffData, 0, output, currentPos, cffDataLength );
+            currentPos += cffDataLength;
+            realSize += cffDataLength;
+            size = currentPos - start;
+
+            int checksum = getCheckSum(start, size);
+            writeULong(cffDirOffset, checksum);
+            writeULong(cffDirOffset + 4, start);
+            writeULong(cffDirOffset + 8, size);
+            currentPos += 12;
+            realSize += 12;
+
+        } else {
+            throw new IOException("Can't find CFF table");
+        }
+    }
 
     /**
      * Create the hmtx table by copying metrics from original
@@ -509,9 +554,10 @@ public class TTFSubSetFile extends TTFFile {
         getNumGlyphs(in);
         readHorizontalHeader(in);
         readHorizontalMetrics(in);
-        readIndexToLocation(in);
-
-        scanGlyphs(in, subsetGlyphs);
+        if (!isCFF()) {
+            readIndexToLocation(in);
+            scanGlyphs(in, subsetGlyphs);
+        }
 
         createDirectory();                // Create the TrueType header and directory
 
@@ -539,8 +585,12 @@ public class TTFSubSetFile extends TTFFile {
             log.debug("TrueType: prep table not present. Skipped.");
         }
 
-        createLoca(subsetGlyphs.size());    // create empty loca table
-        createGlyf(in, subsetGlyphs);       //create glyf table and update loca table
+        if (isCFF()) {
+            createCff(in, subsetGlyphs);
+        } else {
+            createLoca(subsetGlyphs.size());    // create empty loca table
+            createGlyf(in, subsetGlyphs);       //create glyf table and update loca table
+        }
 
         pad4();
         createCheckSumAdjustment();