1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package sfnt 6 7import ( 8 "sort" 9) 10 11const ( 12 hexScriptLatn = uint32(0x6c61746e) // latn 13 hexScriptDFLT = uint32(0x44464c54) // DFLT 14 hexFeatureKern = uint32(0x6b65726e) // kern 15) 16 17//kernFunc returns the unscaled kerning value for kerning pair a+b. 18// Returns ErrNotFound if no kerning is specified for this pair. 19type kernFunc func(a, b GlyphIndex) (int16, error) 20 21func (f *Font) parseGPOSKern(buf []byte) ([]byte, []kernFunc, error) { 22 // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos 23 24 if f.gpos.length == 0 { 25 return buf, nil, nil 26 } 27 const headerSize = 10 // GPOS header v1.1 is 14 bytes, but we don't support FeatureVariations 28 if f.gpos.length < headerSize { 29 return buf, nil, errInvalidGPOSTable 30 } 31 32 buf, err := f.src.view(buf, int(f.gpos.offset), headerSize) 33 if err != nil { 34 return buf, nil, err 35 } 36 37 // check for version 1.0/1.1 38 if u16(buf) != 1 || u16(buf[2:]) > 1 { 39 return buf, nil, errUnsupportedGPOSTable 40 } 41 scriptListOffset := u16(buf[4:]) 42 featureListOffset := u16(buf[6:]) 43 lookupListOffset := u16(buf[8:]) 44 45 // get all feature indices for latn script 46 buf, featureIdxs, err := f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptLatn) 47 if err != nil { 48 return buf, nil, err 49 } 50 if len(featureIdxs) == 0 { 51 // get all feature indices for DFLT script 52 buf, featureIdxs, err = f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptDFLT) 53 if err != nil { 54 return buf, nil, err 55 } 56 if len(featureIdxs) == 0 { 57 return buf, nil, nil 58 } 59 } 60 61 // get all lookup indices for kern features 62 buf, lookupIdx, err := f.parseGPOSFeaturesLookup(buf, int(f.gpos.offset)+int(featureListOffset), featureIdxs, hexFeatureKern) 63 64 // LookupTableList: lookupCount,[]lookups 65 buf, numLookupTables, err := f.src.varLenView(buf, int(f.gpos.offset)+int(lookupListOffset), 2, 0, 2) 66 if err != nil { 67 return buf, nil, err 68 } 69 70 var kernFuncs []kernFunc 71 72lookupTables: 73 for _, n := range lookupIdx { 74 if n > numLookupTables { 75 return buf, nil, errInvalidGPOSTable 76 } 77 tableOffset := int(f.gpos.offset) + int(lookupListOffset) + int(u16(buf[2+n*2:])) 78 79 // LookupTable: lookupType, lookupFlag, subTableCount, []subtableOffsets, markFilteringSet 80 buf, numSubTables, err := f.src.varLenView(buf, tableOffset, 8, 4, 2) 81 if err != nil { 82 return buf, nil, err 83 } 84 85 flags := u16(buf[2:]) 86 87 subTableOffsets := make([]int, numSubTables) 88 for i := 0; i < int(numSubTables); i++ { 89 subTableOffsets[i] = int(tableOffset) + int(u16(buf[6+i*2:])) 90 } 91 92 switch lookupType := u16(buf); lookupType { 93 case 2: // PairPos table 94 case 9: 95 // Extension Positioning table defines an additional u32 offset 96 // to allow subtables to exceed the 16-bit limit. 97 for i := range subTableOffsets { 98 buf, err = f.src.view(buf, subTableOffsets[i], 8) 99 if err != nil { 100 return buf, nil, err 101 } 102 if format := u16(buf); format != 1 { 103 return buf, nil, errUnsupportedExtensionPosFormat 104 } 105 if lookupType := u16(buf[2:]); lookupType != 2 { 106 continue lookupTables 107 } 108 subTableOffsets[i] += int(u32(buf[4:])) 109 } 110 default: // other types are not supported 111 continue 112 } 113 114 if flags&0x0010 > 0 { 115 // useMarkFilteringSet enabled, skip as it is not supported 116 continue 117 } 118 119 for _, subTableOffset := range subTableOffsets { 120 buf, err = f.src.view(buf, int(subTableOffset), 4) 121 if err != nil { 122 return buf, nil, err 123 } 124 format := u16(buf) 125 126 var lookupIndex indexLookupFunc 127 buf, lookupIndex, err = f.makeCachedCoverageLookup(buf, subTableOffset+int(u16(buf[2:]))) 128 if err != nil { 129 return buf, nil, err 130 } 131 132 switch format { 133 case 1: // Adjustments for Glyph Pairs 134 buf, kern, err := f.parsePairPosFormat1(buf, subTableOffset, lookupIndex) 135 if err != nil { 136 return buf, nil, err 137 } 138 if kern != nil { 139 kernFuncs = append(kernFuncs, kern) 140 } 141 case 2: // Class Pair Adjustment 142 buf, kern, err := f.parsePairPosFormat2(buf, subTableOffset, lookupIndex) 143 if err != nil { 144 return buf, nil, err 145 } 146 if kern != nil { 147 kernFuncs = append(kernFuncs, kern) 148 } 149 } 150 } 151 } 152 153 return buf, kernFuncs, nil 154} 155 156func (f *Font) parsePairPosFormat1(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) { 157 // PairPos Format 1: posFormat, coverageOffset, valueFormat1, 158 // valueFormat2, pairSetCount, []pairSetOffsets 159 var err error 160 var nPairs int 161 buf, nPairs, err = f.src.varLenView(buf, offset, 10, 8, 2) 162 if err != nil { 163 return buf, nil, err 164 } 165 // check valueFormat1 and valueFormat2 flags 166 if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 { 167 // we only support kerning with X_ADVANCE for first glyph 168 return buf, nil, nil 169 } 170 171 // PairPos table contains an array of offsets to PairSet 172 // tables, which contains an array of PairValueRecords. 173 // Calculate length of complete PairPos table by jumping to 174 // last PairSet. 175 // We need to iterate all offsets to find the last pair as 176 // offsets are not sorted and can be repeated. 177 var lastPairSetOffset int 178 for n := 0; n < nPairs; n++ { 179 pairOffset := int(u16(buf[10+n*2:])) 180 if pairOffset > lastPairSetOffset { 181 lastPairSetOffset = pairOffset 182 } 183 } 184 buf, err = f.src.view(buf, offset+lastPairSetOffset, 2) 185 if err != nil { 186 return buf, nil, err 187 } 188 189 pairValueCount := int(u16(buf)) 190 // Each PairSet contains the secondGlyph (u16) and one or more value records (all u16). 191 // We only support lookup tables with one value record (X_ADVANCE, see valueFormat1/2 above). 192 lastPairSetLength := 2 + pairValueCount*4 193 194 length := lastPairSetOffset + lastPairSetLength 195 buf, err = f.src.view(buf, offset, length) 196 if err != nil { 197 return buf, nil, err 198 } 199 200 kern := makeCachedPairPosGlyph(lookupIndex, nPairs, buf) 201 return buf, kern, nil 202} 203 204func (f *Font) parsePairPosFormat2(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) { 205 // PairPos Format 2: 206 // posFormat, coverageOffset, valueFormat1, valueFormat2, 207 // classDef1Offset, classDef2Offset, class1Count, class2Count, 208 // []class1Records 209 var err error 210 buf, err = f.src.view(buf, offset, 16) 211 if err != nil { 212 return buf, nil, err 213 } 214 // check valueFormat1 and valueFormat2 flags 215 if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 { 216 // we only support kerning with X_ADVANCE for first glyph 217 return buf, nil, nil 218 } 219 numClass1 := int(u16(buf[12:])) 220 numClass2 := int(u16(buf[14:])) 221 cdef1Offset := offset + int(u16(buf[8:])) 222 cdef2Offset := offset + int(u16(buf[10:])) 223 var cdef1, cdef2 classLookupFunc 224 buf, cdef1, err = f.makeCachedClassLookup(buf, cdef1Offset) 225 if err != nil { 226 return buf, nil, err 227 } 228 buf, cdef2, err = f.makeCachedClassLookup(buf, cdef2Offset) 229 if err != nil { 230 return buf, nil, err 231 } 232 233 buf, err = f.src.view(buf, offset+16, numClass1*numClass2*2) 234 if err != nil { 235 return buf, nil, err 236 } 237 kern := makeCachedPairPosClass( 238 lookupIndex, 239 numClass1, 240 numClass2, 241 cdef1, 242 cdef2, 243 buf, 244 ) 245 246 return buf, kern, nil 247} 248 249// parseGPOSScriptFeatures returns all indices of features in FeatureTable that 250// are valid for the given script. 251// Returns features from DefaultLangSys, different languages are not supported. 252// However, all observed fonts either do not use different languages or use the 253// same features as DefaultLangSys. 254func (f *Font) parseGPOSScriptFeatures(buf []byte, offset int, script uint32) ([]byte, []int, error) { 255 // ScriptList table: scriptCount, []scriptRecords{scriptTag, scriptOffset} 256 buf, numScriptTables, err := f.src.varLenView(buf, offset, 2, 0, 6) 257 if err != nil { 258 return buf, nil, err 259 } 260 261 // Search ScriptTables for script 262 var scriptTableOffset uint16 263 for i := 0; i < numScriptTables; i++ { 264 scriptTag := u32(buf[2+i*6:]) 265 if scriptTag == script { 266 scriptTableOffset = u16(buf[2+i*6+4:]) 267 break 268 } 269 } 270 if scriptTableOffset == 0 { 271 return buf, nil, nil 272 } 273 274 // Script table: defaultLangSys, langSysCount, []langSysRecords{langSysTag, langSysOffset} 275 buf, err = f.src.view(buf, offset+int(scriptTableOffset), 2) 276 if err != nil { 277 return buf, nil, err 278 } 279 defaultLangSysOffset := u16(buf) 280 281 if defaultLangSysOffset == 0 { 282 return buf, nil, nil 283 } 284 285 // LangSys table: lookupOrder (reserved), requiredFeatureIndex, featureIndexCount, []featureIndices 286 buf, numFeatures, err := f.src.varLenView(buf, offset+int(scriptTableOffset)+int(defaultLangSysOffset), 6, 4, 2) 287 288 featureIdxs := make([]int, numFeatures) 289 for i := range featureIdxs { 290 featureIdxs[i] = int(u16(buf[6+i*2:])) 291 } 292 return buf, featureIdxs, nil 293} 294 295func (f *Font) parseGPOSFeaturesLookup(buf []byte, offset int, featureIdxs []int, feature uint32) ([]byte, []int, error) { 296 // FeatureList table: featureCount, []featureRecords{featureTag, featureOffset} 297 buf, numFeatureTables, err := f.src.varLenView(buf, offset, 2, 0, 6) 298 if err != nil { 299 return buf, nil, err 300 } 301 302 lookupIdx := make([]int, 0, 4) 303 304 for _, fidx := range featureIdxs { 305 if fidx > numFeatureTables { 306 return buf, nil, errInvalidGPOSTable 307 } 308 featureTag := u32(buf[2+fidx*6:]) 309 if featureTag != feature { 310 continue 311 } 312 featureOffset := u16(buf[2+fidx*6+4:]) 313 314 buf, numLookups, err := f.src.varLenView(nil, offset+int(featureOffset), 4, 2, 2) 315 if err != nil { 316 return buf, nil, err 317 } 318 319 for i := 0; i < numLookups; i++ { 320 lookupIdx = append(lookupIdx, int(u16(buf[4+i*2:]))) 321 } 322 } 323 324 return buf, lookupIdx, nil 325} 326 327func makeCachedPairPosGlyph(cov indexLookupFunc, num int, buf []byte) kernFunc { 328 glyphs := make([]byte, len(buf)) 329 copy(glyphs, buf) 330 return func(a, b GlyphIndex) (int16, error) { 331 idx, found := cov(a) 332 if !found { 333 return 0, ErrNotFound 334 } 335 if idx >= num { 336 return 0, ErrNotFound 337 } 338 offset := int(u16(glyphs[10+idx*2:])) 339 if offset+1 >= len(glyphs) { 340 return 0, errInvalidGPOSTable 341 } 342 343 count := int(u16(glyphs[offset:])) 344 for i := 0; i < count; i++ { 345 secondGlyphIndex := GlyphIndex(int(u16(glyphs[offset+2+i*4:]))) 346 if secondGlyphIndex == b { 347 return int16(u16(glyphs[offset+2+i*4+2:])), nil 348 } 349 if secondGlyphIndex > b { 350 return 0, ErrNotFound 351 } 352 } 353 354 return 0, ErrNotFound 355 } 356} 357 358func makeCachedPairPosClass(cov indexLookupFunc, num1, num2 int, cdef1, cdef2 classLookupFunc, buf []byte) kernFunc { 359 glyphs := make([]byte, len(buf)) 360 copy(glyphs, buf) 361 return func(a, b GlyphIndex) (int16, error) { 362 // check coverage to avoid selection of default class 0 363 _, found := cov(a) 364 if !found { 365 return 0, ErrNotFound 366 } 367 idxa := cdef1(a) 368 idxb := cdef2(b) 369 return int16(u16(glyphs[(idxb+idxa*num2)*2:])), nil 370 } 371} 372 373// indexLookupFunc returns the index into a PairPos table for the provided glyph. 374// Returns false if the glyph is not covered by this lookup. 375type indexLookupFunc func(GlyphIndex) (int, bool) 376 377func (f *Font) makeCachedCoverageLookup(buf []byte, offset int) ([]byte, indexLookupFunc, error) { 378 var err error 379 buf, err = f.src.view(buf, offset, 2) 380 if err != nil { 381 return buf, nil, err 382 } 383 switch u16(buf) { 384 case 1: 385 // Coverage Format 1: coverageFormat, glyphCount, []glyphArray 386 buf, _, err = f.src.varLenView(buf, offset, 4, 2, 2) 387 if err != nil { 388 return buf, nil, err 389 } 390 return buf, makeCachedCoverageList(buf[2:]), nil 391 case 2: 392 // Coverage Format 2: coverageFormat, rangeCount, []rangeRecords{startGlyphID, endGlyphID, startCoverageIndex} 393 buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6) 394 if err != nil { 395 return buf, nil, err 396 } 397 return buf, makeCachedCoverageRange(buf[2:]), nil 398 default: 399 return buf, nil, errUnsupportedCoverageFormat 400 } 401} 402 403func makeCachedCoverageList(buf []byte) indexLookupFunc { 404 num := int(u16(buf)) 405 list := make([]byte, len(buf)-2) 406 copy(list, buf[2:]) 407 return func(gi GlyphIndex) (int, bool) { 408 idx := sort.Search(num, func(i int) bool { 409 return gi <= GlyphIndex(u16(list[i*2:])) 410 }) 411 if idx < num && GlyphIndex(u16(list[idx*2:])) == gi { 412 return idx, true 413 } 414 415 return 0, false 416 } 417} 418 419func makeCachedCoverageRange(buf []byte) indexLookupFunc { 420 num := int(u16(buf)) 421 ranges := make([]byte, len(buf)-2) 422 copy(ranges, buf[2:]) 423 return func(gi GlyphIndex) (int, bool) { 424 if num == 0 { 425 return 0, false 426 } 427 428 // ranges is an array of startGlyphID, endGlyphID and startCoverageIndex 429 // Ranges are non-overlapping. 430 // The following GlyphIDs/index pairs are stored as follows: 431 // pairs: 130=0, 131=1, 132=2, 133=3, 134=4, 135=5, 137=6 432 // ranges: 130, 135, 0 137, 137, 6 433 // startCoverageIndex is used to calculate the index without counting 434 // the length of the preceeding ranges 435 436 idx := sort.Search(num, func(i int) bool { 437 return gi <= GlyphIndex(u16(ranges[i*6:])) 438 }) 439 // idx either points to a matching start, or to the next range (or idx==num) 440 // e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range 441 442 // check if gi is the start of a range, but only if sort.Search returned a valid result 443 if idx < num { 444 if start := u16(ranges[idx*6:]); gi == GlyphIndex(start) { 445 return int(u16(ranges[idx*6+4:])), true 446 } 447 } 448 // check if gi is in previous range 449 if idx > 0 { 450 idx-- 451 start, end := u16(ranges[idx*6:]), u16(ranges[idx*6+2:]) 452 if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) { 453 return int(u16(ranges[idx*6+4:]) + uint16(gi) - start), true 454 } 455 } 456 457 return 0, false 458 } 459} 460 461// classLookupFunc returns the class ID for the provided glyph. Returns 0 462// (default class) for glyphs not covered by this lookup. 463type classLookupFunc func(GlyphIndex) int 464 465func (f *Font) makeCachedClassLookup(buf []byte, offset int) ([]byte, classLookupFunc, error) { 466 var err error 467 buf, err = f.src.view(buf, offset, 2) 468 if err != nil { 469 return buf, nil, err 470 } 471 switch u16(buf) { 472 case 1: 473 // ClassDefFormat 1: classFormat, startGlyphID, glyphCount, []classValueArray 474 buf, _, err = f.src.varLenView(buf, offset, 6, 4, 2) 475 if err != nil { 476 return buf, nil, err 477 } 478 return buf, makeCachedClassLookupFormat1(buf), nil 479 case 2: 480 // ClassDefFormat 2: classFormat, classRangeCount, []classRangeRecords 481 buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6) 482 if err != nil { 483 return buf, nil, err 484 } 485 return buf, makeCachedClassLookupFormat2(buf), nil 486 default: 487 return buf, nil, errUnsupportedClassDefFormat 488 } 489} 490 491func makeCachedClassLookupFormat1(buf []byte) classLookupFunc { 492 startGI := u16(buf[2:]) 493 num := u16(buf[4:]) 494 classIDs := make([]byte, len(buf)-4) 495 copy(classIDs, buf[6:]) 496 497 return func(gi GlyphIndex) int { 498 // classIDs is an array of target class IDs. gi is the index into that array (minus startGI). 499 if gi < GlyphIndex(startGI) || gi >= GlyphIndex(startGI+num) { 500 // default to class 0 501 return 0 502 } 503 return int(u16(classIDs[(int(gi)-int(startGI))*2:])) 504 } 505} 506 507func makeCachedClassLookupFormat2(buf []byte) classLookupFunc { 508 num := int(u16(buf[2:])) 509 classRanges := make([]byte, len(buf)-2) 510 copy(classRanges, buf[4:]) 511 512 return func(gi GlyphIndex) int { 513 if num == 0 { 514 return 0 // default to class 0 515 } 516 517 // classRange is an array of startGlyphID, endGlyphID and target class ID. 518 // Ranges are non-overlapping. 519 // E.g. 130, 135, 1 137, 137, 5 etc 520 521 idx := sort.Search(num, func(i int) bool { 522 return gi <= GlyphIndex(u16(classRanges[i*6:])) 523 }) 524 // idx either points to a matching start, or to the next range (or idx==num) 525 // e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range 526 527 // check if gi is the start of a range, but only if sort.Search returned a valid result 528 if idx < num { 529 if start := u16(classRanges[idx*6:]); gi == GlyphIndex(start) { 530 return int(u16(classRanges[idx*6+4:])) 531 } 532 } 533 // check if gi is in previous range 534 if idx > 0 { 535 idx-- 536 start, end := u16(classRanges[idx*6:]), u16(classRanges[idx*6+2:]) 537 if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) { 538 return int(u16(classRanges[idx*6+4:])) 539 } 540 } 541 // default to class 0 542 return 0 543 } 544} 545