| /* |
| ******************************************************************************* |
| * 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()); |
| } |
| } |
| } |
| |
| |