...

Source file src/golang.org/x/image/font/sfnt/gpos.go

Documentation: golang.org/x/image/font/sfnt

     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  }
   551  

View as plain text