1/* 2 * @(#)KernTable.cpp 1.1 04/10/13 3 * 4 * (C) Copyright IBM Corp. 2004-2013 - 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 DEBUG 0 18 19U_NAMESPACE_BEGIN 20 21struct 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 27LE_CORRECT_SIZE(PairInfo, KERN_PAIRINFO_SIZE) 28 29#define SWAP_KEY(p) (((le_uint32) SWAPW((p)->left) << 16) | SWAPW((p)->right)) 30 31struct 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 38LE_CORRECT_SIZE(Subtable_0, KERN_SUBTABLE_0_HEADER_SIZE) 39 40// Kern table version 0 only 41struct SubtableHeader { 42 le_uint16 version; 43 le_uint16 length; 44 le_uint16 coverage; 45}; 46#define KERN_SUBTABLE_HEADER_SIZE 6 47LE_CORRECT_SIZE(SubtableHeader, KERN_SUBTABLE_HEADER_SIZE) 48 49// Version 0 only, version 1 has different layout 50struct KernTableHeader { 51 le_uint16 version; 52 le_uint16 nTables; 53}; 54#define KERN_TABLE_HEADER_SIZE 4 55LE_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 */ 77KernTable::KernTable(const LETableReference& base, LEErrorCode &success) 78 : pairs(), fTable(base) 79{ 80 if(LE_FAILURE(success) || fTable.isEmpty()) { 81#if DEBUG 82 fprintf(stderr, "no kern data\n"); 83#endif 84 return; 85 } 86 LEReferenceTo<KernTableHeader> header(fTable, success); 87 88#if 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 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 191g * glyph, plus a trailing pair to mark the end of the last glyph. 192 */ 193void 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 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 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 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 256U_NAMESPACE_END 257 258