Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /* NOTICE: This file has been changed by Plutext Pty Ltd for use in docx4j.
   * The package name has been changed; there may also be other changes.
   * 
   * This notice is included to meet the condition in clause 4(b) of the License. 
   */
  
   /*
   * 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: TTFSubSetFile.java 679326 2008-07-24 09:35:34Z vhennebert $ */
 
 package org.docx4j.fonts.fop.fonts.truetype;
 
 import java.util.List;
 import java.util.Map;


Reads a TrueType file and generates a subset that can be used to embed a TrueType CID font. TrueType tables needed for embedded CID fonts are: "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm". The TrueType spec can be found at the Microsoft Typography site: http://www.microsoft.com/truetype/
 
 public class TTFSubSetFile extends TTFFile {
 
     private byte[] output = null;
     private int realSize = 0;
     private int currentPos = 0;
 
     /*
      * Offsets in name table to be filled out by table.
      * The offsets are to the checkSum field
      */
     private int cvtDirOffset = 0;
     private int fpgmDirOffset = 0;
     private int glyfDirOffset = 0;
     private int headDirOffset = 0;
     private int hheaDirOffset = 0;
     private int hmtxDirOffset = 0;
     private int locaDirOffset = 0;
     private int maxpDirOffset = 0;
     private int prepDirOffset = 0;
 
     private int checkSumAdjustmentOffset = 0;
     private int locaOffset = 0;

    
Initalize the output array
 
     private void init(int size) {
          = new byte[size];
          = 0;
          = 0;
 
         // createDirectory()
     }
 
     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");
         } else {
             numTables += 2; //1 req'd table: glyf,loca
             if (hasCvt()) {
                 numTables++;
             }
             if (hasFpgm()) {
                 numTables++;
             }
             if (hasPrep()) {
                 numTables++;
             }
         }
         return numTables;
     }

    
Create the directory table
 
     private void createDirectory() {
        int numTables = determineTableCount();
        // Create the TrueType header
        writeByte((byte)0);
        writeByte((byte)1);
        writeByte((byte)0);
        writeByte((byte)0);
         += 4;
        writeUShort(numTables);
         += 2;
        // Create searchRange, entrySelector and rangeShift
        int maxPow = maxPow2(numTables);
        int searchRange = maxPow * 16;
        writeUShort(searchRange);
         += 2;
        writeUShort(maxPow);
         += 2;
        writeUShort((numTables * 16) - searchRange);
         += 2;
        // Create space for the table entries
        if (hasCvt()) {
            writeString("cvt ");
             = ;
             += 12;
             += 16;
        }
        if (hasFpgm()) {
            writeString("fpgm");
             = ;
             += 12;
             += 16;
        }
        writeString("glyf");
         = ;
         += 12;
         += 16;
        writeString("head");
         = ;
         += 12;
         += 16;
        writeString("hhea");
         = ;
         += 12;
         += 16;
        writeString("hmtx");
         = ;
         += 12;
         += 16;
        writeString("loca");
         = ;
         += 12;
         += 16;
        writeString("maxp");
         = ;
         += 12;
         += 16;
        if (hasPrep()) {
            writeString("prep");
             = ;
             += 12;
             += 16;
        }
    }


    
