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