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