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