2 * @(#)KernTable.cpp 1.1 04/10/13
4 * (C) Copyright IBM Corp. 2004-2014 - All Rights Reserved
9 #include "LEFontInstance.h"
10 #include "LEGlyphStorage.h"
13 #include "OpenTypeUtilities.h"
17 #define KERNTABLE_DEBUG 0
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
26 #define KERN_PAIRINFO_SIZE 6
27 LE_CORRECT_SIZE(PairInfo
, KERN_PAIRINFO_SIZE
)
29 #define SWAP_KEY(p) (((le_uint32) SWAPW((p)->left) << 16) | SWAPW((p)->right))
33 le_uint16 searchRange
;
34 le_uint16 entrySelector
;
37 #define KERN_SUBTABLE_0_HEADER_SIZE 8
38 LE_CORRECT_SIZE(Subtable_0
, KERN_SUBTABLE_0_HEADER_SIZE
)
40 // Kern table version 0 only
41 struct SubtableHeader
{
46 #define KERN_SUBTABLE_HEADER_SIZE 6
47 LE_CORRECT_SIZE(SubtableHeader
, KERN_SUBTABLE_HEADER_SIZE
)
49 // Version 0 only, version 1 has different layout
50 struct KernTableHeader
{
54 #define KERN_TABLE_HEADER_SIZE 4
55 LE_CORRECT_SIZE(KernTableHeader
, KERN_TABLE_HEADER_SIZE
)
57 #define COVERAGE_HORIZONTAL 0x1
58 #define COVERAGE_MINIMUM 0x2
59 #define COVERAGE_CROSS 0x4
60 #define COVERAGE_OVERRIDE 0x8
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.
67 * This also supports only version 0 of the kern table header, only
68 * Apple supports the latter.
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.
74 * TODO: support multiple subtables
75 * TODO: respect header flags
77 KernTable::KernTable(const LETableReference
& base
, LEErrorCode
&success
)
78 : pairs(), fTable(base
)
80 if(LE_FAILURE(success
) || fTable
.isEmpty()) {
82 fprintf(stderr
, "no kern data\n");
86 LEReferenceTo
<KernTableHeader
> header(fTable
, success
);
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) {
100 if(LE_FAILURE(success
)) return;
102 if (!header
.isEmpty() && header
->version
== 0 && SWAPW(header
->nTables
) > 0) {
103 LEReferenceTo
<SubtableHeader
> subhead(header
, success
, KERN_TABLE_HEADER_SIZE
);
105 if (LE_SUCCESS(success
) && !subhead
.isEmpty() && subhead
->version
== 0) {
106 coverage
= SWAPW(subhead
->coverage
);
108 if (coverage
& COVERAGE_HORIZONTAL
) { // only handle horizontal kerning
109 LEReferenceTo
<Subtable_0
> table(subhead
, success
, KERN_SUBTABLE_HEADER_SIZE
);
111 if(table
.isEmpty() || LE_FAILURE(success
)) return;
113 nPairs
= SWAPW(table
->nPairs
);
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
);
120 entrySelector
= OpenTypeUtilities::highBit(nPairs
);
121 searchRange
= (1 << entrySelector
) * KERN_PAIRINFO_SIZE
;
122 rangeShift
= (nPairs
* KERN_PAIRINFO_SIZE
) - searchRange
;
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.
129 pairs
= LEReferenceToArrayOf
<PairInfo
>(fTable
, // based on overall table
131 (const PairInfo
*)table
.getAlias(), // subtable 0 + ..
132 KERN_SUBTABLE_0_HEADER_SIZE
, // .. offset of header size
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
));
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
);
145 if(LE_SUCCESS(success
) {
146 // dump part of the pair list
149 for (int i
= 256; --i
>= 0;) {
150 LEGlyphID id
= font
->mapCharToGlyph(i
);
157 for (i
= 0; i
< nPairs
; ++i
) {
158 const PairInfo
& p
= pairs
[i
, success
];
160 le_uint16 left
= p
->left
;
161 le_uint16 right
= p
->right
;
164 if (left
< 256 && right
< 256) {
167 if (c
> 0x20 && c
< 0x7f) {
168 fprintf(stderr
, "%c/", c
& 0xff);
170 printf(stderr
, "%0.2x/", c
& 0xff);
174 if (c
> 0x20 && c
< 0x7f) {
175 fprintf(stderr
, "%c ", c
& 0xff);
177 fprintf(stderr
, "%0.2x ", c
& 0xff);
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.
193 void KernTable::process(LEGlyphStorage
& storage
, LEErrorCode
&success
)
195 if (LE_SUCCESS(success
) && !pairs
.isEmpty()) {
197 le_uint32 key
= storage
[0]; // no need to mask off high bits
200 for (int i
= 1, e
= storage
.getGlyphCount(); LE_SUCCESS(success
)&& i
< e
; ++i
) {
201 key
= key
<< 16 | (storage
[i
] & 0xffff);
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
208 const PairInfo
*p
= pairs
.getAlias(0, success
);
210 LEReferenceTo
<PairInfo
> tpRef(pairs
, success
, rangeShift
); // ((char*)pairs) + rangeShift
211 const PairInfo
*tp
= tpRef
.getAlias();
212 if(LE_FAILURE(success
)) return; // get out.
214 if (key
> SWAP_KEY(tp
)) {
219 fprintf(stderr
, "binary search for %0.8x\n", key
);
222 le_uint32 probe
= searchRange
;
224 while (probe
> KERN_PAIRINFO_SIZE
&& LE_SUCCESS(success
)) {
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;
231 fprintf(stdout
, " %.3d (%0.8x)\n", ((char*)tp
- (char*)pairs
)/KERN_PAIRINFO_SIZE
, tkey
);
233 if (tkey
<= key
&& LE_SUCCESS(success
)) {
235 le_int16 value
= SWAPW(tp
->value
);
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
));
241 adjust
+= fTable
.getFont()->xUnitsToPoints(value
);
249 storage
.adjustPosition(i
, adjust
, 0, success
);
252 storage
.adjustPosition(storage
.getGlyphCount(), adjust
, 0, success
);