• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
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