Copy the cvt table as is from original font to subset font
    private boolean createCvt(FontFileReader inthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("cvt ");
        if (entry != null) {
            pad4();
            seekTab(in"cvt ", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
            return true;
        } else {
            return false;
            //throw new IOException("Can't find cvt table");
        }
    }
    private boolean hasCvt() {
        return .containsKey("cvt ");
    }
    private boolean hasFpgm() {
        return .containsKey("fpgm");
    }
    private boolean hasPrep() {
        return .containsKey("prep");
    }

    
Copy the fpgm table as is from original font to subset font
    private boolean createFpgm(FontFileReader inthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("fpgm");
        if (entry != null) {
            pad4();
            seekTab(in"fpgm", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
            return true;
        } else {
            return false;
        }
    }



    
Create an empty loca table without updating checksum
    private void createLoca(int sizethrows IOException {
        pad4();
         = ;
        writeULong( + 4, );
        writeULong( + 8, size * 4 + 4);
         += size * 4 + 4;
         += size * 4 + 4;
    }


    
Copy the maxp table as is from original font to subset font and set num glyphs to size
    private void createMaxp(FontFileReader inint sizethrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("maxp");
        if (entry != null) {
            pad4();
            seekTab(in"maxp", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
            writeUShort( + 4, size);
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
        } else {
            throw new IOException("Can't find maxp table");
        }
    }


    
Copy the prep table as is from original font to subset font
    private boolean createPrep(FontFileReader inthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("prep");
        if (entry != null) {
            pad4();
            seekTab(in"prep", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
            return true;
        } else {
            return false;
        }
    }


    
Copy the hhea table as is from original font to subset font and fill in size of hmtx table
    private void createHhea(FontFileReader inint sizethrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("hhea");
        if (entry != null) {
            pad4();
            seekTab(in"hhea", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
            writeUShort((int)entry.getLength() +  - 2, size);
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
        } else {
            throw new IOException("Can't find hhea table");
        }
    }


    
Copy the head table as is from original font to subset font and set indexToLocaFormat to long and set checkSumAdjustment to 0, store offset to checkSumAdjustment in checkSumAdjustmentOffset
    private void createHead(FontFileReader inthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("head");
        if (entry != null) {
            pad4();
            seekTab(in"head", 0);
            System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
                             0, , (int)entry.getLength());
             =  + 8;
            [ + 8] = 0;     // Set checkSumAdjustment to 0
            [ + 9] = 0;
            [ + 10] = 0;
            [ + 11] = 0;
            [ + 50] = 0;    // long locaformat
            [ + 51] = 1;    // long locaformat
            int checksum = getCheckSum(, (int)entry.getLength());
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, (int)entry.getLength());
             += (int)entry.getLength();
             += (int)entry.getLength();
        } else {
            throw new IOException("Can't find head table");
        }
    }


    
Create the glyf table and fill in loca table
    private void createGlyf(FontFileReader in,
                            Map glyphsthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("glyf");
        int size = 0;
        int start = 0;
        int endOffset = 0;    // Store this as the last loca
        if (entry != null) {
            pad4();
            start = ;
            /* Loca table must be in order by glyph index, so build
             * an array first and then write the glyph info and
             * location offset.
             */
            int[] origIndexes = new int[glyphs.size()];
            Iterator e = glyphs.keySet().iterator();
            while (e.hasNext()) {
                Integer origIndex = (Integer)e.next();
                Integer subsetIndex = (Integer)glyphs.get(origIndex);
                origIndexes[subsetIndex.intValue()] = origIndex.intValue();
            }
            for (int i = 0; i < origIndexes.lengthi++) {
                int glyphLength = 0;
                int nextOffset = 0;
                int origGlyphIndex = origIndexes[i];
                if (origGlyphIndex >= (. - 1)) {
                    nextOffset = (int);
                } else {
                    nextOffset = (int)[origGlyphIndex + 1].getOffset();
                }
                glyphLength = nextOffset - (int)[origGlyphIndex].getOffset();
                // Copy glyph
                System.arraycopy(
                    in.getBytes((int)entry.getOffset() + (int)[origGlyphIndex].getOffset(),
                        glyphLength), 0,
                    ,
                    glyphLength);
                // Update loca table
                writeULong( + i * 4,  - start);
                if (( - start + glyphLength) > endOffset) {
                    endOffset = ( - start + glyphLength);
                }
                 += glyphLength;
                 += glyphLength;
            }
            size =  - start;
            int checksum = getCheckSum(startsize);
            writeULong(checksum);
            writeULong( + 4, start);
            writeULong( + 8, size);
             += 12;
             += 12;
            // Update loca checksum and last loca index
            writeULong( + glyphs.size() * 4, endOffset);
            checksum = getCheckSum(glyphs.size() * 4 + 4);
            writeULong(checksum);
        } else {
            throw new IOException("Can't find glyf table");
        }
    }


    
Create the hmtx table by copying metrics from original font to subset font. The glyphs Map contains an Integer key and Integer value that maps the original metric (key) to the subset metric (value)
    private void createHmtx(FontFileReader in,
                            Map glyphsthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("hmtx");
        int longHorMetricSize = glyphs.size() * 2;
        int leftSideBearingSize = glyphs.size() * 2;
        int hmtxSize = longHorMetricSize + leftSideBearingSize;
        if (entry != null) {
            pad4();
            //int offset = (int)entry.offset;
            Iterator e = glyphs.keySet().iterator();
            while (e.hasNext()) {
                Integer origIndex = (Integer)e.next();
                Integer subsetIndex = (Integer)glyphs.get(origIndex);
                writeUShort( + subsetIndex.intValue() * 4,
                            [origIndex.intValue()].getWx());
                writeUShort( + subsetIndex.intValue() * 4 + 2,
                            [origIndex.intValue()].getLsb());
            }
            int checksum = getCheckSum(hmtxSize);
            writeULong(checksum);
            writeULong( + 4, );
            writeULong( + 8, hmtxSize);
             += hmtxSize;
             += hmtxSize;
        } else {
            throw new IOException("Can't find hmtx table");
        }
    }

    
Returns a List containing the glyph itself plus all glyphs that this composite glyph uses
    private List getIncludedGlyphs(FontFileReader inint glyphOffset,
                                     Integer glyphIdxthrows IOException {
        List ret = new java.util.ArrayList();
        ret.add(glyphIdx);
        int offset = glyphOffset + (int)[glyphIdx.intValue()].getOffset() + 10;
        Integer compositeIdx = null;
        int flags = 0;
        boolean moreComposites = true;
        while (moreComposites) {
            flags = in.readTTFUShort(offset);
            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
            ret.add(compositeIdx);
            offset += 4;
            if ((flags & 1) > 0) {
                // ARG_1_AND_ARG_2_ARE_WORDS
                offset += 4;
            } else {
                offset += 2;
            }
            if ((flags & 8) > 0) {
                offset += 2;    // WE_HAVE_A_SCALE
            } else if ((flags & 64) > 0) {
                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
            } else if ((flags & 128) > 0) {
                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
            }
            if ((flags & 32) > 0) {
                moreComposites = true;
            } else {
                moreComposites = false;
            }
        }
        return ret;
    }


    
Rewrite all compositepointers in glyphindex glyphIdx
    private void remapComposite(FontFileReader inMap glyphs,
                                int glyphOffset,
                                Integer glyphIdxthrows IOException {
        int offset = glyphOffset + (int)[glyphIdx.intValue()].getOffset()
                     + 10;
        Integer compositeIdx = null;
        int flags = 0;
        boolean moreComposites = true;
        while (moreComposites) {
            flags = in.readTTFUShort(offset);
            compositeIdx = new Integer(in.readTTFUShort(offset + 2));
            Integer newIdx = (Integer)glyphs.get(compositeIdx);
            if (newIdx == null) {
                // This errormessage would look much better
                // if the fontname was printed to
                //log.error("An embedded font "
                //                     + "contains bad glyph data. "
                //                     + "Characters might not display "
                //                     + "correctly.");
                moreComposites = false;
                continue;
            }
            in.writeTTFUShort(offset + 2, newIdx.intValue());
            offset += 4;
            if ((flags & 1) > 0) {
                // ARG_1_AND_ARG_2_ARE_WORDS
                offset += 4;
            } else {
                offset += 2;
            }
            if ((flags & 8) > 0) {
                offset += 2;    // WE_HAVE_A_SCALE
            } else if ((flags & 64) > 0) {
                offset += 4;    // WE_HAVE_AN_X_AND_Y_SCALE
            } else if ((flags & 128) > 0) {
                offset += 8;    // WE_HAVE_A_TWO_BY_TWO
            }
            if ((flags & 32) > 0) {
                moreComposites = true;
            } else {
                moreComposites = false;
            }
        }
    }


    
Scan all the original glyphs for composite glyphs and add those glyphs to the glyphmapping also rewrite the composite glyph pointers to the new mapping
    private void scanGlyphs(FontFileReader in,
                            Map glyphsthrows IOException {
        TTFDirTabEntry entry = (TTFDirTabEntry).get("glyf");
        Map newComposites = null;
        Map allComposites = new java.util.HashMap();
        int newIndex = glyphs.size();
        if (entry != null) {
            while (newComposites == null || newComposites.size() > 0) {
                // Inefficient to iterate through all glyphs
                newComposites = new java.util.HashMap();
                Iterator e = glyphs.keySet().iterator();
                while (e.hasNext()) {
                    Integer origIndex = (Integer)e.next();
                    if (in.readTTFShort(entry.getOffset()
                                        + [origIndex.intValue()].getOffset()) < 0) {
                        // origIndex is a composite glyph
                        allComposites.put(origIndexglyphs.get(origIndex));
                        List composites
                            = getIncludedGlyphs(in, (int)entry.getOffset(),
                                              origIndex);
                        // Iterate through all composites pointed to
                        // by this composite and check if they exists
                        // in the glyphs map, add them if not.
                        Iterator cps = composites.iterator();
                        while (cps.hasNext()) {
                            Integer cIdx = (Integer)cps.next();
                            if (glyphs.get(cIdx) == null
                                    && newComposites.get(cIdx) == null) {
                                newComposites.put(cIdx,
                                                  new Integer(newIndex));
                                newIndex++;
                            }
                        }
                    }
                }
                // Add composites to glyphs
                Iterator m = newComposites.keySet().iterator();
                while (m.hasNext()) {
                    Integer im = (Integer)m.next();
                    glyphs.put(imnewComposites.get(im));
                }
            }
            // Iterate through all composites to remap their composite index
            Iterator ce = allComposites.keySet().iterator();
            while (ce.hasNext()) {
                remapComposite(inglyphs, (int)entry.getOffset(),
                               (Integer)ce.next());
            }
        } else {
            throw new IOException("Can't find glyf table");
        }
    }



    
Returns a subset of the original font.

Parameters:
in FontFileReader to read from
name Name to be checked for in the font file
glyphs Map of glyphs (glyphs has old index as (Integer) key and new index as (Integer) value)
Returns:
A subset of the original font
Throws:
java.io.IOException in case of an I/O problem
    public byte[] readFont(FontFileReader inString name,
                           Map glyphsthrows IOException {
        //Check if TrueType collection, and that the name exists in the collection
        if (!checkTTC(inname)) {
            throw new IOException("Failed to read font");
        }
        //Copy the Map as we're going to modify it
        Map subsetGlyphs = new java.util.HashMap(glyphs);
         = new byte[in.getFileSize()];
        readDirTabs(in);
        readFontHeader(in);
        getNumGlyphs(in);
        readHorizontalHeader(in);
        readHorizontalMetrics(in);
        readIndexToLocation(in);
        scanGlyphs(insubsetGlyphs);
        createDirectory();                // Create the TrueType header and directory
        createHead(in);
        createHhea(insubsetGlyphs.size());    // Create the hhea table
        createHmtx(insubsetGlyphs);           // Create hmtx table
        createMaxp(insubsetGlyphs.size());    // copy the maxp table
        boolean optionalTableFound;
        optionalTableFound = createCvt(in);    // copy the cvt table
        if (!optionalTableFound) {
            // cvt is optional (used in TrueType fonts only)
            .debug("TrueType: ctv table not present. Skipped.");
        }
        optionalTableFound = createFpgm(in);    // copy fpgm table
        if (!optionalTableFound) {
            // fpgm is optional (used in TrueType fonts only)
            .debug("TrueType: fpgm table not present. Skipped.");
        }
        optionalTableFound = createPrep(in);    // copy prep table
        if (!optionalTableFound) {
            // prep is optional (used in TrueType fonts only)
            .debug("TrueType: prep table not present. Skipped.");
        }
        createLoca(subsetGlyphs.size());    // create empty loca table
        createGlyf(insubsetGlyphs);       //create glyf table and update loca table
        pad4();
        createCheckSumAdjustment();
        byte[] ret = new byte[];
        System.arraycopy(, 0, ret, 0, );
        return ret;
    }

    
writes a ISO-8859-1 string at the currentPosition updates currentPosition but not realSize

Returns:
number of bytes written
    private int writeString(String str) {
        int length = 0;
        try {
            byte[] buf = str.getBytes("ISO-8859-1");
            System.arraycopy(buf, 0, buf.length);
            length = buf.length;
             += length;
        } catch (java.io.UnsupportedEncodingException e) {
            // This should never happen!
        }
        return length;
    }

    
Appends a byte to the output array, updates currentPost but not realSize
    private void writeByte(byte b) {
        [++] = b;
    }

    
Appends a USHORT to the output array, updates currentPost but not realSize
    private void writeUShort(int s) {
        byte b1 = (byte)((s >> 8) & 0xff);
        byte b2 = (byte)(s & 0xff);
        writeByte(b1);
        writeByte(b2);
    }

    
Appends a USHORT to the output array, at the given position without changing currentPos
    private void writeUShort(int posint s) {
        byte b1 = (byte)((s >> 8) & 0xff);
        byte b2 = (byte)(s & 0xff);
        [pos] = b1;
        [pos + 1] = b2;
    }

    
Appends a ULONG to the output array, updates currentPos but not realSize
    private void writeULong(int s) {
        byte b1 = (byte)((s >> 24) & 0xff);
        byte b2 = (byte)((s >> 16) & 0xff);
        byte b3 = (byte)((s >> 8) & 0xff);
        byte b4 = (byte)(s & 0xff);
        writeByte(b1);
        writeByte(b2);
        writeByte(b3);
        writeByte(b4);
    }

    
Appends a ULONG to the output array, at the given position without changing currentPos
    private void writeULong(int posint s) {
        byte b1 = (byte)((s >> 24) & 0xff);
        byte b2 = (byte)((s >> 16) & 0xff);
        byte b3 = (byte)((s >> 8) & 0xff);
        byte b4 = (byte)(s & 0xff);
        [pos] = b1;
        [pos + 1] = b2;
        [pos + 2] = b3;
        [pos + 3] = b4;
    }

    
Read a signed short value at given position
    private short readShort(int pos) {
        int ret = readUShort(pos);
        return (short)ret;
    }

    
Read a unsigned short value at given position
    private int readUShort(int pos) {
        int ret = (int)[pos];
        if (ret < 0) {
            ret += 256;
        }
        ret = ret << 8;
        if ((int)[pos + 1] < 0) {
            ret |= (int)[pos + 1] + 256;
        } else {
            ret |= (int)[pos + 1];
        }
        return ret;
    }

    
Create a padding in the fontfile to align on a 4-byte boundary
    private void pad4() {
        int padSize =  % 4;
        for (int i = 0; i < padSizei++) {
            [++] = 0;
            ++;
        }
    }

    
Returns the maximum power of 2 <= max
    private int maxPow2(int max) {
        int i = 0;
        while (Math.pow(2, (double)i) < max) {
            i++;
        }
        return (i - 1);
    }
    private int log2(int num) {
        return (int)(Math.log((double)num) / Math.log(2));
    }
    private int getCheckSum(int startint size) {
        return (int)getLongCheckSum(startsize);
    }
    private long getLongCheckSum(int startint size) {
        // All the tables here are aligned on four byte boundaries
        // Add remainder to size if it's not a multiple of 4
        int remainder = size % 4;
        if (remainder != 0) {
            size += remainder;
        }
        long sum = 0;
        for (int i = 0; i < sizei += 4) {
            int l = (int)([start + i] << 24);
            l += (int)([start + i + 1] << 16);
            l += (int)([start + i + 2] << 16);
            l += (int)([start + i + 3] << 16);
            sum += l;
            if (sum > 0xffffffff) {
                sum = sum - 0xffffffff;
            }
        }
        return sum;
    }
    private void createCheckSumAdjustment() {
        long sum = getLongCheckSum(0, );
        int checksum = (int)(0xb1b0afba - sum);
        writeULong(checksum);
    }
New to GrepCode? Check out our FAQ X