git.lucas.co / go_mono
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 }