/*
* 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 426576 2006-07-28 15:44:37Z jeremias $ */
package org.newdawn.slick.tools.hiero.truetype;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
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 {
/** The output to write into */
private byte[] output = null;
/** THe real size of the sub set */
private int realSize = 0;
/** The current position we'll write to or read from */
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;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int fpgmDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int glyfDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int headDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int hheaDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int hmtxDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int locaDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int maxpDirOffset = 0;
/**
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
private int prepDirOffset = 0;
/** The check sum adjustment */
private int checkSumAdjustmentOffset = 0;
/** The offset to the locas */
private int locaOffset = 0;
/**
* Initalize the output array
*
* @param size The size of 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;
if (hasFpgm()) {
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
*
* @param in The input to read the CVT from
* @throws IOException Indicates a failure to read the entry
*/
private void createCvt(FontFileReader in) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
if (entry != null) {
pad4();
seekTab(in, "cvt ", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(cvtDirOffset, checksum);
writeULong(cvtDirOffset + 4, currentPos);
writeULong(cvtDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
throw new IOException("Can't find cvt table");
}
}
/**
* True if this subset has FPGM table
*
* @return True if the subset has FPGM
*/
private boolean hasFpgm() {
return (dirTabs.get("fpgm") != null);
}
/**
* Copy the fpgm table as is from original font to subset font
*
* @param in The stream to read the FPGM table from
* @throws IOException Indicates a failure to read the table
*/
private void createFpgm(FontFileReader in) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
if (entry != null) {
pad4();
seekTab(in, "fpgm", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(fpgmDirOffset, checksum);
writeULong(fpgmDirOffset + 4, currentPos);
writeULong(fpgmDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
//fpgm table is optional
//throw new IOException("Can't find fpgm table");
}
}
/**
* Create an empty loca table without updating checksum
*
* @param size The size of the loca to create
* @throws IOException Indicate a failure to store the loca
*/
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
*
* @param in The reader from which to obtain the info
* @param size The size of the MAXP table to write
* @throws IOException Indicates a failure to write
*/
private void createMaxp(FontFileReader in, int size) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
if (entry != null) {
pad4();
seekTab(in, "maxp", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
writeUShort(currentPos + 4, size);
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(maxpDirOffset, checksum);
writeULong(maxpDirOffset + 4, currentPos);
writeULong(maxpDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
throw new IOException("Can't find maxp table");
}
}
/**
* Copy the prep table as is from original font to subset font
*
* @param in The reader to which we're adding
* @throws IOException Indicates a failure to read the PREP table
*/
private void createPrep(FontFileReader in) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
if (entry != null) {
pad4();
seekTab(in, "prep", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(prepDirOffset, checksum);
writeULong(prepDirOffset + 4, currentPos);
writeULong(prepDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} 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
*
* @param in The reader from which to grab the HHEA table
* @param size The size of the table
* @throws IOException Indicates a failure to read data
*/
private void createHhea(FontFileReader in, int size) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
if (entry != null) {
pad4();
seekTab(in, "hhea", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
writeUShort((int)entry.getLength() + currentPos - 2, size);
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(hheaDirOffset, checksum);
writeULong(hheaDirOffset + 4, currentPos);
writeULong(hheaDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (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
*
* @param in The reader to read the HEAD table from
* @throws IOException Failure to read the table
*/
private void createHead(FontFileReader in) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
if (entry != null) {
pad4();
seekTab(in, "head", 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
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 + 50] = 0; // long locaformat
output[currentPos + 51] = 1; // long locaformat
int checksum = getCheckSum(currentPos, (int)entry.getLength());
writeULong(headDirOffset, checksum);
writeULong(headDirOffset + 4, currentPos);
writeULong(headDirOffset + 8, (int)entry.getLength());
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
throw new IOException("Can't find head table");
}
}
/**
* Create the glyf table and fill in loca table
*
* @param in Reader to get the table from
* @param glyphs The glpyhs table to populate
* @throws IOException Indicates a failure to read data
*/
private void createGlyf(FontFileReader in,
Map 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;
/* 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.length; i++) {
int glyphLength = 0;
int nextOffset = 0;
int origGlyphIndex = origIndexes[i];
if (origGlyphIndex >= (mtxTab.length - 1)) {
nextOffset = (int)lastLoca;
} else {
nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
}
glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
// Copy glyph
System.arraycopy(
in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
glyphLength), 0,
output, currentPos,
glyphLength);
// Update loca table
writeULong(locaOffset + i * 4, currentPos - start);
if ((currentPos - start + glyphLength) > endOffset) {
endOffset = (currentPos - start + glyphLength);
}
currentPos += glyphLength;
realSize += glyphLength;
}
size = currentPos - start;
int checksum = getCheckSum(start, 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 Map contains an
* Integer key and Integer value that maps the original
* metric (key) to the subset metric (value)
*
* @param in The reader from which to grab the HMTX table
* @param glyphs The glyphs table to populate
* @throws IOException Indicates a failure to read
*/
private void createHmtx(FontFileReader in,
Map 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;
Iterator e = glyphs.keySet().iterator();
while (e.hasNext()) {
Integer origIndex = (Integer)e.next();
Integer subsetIndex = (Integer)glyphs.get(origIndex);
writeUShort(currentPos + subsetIndex.intValue() * 4,
mtxTab[origIndex.intValue()].getWx());
writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
mtxTab[origIndex.intValue()].getLsb());
}
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 List containing the glyph itself plus all glyphs
* that this composite glyph uses
*
* @param in The input from which to determine the included glyphs
* @param glyphOffset The offset the glyph
* @param glyphIdx The index of the base glyph
* @return The list of glyphs building the composite
* @throws IOException Indicates a failure to read from the font file
*/
private List getIncludedGlyphs(FontFileReader in, int glyphOffset,
Integer glyphIdx) throws IOException {
List ret = new ArrayList();
ret.add(glyphIdx);
int offset = glyphOffset + (int)mtxTab[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
*
* @param in The input from which to remap
* @param glyphs The glyphs to remap
* @param glyphOffset The offset to start at
* @param glyphIdx The index of the glyph
* @throws IOException Indicates a failure to read from the font file.
*/
private void remapComposite(FontFileReader in, Map glyphs,
int glyphOffset,
Integer glyphIdx) throws IOException {
int offset = glyphOffset + (int)mtxTab[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
*
* @param in The input stream to read the glyphs from
* @param glyphs The glyphs map to populate
* @throws IOException Indicates a failure to read from the reader
*/
private void scanGlyphs(FontFileReader in,
Map glyphs) throws IOException {
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.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()
+ mtxTab[origIndex.intValue()].getOffset()) < 0) {
// origIndex is a composite glyph
allComposites.put(origIndex, glyphs.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(im, newComposites.get(im));
}
}
// Iterate through all composites to remap their composite index
Iterator ce = allComposites.keySet().iterator();
while (ce.hasNext()) {
remapComposite(in, glyphs, (int)entry.getOffset(),
(Integer)ce.next());
}
} else {
throw new IOException("Can't find glyf table");
}
}
/**
* Returns a subset of the original font.
*
* @param in FontFileReader to read from
* @param name Name to be checked for in the font file
* @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
* new index as (Integer) value)
* @return A subset of the original font
* @throws IOException in case of an I/O problem
*/
public byte[] readFont(FontFileReader in, String name,
Map glyphs) throws IOException {
//Check if TrueType collection, and that the name exists in the collection
if (!checkTTC(in, name)) {
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
createHead(in);
createHhea(in, glyphs.size()); // Create the hhea table
createHmtx(in, glyphs); // Create hmtx table
createMaxp(in, glyphs.size()); // copy the maxp table
try {
createCvt(in); // copy the cvt table
} catch (IOException ex) {
// Cvt is optional (only required for OpenType (MS) fonts)
//log.error("TrueType warning: " + ex.getMessage());
}
try {
createFpgm(in); // copy fpgm table
} catch (IOException ex) {
// Fpgm is optional (only required for OpenType (MS) fonts)
//log.error("TrueType warning: " + ex.getMessage());
}
try {
createPrep(in); // copy prep table
} catch (IOException ex) {
// Prep is optional (only required for OpenType (MS) fonts)
//log.error("TrueType warning: " + ex.getMessage());
}
try {
createLoca(glyphs.size()); // create empty loca table
} catch (IOException ex) {
// Loca is optional (only required for OpenType (MS) fonts)
//log.error("TrueType warning: " + ex.getMessage());
}
try {
createGlyf(in, glyphs);
} catch (IOException ex) {
// Glyf is optional (only required for OpenType (MS) fonts)
//log.error("TrueType warning: " + ex.getMessage());
}
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
*
* @param str Write a string at the current position
*/
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 (UnsupportedEncodingException e) {
// This should never happen!
}
return length;
}
/**
* Appends a byte to the output array,
* updates currentPost but not realSize
*
* @param b The byte value to write out
*/
private void writeByte(byte b) {
output[currentPos++] = b;
}
/**
* Appends a USHORT to the output array,
* updates currentPost but not realSize
*
* @param s The short to write
*/
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
*
* @param pos The position to write to
* @param s The short to be written
*/
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
*
* @param s The value to write
*/
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
*
* @param pos The position to write to
* @param s The value to write
*/
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
*
* @param pos The position from the file to read
* @return The short read
*/
private short readShort(int pos) {
int ret = readUShort(pos);
return (short)ret;
}
/**
* Read a unsigned short value at given position
*
* @param pos The position from the file to read
* @return The short read
*/
private int readUShort(int pos) {
int ret = output[pos];
if (ret < 0) {
ret += 256;
}
ret = ret << 8;
if (output[pos + 1] < 0) {
ret |= output[pos + 1] + 256;
} else {
ret |= 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
*
* @param max The value to find the maximum power of 2
* @return The maximum power of 2
*/
private int maxPow2(int max) {
int i = 0;
while (Math.pow(2, i) < max) {
i++;
}
return (i - 1);
}
/**
* Perform a log2
*
* @param num The number to log2
* @return The log2 value
*/
private int log2(int num) {
return (int)(Math.log(num) / Math.log(2));
}
/**
* Get the checksum for this entry
*
* @param start The start of the value
* @param size The size of the value
* @return The calculated check sum
*/
private int getCheckSum(int start, int size) {
return (int)getLongCheckSum(start, size);
}
/**
* Get the checksum as a long
*
* @param start The start value
* @param size The size of the values to checksum
* @return The long checksum
*/
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 = (output[start + i] << 24);
l += (output[start + i + 1] << 16);
l += (output[start + i + 2] << 16);
l += (output[start + i + 3] << 16);
sum += l;
if (sum > 0xffffffff) {
sum = sum - 0xffffffff;
}
}
return sum;
}
/**
* Create the checksum adjustment
*/
private void createCheckSumAdjustment() {
long sum = getLongCheckSum(0, realSize);
int checksum = (int)(0xb1b0afba - sum);
writeULong(checkSumAdjustmentOffset, checksum);
}
}