Home | History | Annotate | Download | only in layout
      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 
     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