1 /* 2 ******************************************************************************* 3 * Copyright (C) 1998-2010, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * Created on Dec 3, 2003 8 * 9 ******************************************************************************* 10 */ 11 package com.ibm.icu.dev.tool.layout; 12 13 import java.util.Vector; 14 15 import com.ibm.icu.impl.Utility; 16 17 public class ClassTable implements LookupSubtable 18 { 19 static class ClassEntry 20 { 21 private int glyphID; 22 private int classID; 23 ClassEntry(int glyphID, int classID)24 public ClassEntry(int glyphID, int classID) 25 { 26 this.glyphID = glyphID; 27 this.classID = classID; 28 } 29 getGlyphID()30 public int getGlyphID() 31 { 32 return glyphID; 33 } 34 getClassID()35 public int getClassID() 36 { 37 return classID; 38 } 39 compareTo(ClassEntry that)40 public int compareTo(ClassEntry that) 41 { 42 return this.glyphID - that.glyphID; 43 } 44 45 // 46 // Straight insertion sort from Knuth vol. III, pg. 81 47 // sort(ClassEntry[] table, Vector unsorted)48 public static void sort(ClassEntry[] table, Vector unsorted) 49 { 50 for (int e = 0; e < table.length; e += 1) { 51 int i; 52 ClassEntry v = (ClassEntry) unsorted.elementAt(e); 53 54 for (i = e - 1; i >= 0; i -= 1) { 55 if (v.compareTo(table[i]) >= 0) { 56 break; 57 } 58 59 table[i + 1] = table[i]; 60 } 61 62 table[i + 1] = v; 63 } 64 } 65 search(ClassEntry[] table, int glyphID)66 public static int search(ClassEntry[] table, int glyphID) 67 { 68 int log2 = Utility.highBit(table.length); 69 int power = 1 << log2; 70 int extra = table.length - power; 71 int probe = power; 72 int index = 0; 73 74 if (table[extra].glyphID <= glyphID) { 75 index = extra; 76 } 77 78 while (probe > (1 << 0)) { 79 probe >>= 1; 80 81 if (table[index + probe].glyphID <= glyphID) { 82 index += probe; 83 } 84 } 85 86 if (table[index].glyphID == glyphID) { 87 return index; 88 } 89 90 return -1; 91 } 92 } 93 94 static class ClassRangeRecord 95 { 96 private int startGlyphID; 97 private int endGlyphID; 98 private int classID; 99 ClassRangeRecord(int startGlyphID, int endGlyphID, int classID)100 public ClassRangeRecord(int startGlyphID, int endGlyphID, int classID) 101 { 102 this.startGlyphID = startGlyphID; 103 this.endGlyphID = endGlyphID; 104 this.classID = classID; 105 } 106 write(OpenTypeTableWriter writer)107 public void write(OpenTypeTableWriter writer) 108 { 109 System.out.print(Utility.hex(startGlyphID, 6)); 110 System.out.print(" - "); 111 System.out.print(Utility.hex(endGlyphID, 6)); 112 System.out.print(": "); 113 System.out.println(classID); 114 115 writer.writeData(startGlyphID); 116 writer.writeData(endGlyphID); 117 writer.writeData(classID); 118 } 119 } 120 121 private Vector classMap; 122 private ClassEntry[] classTable; 123 private int snapshotSize; 124 ClassTable()125 public ClassTable() 126 { 127 this.classMap = new Vector(); 128 this.classTable = null; 129 this.snapshotSize = -1; 130 131 } 132 addMapping(int charID, int classID)133 public void addMapping(int charID, int classID) 134 { 135 ClassEntry entry = new ClassEntry(charID, classID); 136 137 classMap.addElement(entry); 138 } 139 addMapping(int startCharID, int endCharID, int classID)140 public void addMapping(int startCharID, int endCharID, int classID) 141 { 142 for (int charID = startCharID; charID <= endCharID; charID += 1) { 143 addMapping(charID, classID); 144 } 145 } 146 getGlyphClassID(int glyphID)147 public int getGlyphClassID(int glyphID) 148 { 149 int index = ClassEntry.search(classTable, glyphID); 150 151 if (index >= 0) { 152 return classTable[index].getClassID(); 153 } 154 155 return 0; 156 } 157 snapshot()158 public void snapshot() 159 { 160 if (snapshotSize != classMap.size()) { 161 snapshotSize = classMap.size(); 162 classTable = new ClassEntry[snapshotSize]; 163 164 ClassEntry.sort(classTable, classMap); 165 } 166 } 167 writeClassTable(OpenTypeTableWriter writer)168 public void writeClassTable(OpenTypeTableWriter writer) 169 { 170 snapshot(); 171 172 Vector classRanges = new Vector(); 173 int startIndex = 0; 174 175 while (startIndex < classTable.length) { 176 int startID = classTable[startIndex].getGlyphID(); 177 int classID = classTable[startIndex].getClassID(); 178 int nextID = startID; 179 int endID = startID; 180 int endIndex; 181 182 for (endIndex = startIndex; endIndex < classTable.length; endIndex += 1) { 183 if (classTable[endIndex].getGlyphID() != nextID || 184 classTable[endIndex].getClassID() != classID) { 185 break; 186 } 187 188 endID = nextID; 189 nextID += 1; 190 } 191 192 if (classID != 0) { 193 ClassRangeRecord range = new ClassRangeRecord(startID, endID, classID); 194 195 classRanges.addElement(range); 196 } 197 198 startIndex = endIndex; 199 } 200 201 writer.writeData(2); // table format = 2 (class ranges) 202 writer.writeData(classRanges.size()); // class range count 203 204 for (int i = 0; i < classRanges.size(); i += 1) { 205 ClassRangeRecord range = (ClassRangeRecord) classRanges.elementAt(i); 206 207 range.write(writer); 208 } 209 } 210 writeLookupSubtable(OpenTypeTableWriter writer)211 public void writeLookupSubtable(OpenTypeTableWriter writer) 212 { 213 int singleSubstitutionsBase = writer.getOutputIndex(); 214 int coverageTableIndex; 215 216 snapshot(); 217 218 writer.writeData(2); // format 2: Specified output glyph indices 219 coverageTableIndex = writer.getOutputIndex(); 220 writer.writeData(0); // offset to coverage table (fixed later) 221 writer.writeData(classTable.length); // number of glyphIDs in substitution array 222 223 for (int i = 0; i < classTable.length; i += 1) { 224 writer.writeData(classTable[i].getClassID()); 225 } 226 227 writer.fixOffset(coverageTableIndex, singleSubstitutionsBase); 228 writer.writeData(1); 229 writer.writeData(classTable.length); 230 231 for (int i = 0; i < classTable.length; i += 1) { 232 writer.writeData(classTable[i].getGlyphID()); 233 } 234 } 235 } 236 237