1 /* 2 * @(#)KernTable.cpp 1.1 04/10/13 3 * 4 * (C) Copyright IBM Corp. 2004-2014 - All Rights Reserved 5 * 6 */ 7 8 #include "KernTable.h" 9 #include "LEFontInstance.h" 10 #include "LEGlyphStorage.h" 11 12 #include "LESwaps.h" 13 #include "OpenTypeUtilities.h" 14 15 #include <stdio.h> 16 17 #define KERNTABLE_DEBUG 0 18 19 U_NAMESPACE_BEGIN 20 21 struct PairInfo { 22 le_uint16 left; // left glyph of kern pair 23 le_uint16 right; // right glyph of kern pair 24 le_int16 value; // fword, kern value in funits 25 }; 26 #define KERN_PAIRINFO_SIZE 6 27 LE_CORRECT_SIZE(PairInfo, KERN_PAIRINFO_SIZE) 28 29 #define SWAP_KEY(p) (((le_uint32) SWAPW((p)->left) << 16) | SWAPW((p)->right)) 30 31 struct Subtable_0 { 32 le_uint16 nPairs; 33 le_uint16 searchRange; 34 le_uint16 entrySelector; 35 le_uint16 rangeShift; 36 }; 37 #define KERN_SUBTABLE_0_HEADER_SIZE 8 38 LE_CORRECT_SIZE(Subtable_0, KERN_SUBTABLE_0_HEADER_SIZE) 39 40 // Kern table version 0 only 41 struct SubtableHeader { 42 le_uint16 version; 43 le_uint16 length; 44 le_uint16 coverage; 45 }; 46 #define KERN_SUBTABLE_HEADER_SIZE 6 47 LE_CORRECT_SIZE(SubtableHeader, KERN_SUBTABLE_HEADER_SIZE) 48 49 // Version 0 only, version 1 has different layout 50 struct KernTableHeader { 51 le_uint16 version; 52 le_uint16 nTables; 53 }; 54 #define KERN_TABLE_HEADER_SIZE 4 55 LE_CORRECT_SIZE(KernTableHeader, KERN_TABLE_HEADER_SIZE) 56 57 #define COVERAGE_HORIZONTAL 0x1 58 #define COVERAGE_MINIMUM 0x2 59 #define COVERAGE_CROSS 0x4 60 #define COVERAGE_OVERRIDE 0x8 61 62 /* 63 * This implementation has support for only one subtable, so if the font has 64 * multiple subtables, only the first will be used. If this turns out to 65 * be a problem in practice we should add it. 66 * 67 * This also supports only version 0 of the kern table header, only 68 * Apple supports the latter. 69 * 70 * This implementation isn't careful about the kern table flags, and 71 * might invoke kerning when it is not supposed to. That too I'm 72 * leaving for a bug fix. 73 * 74 * TODO: support multiple subtables 75 * TODO: respect header flags 76 */ 77 KernTable::KernTable(const LETableReference& base, LEErrorCode &success) 78 : pairs(), fTable(base) 79 { 80 if(LE_FAILURE(success) || fTable.isEmpty()) { 81 #if KERNTABLE_DEBUG 82 fprintf(stderr, "no kern data\n"); 83 #endif 84 return; 85 } 86 LEReferenceTo<KernTableHeader> header(fTable, success); 87 88 #if KERNTABLE_DEBUG 89 // dump first 32 bytes of header 90 for (int i = 0; i < 64; ++i) { 91 fprintf(stderr, "%0.2x ", ((const char*)header.getAlias())[i]&0xff); 92 if (((i+1)&0xf) == 0) { 93 fprintf(stderr, "\n"); 94 } else if (((i+1)&0x7) == 0) { 95 fprintf(stderr, " "); 96 } 97 } 98 #endif 99 100 if(LE_FAILURE(success)) return; 101 102 if (!header.isEmpty() && header->version == 0 && SWAPW(header->nTables) > 0) { 103 LEReferenceTo<SubtableHeader> subhead(header, success, KERN_TABLE_HEADER_SIZE); 104 105 if (LE_SUCCESS(success) && !subhead.isEmpty() && subhead->version == 0) { 106 coverage = SWAPW(subhead->coverage); 107 108 if (coverage & COVERAGE_HORIZONTAL) { // only handle horizontal kerning 109 LEReferenceTo<Subtable_0> table(subhead, success, KERN_SUBTABLE_HEADER_SIZE); 110 111 if(table.isEmpty() || LE_FAILURE(success)) return; 112 113 nPairs = SWAPW(table->nPairs); 114 115 #if 0 // some old fonts have bad values here... 116 searchRange = SWAPW(table->searchRange); 117 entrySelector = SWAPW(table->entrySelector); 118 rangeShift = SWAPW(table->rangeShift); 119 #else 120 entrySelector = OpenTypeUtilities::highBit(nPairs); 121 searchRange = (1 << entrySelector) * KERN_PAIRINFO_SIZE; 122 rangeShift = (nPairs * KERN_PAIRINFO_SIZE) - searchRange; 123 #endif 124 125 if(LE_SUCCESS(success) && nPairs>0) { 126 // pairs is an instance member, and table is on the stack. 127 // set 'pairs' based on table.getAlias(). This will range check it. 128 129 pairs = LEReferenceToArrayOf<PairInfo>(fTable, // based on overall table 130 success, 131 (const PairInfo*)table.getAlias(), // subtable 0 + .. 132 KERN_SUBTABLE_0_HEADER_SIZE, // .. offset of header size 133 nPairs); // count 134 } 135 136 #if 0 137 fprintf(stderr, "coverage: %0.4x nPairs: %d pairs %p\n", coverage, nPairs, pairs.getAlias()); 138 fprintf(stderr, " searchRange: %d entrySelector: %d rangeShift: %d\n", searchRange, entrySelector, rangeShift); 139 fprintf(stderr, "[[ ignored font table entries: range %d selector %d shift %d ]]\n", SWAPW(table->searchRange), SWAPW(table->entrySelector), SWAPW(table->rangeShift)); 140 #endif 141 #if KERNTABLE_DEBUG 142 fprintf(stderr, "coverage: %0.4x nPairs: %d pairs 0x%x\n", coverage, nPairs, pairs); 143 fprintf(stderr, " searchRange: %d entrySelector: %d rangeShift: %d\n", searchRange, entrySelector, rangeShift); 144 145 if(LE_SUCCESS(success) { 146 // dump part of the pair list 147 char ids[256]; 148 149 for (int i = 256; --i >= 0;) { 150 LEGlyphID id = font->mapCharToGlyph(i); 151 152 if (id < 256) { 153 ids[id] = (char)i; 154 } 155 } 156 157 for (i = 0; i < nPairs; ++i) { 158 const PairInfo& p = pairs[i, success]; 159 160 le_uint16 left = p->left; 161 le_uint16 right = p->right; 162 163 164 if (left < 256 && right < 256) { 165 char c = ids[left]; 166 167 if (c > 0x20 && c < 0x7f) { 168 fprintf(stderr, "%c/", c & 0xff); 169 } else { 170 printf(stderr, "%0.2x/", c & 0xff); 171 } 172 173 c = ids[right]; 174 if (c > 0x20 && c < 0x7f) { 175 fprintf(stderr, "%c ", c & 0xff); 176 } else { 177 fprintf(stderr, "%0.2x ", c & 0xff); 178 } 179 } 180 } 181 } 182 #endif 183 } 184 } 185 } 186 } 187 188 189 /* 190 * Process the glyph positions. The positions array has two floats for each 191 g * glyph, plus a trailing pair to mark the end of the last glyph. 192 */ 193 void KernTable::process(LEGlyphStorage& storage, LEErrorCode &success) 194 { 195 if (LE_SUCCESS(success) && !pairs.isEmpty()) { 196 197 le_uint32 key = storage[0]; // no need to mask off high bits 198 float adjust = 0; 199 200 for (int i = 1, e = storage.getGlyphCount(); LE_SUCCESS(success)&& i < e; ++i) { 201 key = key << 16 | (storage[i] & 0xffff); 202 203 // argh, to do a binary search, we need to have the pair list in sorted order 204 // but it is not in sorted order on win32 platforms because of the endianness difference 205 // so either I have to swap the element each time I examine it, or I have to swap 206 // all the elements ahead of time and store them in the font 207 208 const PairInfo *p = pairs.getAlias(0, success); 209 210 LEReferenceTo<PairInfo> tpRef(pairs, success, rangeShift); // ((char*)pairs) + rangeShift 211 const PairInfo *tp = tpRef.getAlias(); 212 if(LE_FAILURE(success)) return; // get out. 213 214 if (key > SWAP_KEY(tp)) { 215 p = tp; 216 } 217 218 #if KERNTABLE_DEBUG 219 fprintf(stderr, "binary search for %0.8x\n", key); 220 #endif 221 222 le_uint32 probe = searchRange; 223 224 while (probe > KERN_PAIRINFO_SIZE && LE_SUCCESS(success)) { 225 probe >>= 1; 226 tpRef = LEReferenceTo<PairInfo>(pairs, success, p, probe); // (char*)p + probe 227 tp = tpRef.getAlias(); 228 le_uint32 tkey = SWAP_KEY(tp); 229 if(LE_FAILURE(success)) break; 230 #if KERNTABLE_DEBUG 231 fprintf(stdout, " %.3d (%0.8x)\n", ((char*)tp - (char*)pairs)/KERN_PAIRINFO_SIZE, tkey); 232 #endif 233 if (tkey <= key && LE_SUCCESS(success)) { 234 if (tkey == key) { 235 le_int16 value = SWAPW(tp->value); 236 #if KERNTABLE_DEBUG 237 fprintf(stdout, "binary found kerning pair %x:%x at %d, value: 0x%x (%g)\n", 238 storage[i-1], storage[i], i, value & 0xffff, font->xUnitsToPoints(value)); 239 fflush(stdout); 240 #endif 241 adjust += fTable.getFont()->xUnitsToPoints(value); 242 break; 243 } 244 245 p = tp; 246 } 247 } 248 249 storage.adjustPosition(i, adjust, 0, success); 250 } 251 252 storage.adjustPosition(storage.getGlyphCount(), adjust, 0, success); 253 } 254 } 255 256 U_NAMESPACE_END 257 258