/* ******************************************************************************* * Copyright (C) 1998-2010, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * Created on Dec 3, 2003 * ******************************************************************************* */ package com.ibm.icu.dev.tool.layout; import java.util.Vector; import com.ibm.icu.impl.Utility; public class ClassTable implements LookupSubtable { static class ClassEntry { private int glyphID; private int classID; public ClassEntry(int glyphID, int classID) { this.glyphID = glyphID; this.classID = classID; } public int getGlyphID() { return glyphID; } public int getClassID() { return classID; } public int compareTo(ClassEntry that) { return this.glyphID - that.glyphID; } // // Straight insertion sort from Knuth vol. III, pg. 81 // public static void sort(ClassEntry[] table, Vector unsorted) { for (int e = 0; e < table.length; e += 1) { int i; ClassEntry v = (ClassEntry) unsorted.elementAt(e); for (i = e - 1; i >= 0; i -= 1) { if (v.compareTo(table[i]) >= 0) { break; } table[i + 1] = table[i]; } table[i + 1] = v; } } public static int search(ClassEntry[] table, int glyphID) { int log2 = Utility.highBit(table.length); int power = 1 << log2; int extra = table.length - power; int probe = power; int index = 0; if (table[extra].glyphID <= glyphID) { index = extra; } while (probe > (1 << 0)) { probe >>= 1; if (table[index + probe].glyphID <= glyphID) { index += probe; } } if (table[index].glyphID == glyphID) { return index; } return -1; } } static class ClassRangeRecord { private int startGlyphID; private int endGlyphID; private int classID; public ClassRangeRecord(int startGlyphID, int endGlyphID, int classID) { this.startGlyphID = startGlyphID; this.endGlyphID = endGlyphID; this.classID = classID; } public void write(OpenTypeTableWriter writer) { System.out.print(Utility.hex(startGlyphID, 6)); System.out.print(" - "); System.out.print(Utility.hex(endGlyphID, 6)); System.out.print(": "); System.out.println(classID); writer.writeData(startGlyphID); writer.writeData(endGlyphID); writer.writeData(classID); } } private Vector classMap; private ClassEntry[] classTable; private int snapshotSize; public ClassTable() { this.classMap = new Vector(); this.classTable = null; this.snapshotSize = -1; } public void addMapping(int charID, int classID) { ClassEntry entry = new ClassEntry(charID, classID); classMap.addElement(entry); } public void addMapping(int startCharID, int endCharID, int classID) { for (int charID = startCharID; charID <= endCharID; charID += 1) { addMapping(charID, classID); } } public int getGlyphClassID(int glyphID) { int index = ClassEntry.search(classTable, glyphID); if (index >= 0) { return classTable[index].getClassID(); } return 0; } public void snapshot() { if (snapshotSize != classMap.size()) { snapshotSize = classMap.size(); classTable = new ClassEntry[snapshotSize]; ClassEntry.sort(classTable, classMap); } } public void writeClassTable(OpenTypeTableWriter writer) { snapshot(); Vector classRanges = new Vector(); int startIndex = 0; while (startIndex < classTable.length) { int startID = classTable[startIndex].getGlyphID(); int classID = classTable[startIndex].getClassID(); int nextID = startID; int endID = startID; int endIndex; for (endIndex = startIndex; endIndex < classTable.length; endIndex += 1) { if (classTable[endIndex].getGlyphID() != nextID || classTable[endIndex].getClassID() != classID) { break; } endID = nextID; nextID += 1; } if (classID != 0) { ClassRangeRecord range = new ClassRangeRecord(startID, endID, classID); classRanges.addElement(range); } startIndex = endIndex; } writer.writeData(2); // table format = 2 (class ranges) writer.writeData(classRanges.size()); // class range count for (int i = 0; i < classRanges.size(); i += 1) { ClassRangeRecord range = (ClassRangeRecord) classRanges.elementAt(i); range.write(writer); } } public void writeLookupSubtable(OpenTypeTableWriter writer) { int singleSubstitutionsBase = writer.getOutputIndex(); int coverageTableIndex; snapshot(); writer.writeData(2); // format 2: Specified output glyph indices coverageTableIndex = writer.getOutputIndex(); writer.writeData(0); // offset to coverage table (fixed later) writer.writeData(classTable.length); // number of glyphIDs in substitution array for (int i = 0; i < classTable.length; i += 1) { writer.writeData(classTable[i].getClassID()); } writer.fixOffset(coverageTableIndex, singleSubstitutionsBase); writer.writeData(1); writer.writeData(classTable.length); for (int i = 0; i < classTable.length; i += 1) { writer.writeData(classTable[i].getGlyphID()); } } }