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