aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfotis <fotis@unknown>2001-03-23 08:41:34 +0000
committerfotis <fotis@unknown>2001-03-23 08:41:34 +0000
commitbd3f6e8a9b2b7a5182b25f51859eb5215676f257 (patch)
tree099e51ec93761221a9396f9102c844f8bb267ba0
parentbf9c64641acd1fef7c4b26ec666cf18bdb8e124b (diff)
downloadxmlgraphics-fop-bd3f6e8a9b2b7a5182b25f51859eb5215676f257.tar.gz
xmlgraphics-fop-bd3f6e8a9b2b7a5182b25f51859eb5215676f257.zip
subset fonts embedding [submitted by Tore Engvig]
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194172 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/org/apache/fop/fonts/TTFSubSetFile.java761
1 files changed, 761 insertions, 0 deletions
diff --git a/src/org/apache/fop/fonts/TTFSubSetFile.java b/src/org/apache/fop/fonts/TTFSubSetFile.java
new file mode 100644
index 000000000..23652031a
--- /dev/null
+++ b/src/org/apache/fop/fonts/TTFSubSetFile.java
@@ -0,0 +1,761 @@
+/* -- $Id$
+ *
+ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources."
+ */
+
+package org.apache.fop.fonts;
+import java.io.*;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * 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 {
+ byte[] output = null;
+ int realSize = 0;
+ int currentPos = 0;
+
+ /* Offsets in name table to be filled out by table.
+ The offsets are to the checkSum field */
+ int cvtDirOffset = 0;
+ int fpgmDirOffset = 0;
+ int glyfDirOffset = 0;
+ int headDirOffset = 0;
+ int hheaDirOffset = 0;
+ int hmtxDirOffset = 0;
+ int locaDirOffset = 0;
+ int maxpDirOffset = 0;
+ int prepDirOffset = 0;
+
+ int checkSumAdjustmentOffset = 0;
+ int locaOffset = 0;
+
+ /**
+ * Initalize the output array
+ */
+ private void init(int size) {
+ output = new byte[size];
+ realSize = 0;
+ currentPos = 0;
+
+ // createDirectory()
+ }
+
+ /**
+ Create the directory table
+ */
+ private void createDirectory() {
+ int numTables = 9;
+ // Create the TrueType header
+ writeByte((byte)0);
+ writeByte((byte)1);
+ writeByte((byte)0);
+ writeByte((byte)0);
+ realSize+=4;
+
+ writeUShort(numTables);
+ realSize += 2;
+
+ // Create searchRange, entrySelector and rangeShift
+ int maxPow = maxPow2(numTables);
+ int searchRange = maxPow*16;
+ writeUShort(searchRange);
+ realSize += 2;
+
+ writeUShort(maxPow);
+ realSize += 2;
+
+ writeUShort((numTables*16) - searchRange);
+ realSize += 2;
+
+ // Create space for the table entries
+ writeString("cvt ");
+ cvtDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("fpgm");
+ fpgmDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("glyf");
+ glyfDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("head");
+ headDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("hhea");
+ hheaDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("hmtx");
+ hmtxDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("loca");
+ locaDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("maxp");
+ maxpDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+
+ writeString("prep");
+ prepDirOffset = currentPos;
+ currentPos+=12;
+ realSize+=16;
+ }
+
+
+ /**
+ * Copy the cvt table as is from original font to subset font
+ */
+ private void createCvt(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "cvt ", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(cvtDirOffset, checksum);
+ writeULong(cvtDirOffset+4, currentPos);
+ writeULong(cvtDirOffset+8, (int)entry.length);
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } else {
+ throw new IOException ("Can't find cvt table");
+ }
+ }
+
+
+
+ /**
+ * Copy the fpgm table as is from original font to subset font
+ */
+ private void createFpgm(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "fpgm", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(fpgmDirOffset, checksum);
+ writeULong(fpgmDirOffset+4, currentPos);
+ writeULong(fpgmDirOffset+8, (int)entry.length);
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } else {
+ throw new IOException ("Can't find fpgm table");
+ }
+ }
+
+
+
+ /**
+ * Create an empty loca table without updating checksum
+ */
+ private void createLoca(int size) throws IOException {
+ pad4();
+ locaOffset = currentPos;
+ writeULong(locaDirOffset+4, currentPos);
+ writeULong(locaDirOffset+8, size*4+4);
+ currentPos+=size*4+4;
+ realSize+=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 in, int size) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "maxp", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+ writeUShort(currentPos+4, size);
+
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(maxpDirOffset, checksum);
+ writeULong(maxpDirOffset+4, currentPos);
+ writeULong(maxpDirOffset+8, (int)entry.length);
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } else {
+ throw new IOException ("Can't find maxp table");
+ }
+ }
+
+
+ /**
+ * Copy the prep table as is from original font to subset font
+ */
+ private void createPrep(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "prep", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(prepDirOffset, checksum);
+ writeULong(prepDirOffset+4, currentPos);
+ writeULong(prepDirOffset+8, (int)entry.length);
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } else {
+ throw new IOException ("Can't find prep table");
+ }
+ }
+
+
+ /**
+ * Copy the hhea table as is from original font to subset font
+ * and fill in size of hmtx table
+ */
+ private void createHhea(FontFileReader in, int size) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "hhea", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+ writeUShort((int)entry.length + currentPos-2, size);
+
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(hheaDirOffset, checksum);
+ writeULong(hheaDirOffset+4, currentPos);
+ writeULong(hheaDirOffset+8, (int)entry.length);
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } 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 in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+ if (entry != null) {
+ pad4();
+ seek_tab(in, "head", 0);
+ System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length),
+ 0, output, currentPos, (int)entry.length);
+
+ checkSumAdjustmentOffset = currentPos + 8;
+ output[currentPos+8] = 0; // Set checkSumAdjustment to 0
+ output[currentPos+9] = 0;
+ output[currentPos+10] = 0;
+ output[currentPos+11] = 0;
+ output[currentPos+(int)entry.length-2] = 0; // long locaformat
+
+ int checksum = getCheckSum(currentPos, (int)entry.length);
+ writeULong(headDirOffset, checksum);
+ writeULong(headDirOffset+4, currentPos);
+ writeULong(headDirOffset+8, (int)entry.length);
+
+ currentPos+=(int)entry.length;
+ realSize+=(int)entry.length;
+ } else {
+ throw new IOException ("Can't find head table");
+ }
+ }
+
+
+ /**
+ * Create the glyf table and fill in loca table
+ */
+ private void createGlyf(FontFileReader in, Hashtable glyphs)
+ throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ int size = 0;
+ int start = 0;
+ int endOffset = 0; // Store this as the last loca
+ if (entry != null) {
+ pad4();
+ start = currentPos;
+
+ for (Enumeration e = glyphs.keys(); e.hasMoreElements();) {
+ int glyphLength = 0;
+ Integer origIndex = (Integer)e.nextElement();
+ Integer subsetIndex = (Integer)glyphs.get(origIndex);
+
+ int nextOffset = 0;
+ if (origIndex.intValue() >= (mtx_tab.length-1))
+ nextOffset = (int)lastLoca;
+ else
+ nextOffset = (int)mtx_tab[origIndex.intValue()+1].offset;
+
+ glyphLength = nextOffset -
+ (int)mtx_tab[origIndex.intValue()].offset;
+
+ // Copy glyph
+ System.arraycopy(in.getBytes((int)entry.offset +
+ (int)mtx_tab[origIndex.intValue()].offset,
+ glyphLength),
+ 0, output,
+ currentPos,
+ glyphLength);
+
+
+ // Update loca table
+ writeULong(locaOffset + subsetIndex.intValue()*4,
+ currentPos-start);
+ if ((currentPos - start + glyphLength) > endOffset)
+ endOffset = (currentPos - start + glyphLength);
+
+ currentPos+=glyphLength;
+ realSize+=glyphLength;
+
+ }
+
+ size = currentPos - start;
+
+ int checksum = getCheckSum(currentPos, size);
+ writeULong(glyfDirOffset, checksum);
+ writeULong(glyfDirOffset+4, start);
+ writeULong(glyfDirOffset+8, size);
+ currentPos+=12;
+ realSize+=12;
+
+ // Update loca checksum and last loca index
+ writeULong(locaOffset + glyphs.size()*4,
+ endOffset);
+
+ checksum = getCheckSum(locaOffset, glyphs.size()*4+4);
+ writeULong(locaDirOffset, 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 hashtable contains an
+ * Integer key and Integer value that maps the original
+ * metric (key) to the subset metric (value)
+ */
+ private void createHmtx(FontFileReader in, Hashtable glyphs)
+ throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.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;
+ for (Enumeration e = glyphs.keys(); e.hasMoreElements();) {
+ Integer origIndex = (Integer)e.nextElement();
+ Integer subsetIndex = (Integer)glyphs.get(origIndex);
+
+ writeUShort(currentPos+subsetIndex.intValue()*4,
+ mtx_tab[origIndex.intValue()].wx);
+ writeUShort(currentPos+subsetIndex.intValue()*4+2,
+ mtx_tab[origIndex.intValue()].lsb);
+ }
+
+ int checksum = getCheckSum(currentPos, hmtxSize);
+ writeULong(hmtxDirOffset, checksum);
+ writeULong(hmtxDirOffset+4, currentPos);
+ writeULong(hmtxDirOffset+8, hmtxSize);
+ currentPos+=hmtxSize;
+ realSize+=hmtxSize;
+ } else {
+ throw new IOException ("Can't find hmtx table");
+ }
+ }
+
+ /**
+ * Returns a Vector containing the glyph itself plus all glyphs
+ * that this composite glyph uses
+ */
+ private Vector getIncludedGlyphs(FontFileReader in,
+ int glyphOffset, Integer glyphIdx)
+ throws IOException {
+ Vector ret = new Vector();
+ ret.addElement(glyphIdx);
+ int offset = glyphOffset +
+ (int)mtx_tab[glyphIdx.intValue()].offset + 10;
+ Integer compositeIdx = null;
+ int flags = 0;
+ boolean moreComposites = true;
+ while (moreComposites) {
+ flags = in.readTTFUShort(offset);
+ compositeIdx = new Integer(in.readTTFUShort(offset+2));
+ ret.addElement(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 in, Hashtable glyphs,
+ int glyphOffset, Integer glyphIdx)
+ throws IOException {
+ int offset = glyphOffset +
+ (int)mtx_tab[glyphIdx.intValue()].offset + 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);
+ 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, Hashtable glyphs) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ Hashtable newComposites = null;
+ Hashtable allComposites = new Hashtable();
+
+ int newIndex = glyphs.size();
+
+ if (entry != null) {
+ while (newComposites == null || newComposites.size() > 0) {
+ // Inefficient to iterate through all glyphs
+ newComposites = new Hashtable();
+
+ for (Enumeration e = glyphs.keys(); e.hasMoreElements();) {
+ Integer origIndex = (Integer)e.nextElement();
+
+ if (in.readTTFShort(entry.offset +
+ mtx_tab[origIndex.intValue()].offset) < 0) {
+ // origIndex is a composite glyph
+ allComposites.put(origIndex, glyphs.get(origIndex));
+ Vector composites = getIncludedGlyphs(in, (int)entry.offset,
+ origIndex);
+
+ // Iterate through all composites pointed to
+ // by this composite and check if they exists
+ // in the glyphs map, add them if not.
+ for (Enumeration cps=composites.elements();
+ cps.hasMoreElements();) {
+
+ Integer cIdx =(Integer)cps.nextElement();
+ if (glyphs.get(cIdx) == null &&
+ newComposites.get(cIdx) == null) {
+ newComposites.put(cIdx, new Integer(newIndex));
+ newIndex++;
+ }
+ }
+ }
+ }
+
+ // Add composites to glyphs
+ for (Enumeration m = newComposites.keys(); m.hasMoreElements();) {
+ Integer im = (Integer)m.nextElement();
+ glyphs.put(im, newComposites.get(im));
+ }
+ }
+
+ // Iterate through all composites to remap their composite index
+
+ for (Enumeration ce = allComposites.keys(); ce.hasMoreElements();) {
+ remapComposite(in, glyphs, (int)entry.offset,
+ (Integer)ce.nextElement());
+ }
+
+ } else {
+ throw new IOException ("Can't find glyf table");
+ }
+ }
+
+
+
+ /**
+ * glyphs has old index as (Integer) key and new index
+ * as (Integer) value
+ */
+
+ public byte[] readFont(FontFileReader in, String name,
+ Hashtable glyphs) throws IOException {
+
+ /* Check if TrueType collection, and that the name
+ exists in the collection
+ */
+ if (!checkTTC(in, name, false))
+ throw new IOException("Failed to read font");
+
+ output = new byte[in.getFileSize()];
+
+ readDirTabs(in);
+ readFontHeader(in);
+ getNumGlyphs(in);
+ readHorizontalHeader(in);
+ readHorizontalMetrics(in);
+ readIndexToLocation(in);
+
+ scanGlyphs(in, glyphs);
+
+ createDirectory(); // Create the TrueType header and directory
+ createCvt(in); // copy the cvt table
+ createFpgm(in); // copy fpgm table
+ createHead(in);
+ createHhea(in, glyphs.size()); // Create the hhea table
+ createHmtx(in, glyphs); // Create hmtx table
+ createMaxp(in, glyphs.size()); // copy the maxp table
+ createPrep(in); // copy prep table
+ createLoca(glyphs.size()); // create empty loca table
+ createGlyf(in, glyphs);
+
+ pad4();
+ createCheckSumAdjustment();
+
+ byte[] ret = new byte[realSize];
+ System.arraycopy(output, 0, ret, 0, realSize);
+
+ return ret;
+ }
+
+ /**
+ * writes a ISO-8859-1 string at the currentPosition
+ * updates currentPosition but not realSize
+ * @return number of bytes written
+ */
+ private int writeString(String str) {
+ int length = 0;
+ try {
+ byte[] buf = str.getBytes("ISO-8859-1");
+ System.arraycopy(buf, 0, output, currentPos, buf.length);
+ length = buf.length;
+ currentPos += length;
+ } catch (Exception e) {
+ // This should never happen!
+ }
+
+ return length;
+ }
+
+ /**
+ Appends a byte to the output array,
+ updates currentPost but not realSize
+ */
+ private void writeByte(byte b) {
+ output[currentPos++] = 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 pos, int s) {
+ byte b1 = (byte)((s >> 8) & 0xff);
+ byte b2 = (byte) (s & 0xff);
+ output[pos] = b1;
+ output[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 pos, 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);
+ output[pos] = b1;
+ output[pos+1] = b2;
+ output[pos+2] = b3;
+ output[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)output[pos];
+ if (ret < 0)
+ ret+=256;
+ ret = ret << 8;
+ if ((int)output[pos+1] < 0) {
+ ret |= (int)output[pos+1]+256;
+ } else
+ ret |= (int)output[pos+1];
+
+ return ret;
+ }
+
+ /**
+ * Create a padding in the fontfile to align
+ * on a 4-byte boundary
+ */
+ private void pad4() {
+ int padSize = currentPos % 4;
+ for (int i = 0; i < padSize; i++) {
+ output[currentPos++] = 0;
+ realSize++;
+ }
+ }
+ /**
+ * 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 start, int size) {
+ return (int)getLongCheckSum(start, size);
+ }
+
+ private long getLongCheckSum(int start, int 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 < size; i+=4) {
+ int l = (int)(output[start+i] << 24);
+ l += (int)(output[start+i+1] << 16);
+ l += (int)(output[start+i+2] << 16);
+ l += (int)(output[start+i+3] << 16);
+ sum += l;
+ if (sum > 0xffffffff)
+ sum = sum - 0xffffffff;
+ }
+
+ return sum;
+ }
+
+ private void createCheckSumAdjustment() {
+ long sum = getLongCheckSum(0, realSize);
+ int checksum = (int)(0xb1b0afba - sum);
+ writeULong(checkSumAdjustmentOffset, checksum);
+ }
+}
+
+
+