1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 /* 27 * 28 * 29 * (C) Copyright IBM Corp. 2004-2010 - All Rights Reserved 30 * 31 */ 32 33 #include "KernTable.h" 34 #include "LEFontInstance.h" 35 #include "LEGlyphStorage.h" 36 37 #include "LESwaps.h" 38 #include "OpenTypeUtilities.h" 39 40 #include <stdio.h> 41 42 #define DEBUG 0 43 44 U_NAMESPACE_BEGIN 45 46 struct PairInfo { 47 le_uint32 key; // sigh, MSVC compiler gags on union here 48 le_int16 value; // fword, kern value in funits 49 }; 50 #define KERN_PAIRINFO_SIZE 6 51 LE_CORRECT_SIZE(PairInfo, KERN_PAIRINFO_SIZE) 52 struct Subtable_0 { 53 le_uint16 nPairs; 54 le_uint16 searchRange; 55 le_uint16 entrySelector; 56 le_uint16 rangeShift; 57 }; 58 #define KERN_SUBTABLE_0_HEADER_SIZE 8 59 LE_CORRECT_SIZE(Subtable_0, KERN_SUBTABLE_0_HEADER_SIZE) 60 61 // Kern table version 0 only 62 struct SubtableHeader { 63 le_uint16 version; 64 le_uint16 length; 65 le_uint16 coverage; 66 }; 67 #define KERN_SUBTABLE_HEADER_SIZE 6 68 LE_CORRECT_SIZE(SubtableHeader, KERN_SUBTABLE_HEADER_SIZE) 69 70 // Version 0 only, version 1 has different layout 71 struct KernTableHeader { 72 le_uint16 version; 73 le_uint16 nTables; 74 }; 75 #define KERN_TABLE_HEADER_SIZE 4 76 LE_CORRECT_SIZE(KernTableHeader, KERN_TABLE_HEADER_SIZE) 77 78 #define COVERAGE_HORIZONTAL 0x1 79 #define COVERAGE_MINIMUM 0x2 80 #define COVERAGE_CROSS 0x4 81 #define COVERAGE_OVERRIDE 0x8 82 83 /* 84 * This implementation has support for only one subtable, so if the font has 85 * multiple subtables, only the first will be used. If this turns out to 86 * be a problem in practice we should add it. 87 * 88 * This also supports only version 0 of the kern table header, only 89 * Apple supports the latter. 90 * 91 * This implementation isn't careful about the kern table flags, and 92 * might invoke kerning when it is not supposed to. That too I'm 93 * leaving for a bug fix. 94 * 95 * TODO: support multiple subtables 96 * TODO: respect header flags 97 */ 98 KernTable::KernTable(const LETableReference& base, LEErrorCode &success) 99 : pairs(), pairsSwapped(NULL), fTable(base) 100 { 101 if(LE_FAILURE(success) || (fTable.isEmpty())) { 102 #if DEBUG 103 fprintf(stderr, "no kern data\n"); 104 #endif 105 return; 106 } 107 LEReferenceTo<KernTableHeader> header(fTable, success); 108 109 #if DEBUG 110 // dump first 32 bytes of header 111 for (int i = 0; i < 64; ++i) { 112 fprintf(stderr, "%0.2x ", ((const char*)header.getAlias())[i]&0xff); 113 if (((i+1)&0xf) == 0) { 114 fprintf(stderr, "\n"); 115 } else if (((i+1)&0x7) == 0) { 116 fprintf(stderr, " "); 117 } 118 } 119 #endif 120 121 if(LE_FAILURE(success)) return; 122 123 if (!header.isEmpty() && header->version == 0 && SWAPW(header->nTables) > 0) { 124 LEReferenceTo<SubtableHeader> subhead(header, success, KERN_TABLE_HEADER_SIZE); 125 126 if (LE_SUCCESS(success) && !subhead.isEmpty() && subhead->version == 0) { 127 coverage = SWAPW(subhead->coverage); 128 if (coverage & COVERAGE_HORIZONTAL) { // only handle horizontal kerning 129 LEReferenceTo<Subtable_0> table(subhead, success, KERN_SUBTABLE_HEADER_SIZE); 130 131 if(table.isEmpty() || LE_FAILURE(success)) return; 132 133 nPairs = SWAPW(table->nPairs); 134 135 #if 0 // some old fonts have bad values here... 136 searchRange = SWAPW(table->searchRange); 137 entrySelector = SWAPW(table->entrySelector); 138 rangeShift = SWAPW(table->rangeShift); 139 #else 140 entrySelector = OpenTypeUtilities::highBit(nPairs); 141 searchRange = (1 << entrySelector) * KERN_PAIRINFO_SIZE; 142 rangeShift = (nPairs * KERN_PAIRINFO_SIZE) - searchRange; 143 #endif 144 145 if(LE_SUCCESS(success) && nPairs>0) { 146 // pairs is an instance member, and table is on the stack. 147 // set 'pairs' based on table.getAlias(). This will range check it. 148 149 pairs = LEReferenceToArrayOf<PairInfo>(fTable, // based on overall table 150 success, 151 (const PairInfo*)table.getAlias(), // subtable 0 + .. 152 KERN_SUBTABLE_0_HEADER_SIZE, // .. offset of header size 153 nPairs); // count 154 } 155 if (LE_SUCCESS(success) && pairs.isValid()) { 156 pairsSwapped = (PairInfo*)(malloc(nPairs*sizeof(PairInfo))); 157 PairInfo *p = (PairInfo*)pairsSwapped; 158 for (int i = 0; LE_SUCCESS(success) && i < nPairs; i++, p++) { 159 memcpy(p, pairs.getAlias(i,success), KERN_PAIRINFO_SIZE); 160 p->key = SWAPL(p->key); 161 } 162 fTable.getFont()->setKernPairs((void*)pairsSwapped); // store it 163 } 164 165 #if 0 166 fprintf(stderr, "coverage: %0.4x nPairs: %d pairs %p\n", coverage, nPairs, pairs.getAlias()); 167 fprintf(stderr, " searchRange: %d entrySelector: %d rangeShift: %d\n", searchRange, entrySelector, rangeShift); 168 fprintf(stderr, "[[ ignored font table entries: range %d selector %d shift %d ]]\n", SWAPW(table->searchRange), SWAPW(table->entrySelector), SWAPW(table->rangeShift)); 169 #endif 170 #if DEBUG 171 fprintf(stderr, "coverage: %0.4x nPairs: %d pairs 0x%x\n", coverage, nPairs, pairs); 172 fprintf(stderr, 173 " searchRange(pairs): %d entrySelector: %d rangeShift(pairs): %d\n", 174 searchRange, entrySelector, rangeShift); 175 176 { 177 // dump part of the pair list 178 char ids[256]; 179 for (int i = 256; --i >= 0;) { 180 LEGlyphID id = font->mapCharToGlyph(i); 181 if (id < 256) { 182 ids[id] = (char)i; 183 } 184 } 185 PairInfo *p = pairs; 186 for (int i = 0; i < nPairs; ++i, p++) { 187 le_uint32 k = p->key; 188 le_uint16 left = (k >> 16) & 0xffff; 189 le_uint16 right = k & 0xffff; 190 if (left < 256 && right < 256) { 191 char c = ids[left]; 192 if (c > 0x20 && c < 0x7f) { 193 fprintf(stderr, "%c/", c & 0xff); 194 } else { 195 fprintf(stderr, "%0.2x/", c & 0xff); 196 } 197 c = ids[right]; 198 if (c > 0x20 && c < 0x7f) { 199 fprintf(stderr, "%c ", c & 0xff); 200 } else { 201 fprintf(stderr, "%0.2x ", c & 0xff); 202 } 203 } 204 } 205 } 206 #endif 207 } 208 } 209 } 210 } 211 212 213 /* 214 * Process the glyph positions. The positions array has two floats for each 215 * glyph, plus a trailing pair to mark the end of the last glyph. 216 */ 217 void KernTable::process(LEGlyphStorage& storage, LEErrorCode &success) 218 { 219 if(LE_FAILURE(success)) return; 220 221 if (pairsSwapped) { 222 success = LE_NO_ERROR; 223 224 le_uint32 key = storage[0]; // no need to mask off high bits 225 float adjust = 0; 226 227 for (int i = 1, e = storage.getGlyphCount(); LE_SUCCESS(success)&& i < e; ++i) { 228 key = key << 16 | (storage[i] & 0xffff); 229 230 // argh, to do a binary search, we need to have the pair list in sorted order 231 // but it is not in sorted order on win32 platforms because of the endianness difference 232 // so either I have to swap the element each time I examine it, or I have to swap 233 // all the elements ahead of time and store them in the font 234 235 const PairInfo* p = pairsSwapped; 236 const PairInfo* tp = (const PairInfo*)(p + (rangeShift/KERN_PAIRINFO_SIZE)); /* rangeshift is in original table bytes */ 237 if (key > tp->key) { 238 p = tp; 239 } 240 241 #if DEBUG 242 fprintf(stderr, "binary search for %0.8x\n", key); 243 #endif 244 245 le_uint32 probe = searchRange; 246 while (probe > 1) { 247 probe >>= 1; 248 tp = (const PairInfo*)(p + (probe/KERN_PAIRINFO_SIZE)); 249 le_uint32 tkey = tp->key; 250 #if DEBUG 251 fprintf(stdout, " %.3d (%0.8x)\n", (tp - pairsSwapped), tkey); 252 #endif 253 if (tkey <= key) { 254 if (tkey == key) { 255 le_int16 value = SWAPW(tp->value); 256 #if DEBUG 257 fprintf(stdout, "binary found kerning pair %x:%x at %d, value: 0x%x (%g)\n", 258 storage[i-1], storage[i], i, value & 0xffff, font->xUnitsToPoints(value)); 259 fflush(stdout); 260 #endif 261 // Have to undo the device transform. 262 // REMIND either find a way to do this only if there is a 263 // device transform, or a faster way, such as moving the 264 // entire kern table up to Java. 265 LEPoint pt; 266 pt.fX = fTable.getFont()->xUnitsToPoints(value); 267 pt.fY = 0; 268 269 fTable.getFont()->getKerningAdjustment(pt); 270 adjust += pt.fX; 271 break; 272 } 273 p = tp; 274 } 275 } 276 277 storage.adjustPosition(i, adjust, 0, success); 278 } 279 storage.adjustPosition(storage.getGlyphCount(), adjust, 0, success); 280 } 281 } 282 283 U_NAMESPACE_END 284