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 */
13package com.ibm.icu.dev.tool.layout;
14
15import java.util.Vector;
16
17import com.ibm.icu.impl.Utility;
18
19public class ClassTable implements LookupSubtable
20{
21    static class ClassEntry
22    {
23        private int glyphID;
24        private int classID;
25
26        public ClassEntry(int glyphID, int classID)
27        {
28            this.glyphID = glyphID;
29            this.classID = classID;
30        }
31
32        public int getGlyphID()
33        {
34            return glyphID;
35        }
36
37        public int getClassID()
38        {
39            return classID;
40        }
41
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        //
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
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
102        public ClassRangeRecord(int startGlyphID, int endGlyphID, int classID)
103        {
104            this.startGlyphID = startGlyphID;
105            this.endGlyphID = endGlyphID;
106            this.classID = classID;
107        }
108
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
127    public ClassTable()
128    {
129        this.classMap = new Vector();
130        this.classTable = null;
131        this.snapshotSize = -1;
132
133    }
134
135    public void addMapping(int charID, int classID)
136    {
137        ClassEntry entry = new ClassEntry(charID, classID);
138
139        classMap.addElement(entry);
140    }
141
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
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
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
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
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