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