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