| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- // Copyright 2019 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package sfnt
- import (
- "sort"
- )
- const (
- hexScriptLatn = uint32(0x6c61746e) // latn
- hexScriptDFLT = uint32(0x44464c54) // DFLT
- hexFeatureKern = uint32(0x6b65726e) // kern
- )
- // kernFunc returns the unscaled kerning value for kerning pair a+b.
- // Returns ErrNotFound if no kerning is specified for this pair.
- type kernFunc func(a, b GlyphIndex) (int16, error)
- func (f *Font) parseGPOSKern(buf []byte) ([]byte, []kernFunc, error) {
- // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
- if f.gpos.length == 0 {
- return buf, nil, nil
- }
- const headerSize = 10 // GPOS header v1.1 is 14 bytes, but we don't support FeatureVariations
- if f.gpos.length < headerSize {
- return buf, nil, errInvalidGPOSTable
- }
- buf, err := f.src.view(buf, int(f.gpos.offset), headerSize)
- if err != nil {
- return buf, nil, err
- }
- // check for version 1.0/1.1
- if u16(buf) != 1 || u16(buf[2:]) > 1 {
- return buf, nil, errUnsupportedGPOSTable
- }
- scriptListOffset := u16(buf[4:])
- featureListOffset := u16(buf[6:])
- lookupListOffset := u16(buf[8:])
- // get all feature indices for latn script
- buf, featureIdxs, err := f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptLatn)
- if err != nil {
- return buf, nil, err
- }
- if len(featureIdxs) == 0 {
- // get all feature indices for DFLT script
- buf, featureIdxs, err = f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptDFLT)
- if err != nil {
- return buf, nil, err
- }
- if len(featureIdxs) == 0 {
- return buf, nil, nil
- }
- }
- // get all lookup indices for kern features
- buf, lookupIdx, err := f.parseGPOSFeaturesLookup(buf, int(f.gpos.offset)+int(featureListOffset), featureIdxs, hexFeatureKern)
- if err != nil {
- return buf, nil, err
- }
- // LookupTableList: lookupCount,[]lookups
- buf, numLookupTables, err := f.src.varLenView(buf, int(f.gpos.offset)+int(lookupListOffset), 2, 0, 2)
- if err != nil {
- return buf, nil, err
- }
- var kernFuncs []kernFunc
- lookupTables:
- for _, n := range lookupIdx {
- if n > numLookupTables {
- return buf, nil, errInvalidGPOSTable
- }
- tableOffset := int(f.gpos.offset) + int(lookupListOffset) + int(u16(buf[2+n*2:]))
- // LookupTable: lookupType, lookupFlag, subTableCount, []subtableOffsets, markFilteringSet
- buf, numSubTables, err := f.src.varLenView(buf, tableOffset, 8, 4, 2)
- if err != nil {
- return buf, nil, err
- }
- flags := u16(buf[2:])
- subTableOffsets := make([]int, numSubTables)
- for i := 0; i < int(numSubTables); i++ {
- subTableOffsets[i] = int(tableOffset) + int(u16(buf[6+i*2:]))
- }
- switch lookupType := u16(buf); lookupType {
- case 2: // PairPos table
- case 9:
- // Extension Positioning table defines an additional u32 offset
- // to allow subtables to exceed the 16-bit limit.
- for i := range subTableOffsets {
- buf, err = f.src.view(buf, subTableOffsets[i], 8)
- if err != nil {
- return buf, nil, err
- }
- if format := u16(buf); format != 1 {
- return buf, nil, errUnsupportedExtensionPosFormat
- }
- if lookupType := u16(buf[2:]); lookupType != 2 {
- continue lookupTables
- }
- subTableOffsets[i] += int(u32(buf[4:]))
- }
- default: // other types are not supported
- continue
- }
- if flags&0x0010 > 0 {
- // useMarkFilteringSet enabled, skip as it is not supported
- continue
- }
- for _, subTableOffset := range subTableOffsets {
- buf, err = f.src.view(buf, int(subTableOffset), 4)
- if err != nil {
- return buf, nil, err
- }
- format := u16(buf)
- var lookupIndex indexLookupFunc
- buf, lookupIndex, err = f.makeCachedCoverageLookup(buf, subTableOffset+int(u16(buf[2:])))
- if err != nil {
- return buf, nil, err
- }
- switch format {
- case 1: // Adjustments for Glyph Pairs
- buf, kern, err := f.parsePairPosFormat1(buf, subTableOffset, lookupIndex)
- if err != nil {
- return buf, nil, err
- }
- if kern != nil {
- kernFuncs = append(kernFuncs, kern)
- }
- case 2: // Class Pair Adjustment
- buf, kern, err := f.parsePairPosFormat2(buf, subTableOffset, lookupIndex)
- if err != nil {
- return buf, nil, err
- }
- if kern != nil {
- kernFuncs = append(kernFuncs, kern)
- }
- }
- }
- }
- return buf, kernFuncs, nil
- }
- func (f *Font) parsePairPosFormat1(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) {
- // PairPos Format 1: posFormat, coverageOffset, valueFormat1,
- // valueFormat2, pairSetCount, []pairSetOffsets
- var err error
- var nPairs int
- buf, nPairs, err = f.src.varLenView(buf, offset, 10, 8, 2)
- if err != nil {
- return buf, nil, err
- }
- // check valueFormat1 and valueFormat2 flags
- if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 {
- // we only support kerning with X_ADVANCE for first glyph
- return buf, nil, nil
- }
- // PairPos table contains an array of offsets to PairSet
- // tables, which contains an array of PairValueRecords.
- // Calculate length of complete PairPos table by jumping to
- // last PairSet.
- // We need to iterate all offsets to find the last pair as
- // offsets are not sorted and can be repeated.
- var lastPairSetOffset int
- for n := 0; n < nPairs; n++ {
- pairOffset := int(u16(buf[10+n*2:]))
- if pairOffset > lastPairSetOffset {
- lastPairSetOffset = pairOffset
- }
- }
- buf, err = f.src.view(buf, offset+lastPairSetOffset, 2)
- if err != nil {
- return buf, nil, err
- }
- pairValueCount := int(u16(buf))
- // Each PairSet contains the secondGlyph (u16) and one or more value records (all u16).
- // We only support lookup tables with one value record (X_ADVANCE, see valueFormat1/2 above).
- lastPairSetLength := 2 + pairValueCount*4
- length := lastPairSetOffset + lastPairSetLength
- buf, err = f.src.view(buf, offset, length)
- if err != nil {
- return buf, nil, err
- }
- kern := makeCachedPairPosGlyph(lookupIndex, nPairs, buf)
- return buf, kern, nil
- }
- func (f *Font) parsePairPosFormat2(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) {
- // PairPos Format 2:
- // posFormat, coverageOffset, valueFormat1, valueFormat2,
- // classDef1Offset, classDef2Offset, class1Count, class2Count,
- // []class1Records
- var err error
- buf, err = f.src.view(buf, offset, 16)
- if err != nil {
- return buf, nil, err
- }
- // check valueFormat1 and valueFormat2 flags
- if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 {
- // we only support kerning with X_ADVANCE for first glyph
- return buf, nil, nil
- }
- numClass1 := int(u16(buf[12:]))
- numClass2 := int(u16(buf[14:]))
- cdef1Offset := offset + int(u16(buf[8:]))
- cdef2Offset := offset + int(u16(buf[10:]))
- var cdef1, cdef2 classLookupFunc
- buf, cdef1, err = f.makeCachedClassLookup(buf, cdef1Offset)
- if err != nil {
- return buf, nil, err
- }
- buf, cdef2, err = f.makeCachedClassLookup(buf, cdef2Offset)
- if err != nil {
- return buf, nil, err
- }
- buf, err = f.src.view(buf, offset+16, numClass1*numClass2*2)
- if err != nil {
- return buf, nil, err
- }
- kern := makeCachedPairPosClass(
- lookupIndex,
- numClass1,
- numClass2,
- cdef1,
- cdef2,
- buf,
- )
- return buf, kern, nil
- }
- // parseGPOSScriptFeatures returns all indices of features in FeatureTable that
- // are valid for the given script.
- // Returns features from DefaultLangSys, different languages are not supported.
- // However, all observed fonts either do not use different languages or use the
- // same features as DefaultLangSys.
- func (f *Font) parseGPOSScriptFeatures(buf []byte, offset int, script uint32) ([]byte, []int, error) {
- // ScriptList table: scriptCount, []scriptRecords{scriptTag, scriptOffset}
- buf, numScriptTables, err := f.src.varLenView(buf, offset, 2, 0, 6)
- if err != nil {
- return buf, nil, err
- }
- // Search ScriptTables for script
- var scriptTableOffset uint16
- for i := 0; i < numScriptTables; i++ {
- scriptTag := u32(buf[2+i*6:])
- if scriptTag == script {
- scriptTableOffset = u16(buf[2+i*6+4:])
- break
- }
- }
- if scriptTableOffset == 0 {
- return buf, nil, nil
- }
- // Script table: defaultLangSys, langSysCount, []langSysRecords{langSysTag, langSysOffset}
- buf, err = f.src.view(buf, offset+int(scriptTableOffset), 2)
- if err != nil {
- return buf, nil, err
- }
- defaultLangSysOffset := u16(buf)
- if defaultLangSysOffset == 0 {
- return buf, nil, nil
- }
- // LangSys table: lookupOrder (reserved), requiredFeatureIndex, featureIndexCount, []featureIndices
- buf, numFeatures, err := f.src.varLenView(buf, offset+int(scriptTableOffset)+int(defaultLangSysOffset), 6, 4, 2)
- if err != nil {
- return buf, nil, err
- }
- featureIdxs := make([]int, numFeatures)
- for i := range featureIdxs {
- featureIdxs[i] = int(u16(buf[6+i*2:]))
- }
- return buf, featureIdxs, nil
- }
- func (f *Font) parseGPOSFeaturesLookup(buf []byte, offset int, featureIdxs []int, feature uint32) ([]byte, []int, error) {
- // FeatureList table: featureCount, []featureRecords{featureTag, featureOffset}
- buf, numFeatureTables, err := f.src.varLenView(buf, offset, 2, 0, 6)
- if err != nil {
- return buf, nil, err
- }
- lookupIdx := make([]int, 0, 4)
- for _, fidx := range featureIdxs {
- if fidx > numFeatureTables {
- return buf, nil, errInvalidGPOSTable
- }
- featureTag := u32(buf[2+fidx*6:])
- if featureTag != feature {
- continue
- }
- featureOffset := u16(buf[2+fidx*6+4:])
- buf, numLookups, err := f.src.varLenView(nil, offset+int(featureOffset), 4, 2, 2)
- if err != nil {
- return buf, nil, err
- }
- for i := 0; i < numLookups; i++ {
- lookupIdx = append(lookupIdx, int(u16(buf[4+i*2:])))
- }
- }
- return buf, lookupIdx, nil
- }
- func makeCachedPairPosGlyph(cov indexLookupFunc, num int, buf []byte) kernFunc {
- glyphs := make([]byte, len(buf))
- copy(glyphs, buf)
- return func(a, b GlyphIndex) (int16, error) {
- idx, found := cov(a)
- if !found {
- return 0, ErrNotFound
- }
- if idx >= num {
- return 0, ErrNotFound
- }
- offset := int(u16(glyphs[10+idx*2:]))
- if offset+1 >= len(glyphs) {
- return 0, errInvalidGPOSTable
- }
- count := int(u16(glyphs[offset:]))
- for i := 0; i < count; i++ {
- secondGlyphIndex := GlyphIndex(int(u16(glyphs[offset+2+i*4:])))
- if secondGlyphIndex == b {
- return int16(u16(glyphs[offset+2+i*4+2:])), nil
- }
- if secondGlyphIndex > b {
- return 0, ErrNotFound
- }
- }
- return 0, ErrNotFound
- }
- }
- func makeCachedPairPosClass(cov indexLookupFunc, num1, num2 int, cdef1, cdef2 classLookupFunc, buf []byte) kernFunc {
- glyphs := make([]byte, len(buf))
- copy(glyphs, buf)
- return func(a, b GlyphIndex) (int16, error) {
- // check coverage to avoid selection of default class 0
- _, found := cov(a)
- if !found {
- return 0, ErrNotFound
- }
- idxa := cdef1(a)
- idxb := cdef2(b)
- return int16(u16(glyphs[(idxb+idxa*num2)*2:])), nil
- }
- }
- // indexLookupFunc returns the index into a PairPos table for the provided glyph.
- // Returns false if the glyph is not covered by this lookup.
- type indexLookupFunc func(GlyphIndex) (int, bool)
- func (f *Font) makeCachedCoverageLookup(buf []byte, offset int) ([]byte, indexLookupFunc, error) {
- var err error
- buf, err = f.src.view(buf, offset, 2)
- if err != nil {
- return buf, nil, err
- }
- switch u16(buf) {
- case 1:
- // Coverage Format 1: coverageFormat, glyphCount, []glyphArray
- buf, _, err = f.src.varLenView(buf, offset, 4, 2, 2)
- if err != nil {
- return buf, nil, err
- }
- return buf, makeCachedCoverageList(buf[2:]), nil
- case 2:
- // Coverage Format 2: coverageFormat, rangeCount, []rangeRecords{startGlyphID, endGlyphID, startCoverageIndex}
- buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6)
- if err != nil {
- return buf, nil, err
- }
- return buf, makeCachedCoverageRange(buf[2:]), nil
- default:
- return buf, nil, errUnsupportedCoverageFormat
- }
- }
- func makeCachedCoverageList(buf []byte) indexLookupFunc {
- num := int(u16(buf))
- list := make([]byte, len(buf)-2)
- copy(list, buf[2:])
- return func(gi GlyphIndex) (int, bool) {
- idx := sort.Search(num, func(i int) bool {
- return gi <= GlyphIndex(u16(list[i*2:]))
- })
- if idx < num && GlyphIndex(u16(list[idx*2:])) == gi {
- return idx, true
- }
- return 0, false
- }
- }
- func makeCachedCoverageRange(buf []byte) indexLookupFunc {
- num := int(u16(buf))
- ranges := make([]byte, len(buf)-2)
- copy(ranges, buf[2:])
- return func(gi GlyphIndex) (int, bool) {
- if num == 0 {
- return 0, false
- }
- // ranges is an array of startGlyphID, endGlyphID and startCoverageIndex
- // Ranges are non-overlapping.
- // The following GlyphIDs/index pairs are stored as follows:
- // pairs: 130=0, 131=1, 132=2, 133=3, 134=4, 135=5, 137=6
- // ranges: 130, 135, 0 137, 137, 6
- // startCoverageIndex is used to calculate the index without counting
- // the length of the preceding ranges
- idx := sort.Search(num, func(i int) bool {
- return gi <= GlyphIndex(u16(ranges[i*6:]))
- })
- // idx either points to a matching start, or to the next range (or idx==num)
- // e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range
- // check if gi is the start of a range, but only if sort.Search returned a valid result
- if idx < num {
- if start := u16(ranges[idx*6:]); gi == GlyphIndex(start) {
- return int(u16(ranges[idx*6+4:])), true
- }
- }
- // check if gi is in previous range
- if idx > 0 {
- idx--
- start, end := u16(ranges[idx*6:]), u16(ranges[idx*6+2:])
- if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) {
- return int(u16(ranges[idx*6+4:]) + uint16(gi) - start), true
- }
- }
- return 0, false
- }
- }
- // classLookupFunc returns the class ID for the provided glyph. Returns 0
- // (default class) for glyphs not covered by this lookup.
- type classLookupFunc func(GlyphIndex) int
- func (f *Font) makeCachedClassLookup(buf []byte, offset int) ([]byte, classLookupFunc, error) {
- var err error
- buf, err = f.src.view(buf, offset, 2)
- if err != nil {
- return buf, nil, err
- }
- switch u16(buf) {
- case 1:
- // ClassDefFormat 1: classFormat, startGlyphID, glyphCount, []classValueArray
- buf, _, err = f.src.varLenView(buf, offset, 6, 4, 2)
- if err != nil {
- return buf, nil, err
- }
- return buf, makeCachedClassLookupFormat1(buf), nil
- case 2:
- // ClassDefFormat 2: classFormat, classRangeCount, []classRangeRecords
- buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6)
- if err != nil {
- return buf, nil, err
- }
- return buf, makeCachedClassLookupFormat2(buf), nil
- default:
- return buf, nil, errUnsupportedClassDefFormat
- }
- }
- func makeCachedClassLookupFormat1(buf []byte) classLookupFunc {
- startGI := u16(buf[2:])
- num := u16(buf[4:])
- classIDs := make([]byte, len(buf)-4)
- copy(classIDs, buf[6:])
- return func(gi GlyphIndex) int {
- // classIDs is an array of target class IDs. gi is the index into that array (minus startGI).
- if gi < GlyphIndex(startGI) || gi >= GlyphIndex(startGI+num) {
- // default to class 0
- return 0
- }
- return int(u16(classIDs[(int(gi)-int(startGI))*2:]))
- }
- }
- func makeCachedClassLookupFormat2(buf []byte) classLookupFunc {
- num := int(u16(buf[2:]))
- classRanges := make([]byte, len(buf)-2)
- copy(classRanges, buf[4:])
- return func(gi GlyphIndex) int {
- if num == 0 {
- return 0 // default to class 0
- }
- // classRange is an array of startGlyphID, endGlyphID and target class ID.
- // Ranges are non-overlapping.
- // E.g. 130, 135, 1 137, 137, 5 etc
- idx := sort.Search(num, func(i int) bool {
- return gi <= GlyphIndex(u16(classRanges[i*6:]))
- })
- // idx either points to a matching start, or to the next range (or idx==num)
- // e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range
- // check if gi is the start of a range, but only if sort.Search returned a valid result
- if idx < num {
- if start := u16(classRanges[idx*6:]); gi == GlyphIndex(start) {
- return int(u16(classRanges[idx*6+4:]))
- }
- }
- // check if gi is in previous range
- if idx > 0 {
- idx--
- start, end := u16(classRanges[idx*6:]), u16(classRanges[idx*6+2:])
- if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) {
- return int(u16(classRanges[idx*6+4:]))
- }
- }
- // default to class 0
- return 0
- }
- }
|