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