]>
Commit | Line | Data |
---|---|---|
73c04bcf A |
1 | /* |
2 | * @(#)KernTable.cpp 1.1 04/10/13 | |
3 | * | |
b331163b | 4 | * (C) Copyright IBM Corp. 2004-2014 - All Rights Reserved |
73c04bcf A |
5 | * |
6 | */ | |
7 | ||
8 | #include "KernTable.h" | |
9 | #include "LEFontInstance.h" | |
10 | #include "LEGlyphStorage.h" | |
11 | ||
12 | #include "LESwaps.h" | |
46f4442e | 13 | #include "OpenTypeUtilities.h" |
73c04bcf A |
14 | |
15 | #include <stdio.h> | |
16 | ||
b331163b | 17 | #define KERNTABLE_DEBUG 0 |
73c04bcf A |
18 | |
19 | U_NAMESPACE_BEGIN | |
20 | ||
21 | struct PairInfo { | |
46f4442e A |
22 | le_uint16 left; // left glyph of kern pair |
23 | le_uint16 right; // right glyph of kern pair | |
73c04bcf A |
24 | le_int16 value; // fword, kern value in funits |
25 | }; | |
26 | #define KERN_PAIRINFO_SIZE 6 | |
57a6839d | 27 | LE_CORRECT_SIZE(PairInfo, KERN_PAIRINFO_SIZE) |
73c04bcf | 28 | |
46f4442e A |
29 | #define SWAP_KEY(p) (((le_uint32) SWAPW((p)->left) << 16) | SWAPW((p)->right)) |
30 | ||
73c04bcf A |
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 | |
57a6839d | 38 | LE_CORRECT_SIZE(Subtable_0, KERN_SUBTABLE_0_HEADER_SIZE) |
73c04bcf A |
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 | |
57a6839d | 47 | LE_CORRECT_SIZE(SubtableHeader, KERN_SUBTABLE_HEADER_SIZE) |
73c04bcf A |
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 | |
57a6839d | 55 | LE_CORRECT_SIZE(KernTableHeader, KERN_TABLE_HEADER_SIZE) |
73c04bcf A |
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 | */ | |
57a6839d A |
77 | KernTable::KernTable(const LETableReference& base, LEErrorCode &success) |
78 | : pairs(), fTable(base) | |
73c04bcf | 79 | { |
57a6839d | 80 | if(LE_FAILURE(success) || fTable.isEmpty()) { |
b331163b | 81 | #if KERNTABLE_DEBUG |
73c04bcf A |
82 | fprintf(stderr, "no kern data\n"); |
83 | #endif | |
84 | return; | |
85 | } | |
57a6839d | 86 | LEReferenceTo<KernTableHeader> header(fTable, success); |
73c04bcf | 87 | |
b331163b | 88 | #if KERNTABLE_DEBUG |
73c04bcf A |
89 | // dump first 32 bytes of header |
90 | for (int i = 0; i < 64; ++i) { | |
57a6839d | 91 | fprintf(stderr, "%0.2x ", ((const char*)header.getAlias())[i]&0xff); |
73c04bcf A |
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 | ||
57a6839d | 100 | if(LE_FAILURE(success)) return; |
46f4442e | 101 | |
57a6839d A |
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) { | |
73c04bcf | 106 | coverage = SWAPW(subhead->coverage); |
46f4442e | 107 | |
73c04bcf | 108 | if (coverage & COVERAGE_HORIZONTAL) { // only handle horizontal kerning |
57a6839d A |
109 | LEReferenceTo<Subtable_0> table(subhead, success, KERN_SUBTABLE_HEADER_SIZE); |
110 | ||
111 | if(table.isEmpty() || LE_FAILURE(success)) return; | |
46f4442e A |
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 | ||
57a6839d A |
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. | |
73c04bcf | 128 | |
57a6839d A |
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 | |
b331163b | 141 | #if KERNTABLE_DEBUG |
46f4442e A |
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 | ||
57a6839d | 145 | if(LE_SUCCESS(success) { |
46f4442e A |
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 | ||
57a6839d A |
157 | for (i = 0; i < nPairs; ++i) { |
158 | const PairInfo& p = pairs[i, success]; | |
46f4442e | 159 | |
46f4442e A |
160 | le_uint16 left = p->left; |
161 | le_uint16 right = p->right; | |
162 | ||
57a6839d | 163 | |
46f4442e A |
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 | } | |
73c04bcf A |
182 | #endif |
183 | } | |
184 | } | |
185 | } | |
186 | } | |
57a6839d | 187 | |
73c04bcf A |
188 | |
189 | /* | |
190 | * Process the glyph positions. The positions array has two floats for each | |
57a6839d | 191 | g * glyph, plus a trailing pair to mark the end of the last glyph. |
73c04bcf | 192 | */ |
57a6839d | 193 | void KernTable::process(LEGlyphStorage& storage, LEErrorCode &success) |
73c04bcf | 194 | { |
57a6839d | 195 | if (LE_SUCCESS(success) && !pairs.isEmpty()) { |
73c04bcf A |
196 | |
197 | le_uint32 key = storage[0]; // no need to mask off high bits | |
198 | float adjust = 0; | |
46f4442e | 199 | |
57a6839d | 200 | for (int i = 1, e = storage.getGlyphCount(); LE_SUCCESS(success)&& i < e; ++i) { |
73c04bcf A |
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 | ||
57a6839d A |
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. | |
46f4442e A |
213 | |
214 | if (key > SWAP_KEY(tp)) { | |
215 | p = tp; | |
73c04bcf A |
216 | } |
217 | ||
b331163b | 218 | #if KERNTABLE_DEBUG |
73c04bcf A |
219 | fprintf(stderr, "binary search for %0.8x\n", key); |
220 | #endif | |
221 | ||
222 | le_uint32 probe = searchRange; | |
46f4442e | 223 | |
57a6839d | 224 | while (probe > KERN_PAIRINFO_SIZE && LE_SUCCESS(success)) { |
73c04bcf | 225 | probe >>= 1; |
57a6839d A |
226 | tpRef = LEReferenceTo<PairInfo>(pairs, success, p, probe); // (char*)p + probe |
227 | tp = tpRef.getAlias(); | |
46f4442e | 228 | le_uint32 tkey = SWAP_KEY(tp); |
57a6839d | 229 | if(LE_FAILURE(success)) break; |
b331163b | 230 | #if KERNTABLE_DEBUG |
73c04bcf A |
231 | fprintf(stdout, " %.3d (%0.8x)\n", ((char*)tp - (char*)pairs)/KERN_PAIRINFO_SIZE, tkey); |
232 | #endif | |
57a6839d | 233 | if (tkey <= key && LE_SUCCESS(success)) { |
46f4442e A |
234 | if (tkey == key) { |
235 | le_int16 value = SWAPW(tp->value); | |
b331163b | 236 | #if KERNTABLE_DEBUG |
57a6839d | 237 | fprintf(stdout, "binary found kerning pair %x:%x at %d, value: 0x%x (%g)\n", |
46f4442e A |
238 | storage[i-1], storage[i], i, value & 0xffff, font->xUnitsToPoints(value)); |
239 | fflush(stdout); | |
73c04bcf | 240 | #endif |
57a6839d | 241 | adjust += fTable.getFont()->xUnitsToPoints(value); |
46f4442e A |
242 | break; |
243 | } | |
244 | ||
245 | p = tp; | |
73c04bcf A |
246 | } |
247 | } | |
248 | ||
249 | storage.adjustPosition(i, adjust, 0, success); | |
250 | } | |
46f4442e | 251 | |
73c04bcf A |
252 | storage.adjustPosition(storage.getGlyphCount(), adjust, 0, success); |
253 | } | |
254 | } | |
255 | ||
256 | U_NAMESPACE_END | |
257 |