...

Source file src/oss.terrastruct.com/d2/lib/font/subsetFont.go

Documentation: oss.terrastruct.com/d2/lib/font

     1  /*
     2   * The following code is part of https://github.com/jung-kurt/gofpdf
     3   *
     4   * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy)
     5   *
     6   * Permission to use, copy, modify, and distribute this software for any
     7   * purpose with or without fee is hereby granted, provided that the above
     8   * copyright notice and this permission notice appear in all copies.
     9   *
    10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    11   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    12   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    13   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    14   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    15   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    16   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    17   */
    18  
    19  package font
    20  
    21  import (
    22  	"bytes"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"math"
    26  	"sort"
    27  )
    28  
    29  // flags
    30  const symbolWords = 1 << 0
    31  const symbolScale = 1 << 3
    32  const symbolContinue = 1 << 5
    33  const symbolAllScale = 1 << 6
    34  const symbol2x2 = 1 << 7
    35  
    36  type fontBoxType struct {
    37  	Xmin, Ymin, Xmax, Ymax int
    38  }
    39  
    40  type utf8FontFile struct {
    41  	fileReader           *fileReader
    42  	LastRune             int
    43  	tableDescriptions    map[string]*tableDescription
    44  	outTablesData        map[string][]byte
    45  	symbolPosition       []int
    46  	charSymbolDictionary map[int]int
    47  	Ascent               int
    48  	Descent              int
    49  	Bbox                 fontBoxType
    50  	CapHeight            int
    51  	StemV                int
    52  	ItalicAngle          int
    53  	Flags                int
    54  	UnderlinePosition    float64
    55  	UnderlineThickness   float64
    56  	CharWidths           []int
    57  	DefaultWidth         float64
    58  	symbolData           map[int]map[string][]int
    59  	CodeSymbolDictionary map[int]int
    60  }
    61  
    62  type tableDescription struct {
    63  	name     string
    64  	checksum []int
    65  	position int
    66  	size     int
    67  }
    68  
    69  type fileReader struct {
    70  	readerPosition int64
    71  	array          []byte
    72  }
    73  
    74  func (fr *fileReader) Read(s int) []byte {
    75  	b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)]
    76  	fr.readerPosition += int64(s)
    77  	return b
    78  }
    79  
    80  func (fr *fileReader) seek(shift int64, flag int) (int64, error) {
    81  	if flag == 0 {
    82  		fr.readerPosition = shift
    83  	} else if flag == 1 {
    84  		fr.readerPosition += shift
    85  	} else if flag == 2 {
    86  		fr.readerPosition = int64(len(fr.array)) - shift
    87  	}
    88  	return int64(fr.readerPosition), nil
    89  }
    90  
    91  func newUTF8Font(reader *fileReader) *utf8FontFile {
    92  	utf := utf8FontFile{
    93  		fileReader: reader,
    94  	}
    95  	return &utf
    96  }
    97  
    98  func (utf *utf8FontFile) generateTableDescriptions() {
    99  
   100  	tablesCount := utf.readUint16()
   101  	_ = utf.readUint16()
   102  	_ = utf.readUint16()
   103  	_ = utf.readUint16()
   104  	utf.tableDescriptions = make(map[string]*tableDescription)
   105  
   106  	for i := 0; i < tablesCount; i++ {
   107  		record := tableDescription{
   108  			name:     utf.readTableName(),
   109  			checksum: []int{utf.readUint16(), utf.readUint16()},
   110  			position: utf.readUint32(),
   111  			size:     utf.readUint32(),
   112  		}
   113  		utf.tableDescriptions[record.name] = &record
   114  	}
   115  }
   116  
   117  func (utf *utf8FontFile) readTableName() string {
   118  	return string(utf.fileReader.Read(4))
   119  }
   120  
   121  func (utf *utf8FontFile) readUint16() int {
   122  	s := utf.fileReader.Read(2)
   123  	return (int(s[0]) << 8) + int(s[1])
   124  }
   125  
   126  func (utf *utf8FontFile) readUint32() int {
   127  	s := utf.fileReader.Read(4)
   128  	return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 	16777216  = 1<<24
   129  }
   130  
   131  func (utf *utf8FontFile) calcInt32(x, y []int) []int {
   132  	answer := make([]int, 2)
   133  	if y[1] > x[1] {
   134  		x[1] += 1 << 16
   135  		x[0]++
   136  	}
   137  	answer[1] = x[1] - y[1]
   138  	if y[0] > x[0] {
   139  		x[0] += 1 << 16
   140  	}
   141  	answer[0] = x[0] - y[0]
   142  	answer[0] = answer[0] & 0xFFFF
   143  	return answer
   144  }
   145  
   146  func (utf *utf8FontFile) generateChecksum(data []byte) []int {
   147  	if (len(data) % 4) != 0 {
   148  		for i := 0; (len(data) % 4) != 0; i++ {
   149  			data = append(data, 0)
   150  		}
   151  	}
   152  	answer := []int{0x0000, 0x0000}
   153  	for i := 0; i < len(data); i += 4 {
   154  		answer[0] += (int(data[i]) << 8) + int(data[i+1])
   155  		answer[1] += (int(data[i+2]) << 8) + int(data[i+3])
   156  		answer[0] += answer[1] >> 16
   157  		answer[1] = answer[1] & 0xFFFF
   158  		answer[0] = answer[0] & 0xFFFF
   159  	}
   160  	return answer
   161  }
   162  
   163  func (utf *utf8FontFile) seek(shift int) {
   164  	_, _ = utf.fileReader.seek(int64(shift), 0)
   165  }
   166  
   167  func (utf *utf8FontFile) skip(delta int) {
   168  	_, _ = utf.fileReader.seek(int64(delta), 1)
   169  }
   170  
   171  // SeekTable position
   172  func (utf *utf8FontFile) SeekTable(name string) int {
   173  	return utf.seekTable(name, 0)
   174  }
   175  
   176  func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int {
   177  	_, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0)
   178  	return int(utf.fileReader.readerPosition)
   179  }
   180  
   181  func (utf *utf8FontFile) readInt16() int16 {
   182  	s := utf.fileReader.Read(2)
   183  	a := (int16(s[0]) << 8) + int16(s[1])
   184  	if (int(a) & (1 << 15)) == 0 {
   185  		a = int16(int(a) - (1 << 16))
   186  	}
   187  	return a
   188  }
   189  
   190  func (utf *utf8FontFile) getUint16(pos int) int {
   191  	_, _ = utf.fileReader.seek(int64(pos), 0)
   192  	s := utf.fileReader.Read(2)
   193  	return (int(s[0]) << 8) + int(s[1])
   194  }
   195  
   196  func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte {
   197  	stream = append([]byte{}, stream...)
   198  	return append(append(stream[:offset], value...), stream[offset+len(value):]...)
   199  }
   200  
   201  func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte {
   202  	up := make([]byte, 2)
   203  	binary.BigEndian.PutUint16(up, uint16(value))
   204  	return utf.splice(stream, offset, up)
   205  }
   206  
   207  func (utf *utf8FontFile) getRange(pos, length int) []byte {
   208  	_, _ = utf.fileReader.seek(int64(pos), 0)
   209  	if length < 1 {
   210  		return make([]byte, 0)
   211  	}
   212  	s := utf.fileReader.Read(length)
   213  	return s
   214  }
   215  
   216  func (utf *utf8FontFile) getTableData(name string) []byte {
   217  	desckrip := utf.tableDescriptions[name]
   218  	if desckrip == nil {
   219  		return nil
   220  	}
   221  	if desckrip.size == 0 {
   222  		return nil
   223  	}
   224  	_, _ = utf.fileReader.seek(int64(desckrip.position), 0)
   225  	s := utf.fileReader.Read(desckrip.size)
   226  	return s
   227  }
   228  
   229  func (utf *utf8FontFile) setOutTable(name string, data []byte) {
   230  	if data == nil {
   231  		return
   232  	}
   233  	if name == "head" {
   234  		data = utf.splice(data, 8, []byte{0, 0, 0, 0})
   235  	}
   236  	utf.outTablesData[name] = data
   237  }
   238  
   239  func (utf *utf8FontFile) generateCMAP() map[int][]int {
   240  	cmapPosition := utf.SeekTable("cmap")
   241  	utf.skip(2)
   242  	cmapTableCount := utf.readUint16()
   243  	runeCmapPosition := 0
   244  	for i := 0; i < cmapTableCount; i++ {
   245  		system := utf.readUint16()
   246  		coder := utf.readUint16()
   247  		position := utf.readUint32()
   248  		oldPosition := utf.fileReader.readerPosition
   249  		if (system == 3 && coder == 1) || system == 0 {
   250  			format := utf.getUint16(cmapPosition + position)
   251  			if format == 4 {
   252  				runeCmapPosition = cmapPosition + position
   253  				break
   254  			}
   255  		}
   256  		utf.seek(int(oldPosition))
   257  	}
   258  
   259  	if runeCmapPosition == 0 {
   260  		fmt.Printf("Font does not have cmap for Unicode\n")
   261  		return nil
   262  	}
   263  
   264  	symbolCharDictionary := make(map[int][]int)
   265  	charSymbolDictionary := make(map[int]int)
   266  	utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary)
   267  
   268  	utf.charSymbolDictionary = charSymbolDictionary
   269  
   270  	return symbolCharDictionary
   271  }
   272  
   273  func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) {
   274  	symbolCollection := map[int]int{0: 0}
   275  	charSymbolPairCollection := make(map[int]int)
   276  	for _, char := range usedRunes {
   277  		if _, OK := utf.charSymbolDictionary[char]; OK {
   278  			symbolCollection[utf.charSymbolDictionary[char]] = char
   279  			charSymbolPairCollection[char] = utf.charSymbolDictionary[char]
   280  
   281  		}
   282  		utf.LastRune = max(utf.LastRune, char)
   283  	}
   284  
   285  	begin := utf.tableDescriptions["glyf"].position
   286  
   287  	symbolArray := make(map[int]int)
   288  	symbolCollectionKeys := keySortInt(symbolCollection)
   289  
   290  	symbolCounter := 0
   291  	maxRune := 0
   292  	for _, oldSymbolIndex := range symbolCollectionKeys {
   293  		maxRune = max(maxRune, symbolCollection[oldSymbolIndex])
   294  		symbolArray[oldSymbolIndex] = symbolCounter
   295  		symbolCounter++
   296  	}
   297  	charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection)
   298  	runeSymbolPairCollection := make(map[int]int)
   299  	for _, runa := range charSymbolPairCollectionKeys {
   300  		runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]]
   301  	}
   302  	utf.CodeSymbolDictionary = runeSymbolPairCollection
   303  
   304  	symbolCollectionKeys = keySortInt(symbolCollection)
   305  	for _, oldSymbolIndex := range symbolCollectionKeys {
   306  		_, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys)
   307  	}
   308  
   309  	return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys
   310  }
   311  
   312  func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte {
   313  	cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection)
   314  	cidID := 0
   315  	cidArray := make(map[int][]int)
   316  	prevCid := -2
   317  	prevSymbol := -1
   318  	for _, cid := range cidSymbolPairCollectionKeys {
   319  		if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) {
   320  			if n, OK := cidArray[cidID]; !OK || n == nil {
   321  				cidArray[cidID] = make([]int, 0)
   322  			}
   323  			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
   324  		} else {
   325  			cidID = cid
   326  			cidArray[cidID] = make([]int, 0)
   327  			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
   328  		}
   329  		prevCid = cid
   330  		prevSymbol = cidSymbolPairCollection[cid]
   331  	}
   332  	cidArrayKeys := keySortArrayRangeMap(cidArray)
   333  	segCount := len(cidArray) + 1
   334  
   335  	searchRange := 1
   336  	entrySelector := 0
   337  	for searchRange*2 <= segCount {
   338  		searchRange = searchRange * 2
   339  		entrySelector = entrySelector + 1
   340  	}
   341  	searchRange = searchRange * 2
   342  	rangeShift := segCount*2 - searchRange
   343  
   344  	data := []int{0, 1, 3, 1, 0, 12, 4}
   345  
   346  	cmap := []int{segCount * 2, searchRange, entrySelector, rangeShift}
   347  	for _, start := range cidArrayKeys {
   348  		endCode := start + (len(cidArray[start]) - 1)
   349  		cmap = append(cmap, endCode)
   350  	}
   351  	cmap = append(cmap, 0xFFFF)
   352  	cmap = append(cmap, 0)
   353  
   354  	cmap = append(cmap, cidArrayKeys...)
   355  	cmap = append(cmap, 0xFFFF)
   356  	for _, cidKey := range cidArrayKeys {
   357  		idDelta := -(cidKey - cidArray[cidKey][0])
   358  		cmap = append(cmap, idDelta)
   359  	}
   360  	cmap = append(cmap, 1)
   361  	for range cidArray {
   362  		cmap = append(cmap, 0)
   363  
   364  	}
   365  	cmap = append(cmap, 0)
   366  	for _, start := range cidArrayKeys {
   367  		cmap = append(cmap, cidArray[start]...)
   368  	}
   369  	cmap = append(cmap, 0)
   370  
   371  	// Calculating cmap length based off of fpdf https://github.com/Setasign/FPDF/blob/f4104a04c9a3f95c4c26a0a0531abebcc980987a/makefont/ttfparser.php#L476
   372  	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values
   373  	// The 3 extra bytes are for encodingID, offset, format, which are also part of the cmap
   374  	length := (3 + len(cmap)) * 2
   375  	data = append(data, length, 0)
   376  	data = append(data, cmap...)
   377  
   378  	cmapstr := make([]byte, 0)
   379  	for _, cm := range data {
   380  		cmapstr = append(cmapstr, packUint16(cm)...)
   381  	}
   382  
   383  	return cmapstr
   384  }
   385  
   386  // GenerateCutFont fill utf8FontFile from .utf file, only with runes from usedRunes
   387  func (utf *utf8FontFile) GenerateCutFont(usedRunes map[int]int) []byte {
   388  	utf.fileReader.readerPosition = 0
   389  	utf.symbolPosition = make([]int, 0)
   390  	utf.charSymbolDictionary = make(map[int]int)
   391  	utf.tableDescriptions = make(map[string]*tableDescription)
   392  	utf.outTablesData = make(map[string][]byte)
   393  	utf.Ascent = 0
   394  	utf.Descent = 0
   395  	utf.skip(4)
   396  	utf.LastRune = 0
   397  	utf.generateTableDescriptions()
   398  
   399  	utf.SeekTable("head")
   400  	utf.skip(50)
   401  	LocaFormat := utf.readUint16()
   402  
   403  	utf.SeekTable("hhea")
   404  	utf.skip(34)
   405  	metricsCount := utf.readUint16()
   406  	oldMetrics := metricsCount
   407  
   408  	utf.SeekTable("maxp")
   409  	utf.skip(4)
   410  	numSymbols := utf.readUint16()
   411  
   412  	symbolCharDictionary := utf.generateCMAP()
   413  	if symbolCharDictionary == nil {
   414  		return nil
   415  	}
   416  
   417  	utf.parseHMTXTable(metricsCount, numSymbols, symbolCharDictionary, 1.0)
   418  
   419  	utf.parseLOCATable(LocaFormat, numSymbols)
   420  
   421  	cidSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys := utf.parseSymbols(usedRunes)
   422  
   423  	metricsCount = len(symbolCollection)
   424  	numSymbols = metricsCount
   425  
   426  	utf.setOutTable("name", utf.getTableData("name"))
   427  	utf.setOutTable("cvt ", utf.getTableData("cvt "))
   428  	utf.setOutTable("fpgm", utf.getTableData("fpgm"))
   429  	utf.setOutTable("prep", utf.getTableData("prep"))
   430  	utf.setOutTable("gasp", utf.getTableData("gasp"))
   431  
   432  	postTable := utf.getTableData("post")
   433  	postTable = append(append([]byte{0x00, 0x03, 0x00, 0x00}, postTable[4:16]...), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...)
   434  	utf.setOutTable("post", postTable)
   435  
   436  	delete(cidSymbolPairCollection, 0)
   437  
   438  	utf.setOutTable("cmap", utf.generateCMAPTable(cidSymbolPairCollection, numSymbols))
   439  
   440  	symbolData := utf.getTableData("glyf")
   441  
   442  	offsets := make([]int, 0)
   443  	glyfData := make([]byte, 0)
   444  	pos := 0
   445  	hmtxData := make([]byte, 0)
   446  	utf.symbolData = make(map[int]map[string][]int, 0)
   447  
   448  	for _, originalSymbolIdx := range symbolCollectionKeys {
   449  		hm := utf.getMetrics(oldMetrics, originalSymbolIdx)
   450  		hmtxData = append(hmtxData, hm...)
   451  
   452  		offsets = append(offsets, pos)
   453  		symbolPos := utf.symbolPosition[originalSymbolIdx]
   454  		symbolLen := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
   455  		data := symbolData[symbolPos : symbolPos+symbolLen]
   456  		var up int
   457  		if symbolLen > 0 {
   458  			up = unpackUint16(data[0:2])
   459  		}
   460  
   461  		if symbolLen > 2 && (up&(1<<15)) != 0 {
   462  			posInSymbol := 10
   463  			flags := symbolContinue
   464  			nComponentElements := 0
   465  			for (flags & symbolContinue) != 0 {
   466  				nComponentElements++
   467  				up = unpackUint16(data[posInSymbol : posInSymbol+2])
   468  				flags = up
   469  				up = unpackUint16(data[posInSymbol+2 : posInSymbol+4])
   470  				symbolIdx := up
   471  				if _, OK := utf.symbolData[originalSymbolIdx]; !OK {
   472  					utf.symbolData[originalSymbolIdx] = make(map[string][]int)
   473  				}
   474  				if _, OK := utf.symbolData[originalSymbolIdx]["compSymbols"]; !OK {
   475  					utf.symbolData[originalSymbolIdx]["compSymbols"] = make([]int, 0)
   476  				}
   477  				utf.symbolData[originalSymbolIdx]["compSymbols"] = append(utf.symbolData[originalSymbolIdx]["compSymbols"], symbolIdx)
   478  				data = utf.insertUint16(data, posInSymbol+2, symbolArray[symbolIdx])
   479  				posInSymbol += 4
   480  				if (flags & symbolWords) != 0 {
   481  					posInSymbol += 4
   482  				} else {
   483  					posInSymbol += 2
   484  				}
   485  				if (flags & symbolScale) != 0 {
   486  					posInSymbol += 2
   487  				} else if (flags & symbolAllScale) != 0 {
   488  					posInSymbol += 4
   489  				} else if (flags & symbol2x2) != 0 {
   490  					posInSymbol += 8
   491  				}
   492  			}
   493  		}
   494  
   495  		glyfData = append(glyfData, data...)
   496  		pos += symbolLen
   497  		if pos%4 != 0 {
   498  			padding := 4 - (pos % 4)
   499  			glyfData = append(glyfData, make([]byte, padding)...)
   500  			pos += padding
   501  		}
   502  	}
   503  
   504  	offsets = append(offsets, pos)
   505  	utf.setOutTable("glyf", glyfData)
   506  
   507  	utf.setOutTable("hmtx", hmtxData)
   508  
   509  	locaData := make([]byte, 0)
   510  	if ((pos + 1) >> 1) > 0xFFFF {
   511  		LocaFormat = 1
   512  		for _, offset := range offsets {
   513  			locaData = append(locaData, packUint32(offset)...)
   514  		}
   515  	} else {
   516  		LocaFormat = 0
   517  		for _, offset := range offsets {
   518  			locaData = append(locaData, packUint16(offset/2)...)
   519  		}
   520  	}
   521  	utf.setOutTable("loca", locaData)
   522  
   523  	headData := utf.getTableData("head")
   524  	headData = utf.insertUint16(headData, 50, LocaFormat)
   525  	utf.setOutTable("head", headData)
   526  
   527  	hheaData := utf.getTableData("hhea")
   528  	hheaData = utf.insertUint16(hheaData, 34, metricsCount)
   529  	utf.setOutTable("hhea", hheaData)
   530  
   531  	maxp := utf.getTableData("maxp")
   532  	maxp = utf.insertUint16(maxp, 4, numSymbols)
   533  	utf.setOutTable("maxp", maxp)
   534  
   535  	os2Data := utf.getTableData("OS/2")
   536  	utf.setOutTable("OS/2", os2Data)
   537  
   538  	return utf.assembleTables()
   539  }
   540  
   541  func (utf *utf8FontFile) getSymbols(originalSymbolIdx int, start *int, symbolSet map[int]int, SymbolsCollection map[int]int, SymbolsCollectionKeys []int) (*int, map[int]int, map[int]int, []int) {
   542  	symbolPos := utf.symbolPosition[originalSymbolIdx]
   543  	symbolSize := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
   544  	if symbolSize == 0 {
   545  		return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
   546  	}
   547  	utf.seek(*start + symbolPos)
   548  
   549  	lineCount := utf.readInt16()
   550  
   551  	if lineCount < 0 {
   552  		utf.skip(8)
   553  		flags := symbolContinue
   554  		for flags&symbolContinue != 0 {
   555  			flags = utf.readUint16()
   556  			symbolIndex := utf.readUint16()
   557  			if _, OK := symbolSet[symbolIndex]; !OK {
   558  				symbolSet[symbolIndex] = len(SymbolsCollection)
   559  				SymbolsCollection[symbolIndex] = 1
   560  				SymbolsCollectionKeys = append(SymbolsCollectionKeys, symbolIndex)
   561  			}
   562  			oldPosition, _ := utf.fileReader.seek(0, 1)
   563  			_, _, _, SymbolsCollectionKeys = utf.getSymbols(symbolIndex, start, symbolSet, SymbolsCollection, SymbolsCollectionKeys)
   564  			utf.seek(int(oldPosition))
   565  			if flags&symbolWords != 0 {
   566  				utf.skip(4)
   567  			} else {
   568  				utf.skip(2)
   569  			}
   570  			if flags&symbolScale != 0 {
   571  				utf.skip(2)
   572  			} else if flags&symbolAllScale != 0 {
   573  				utf.skip(4)
   574  			} else if flags&symbol2x2 != 0 {
   575  				utf.skip(8)
   576  			}
   577  		}
   578  	}
   579  	return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
   580  }
   581  
   582  func (utf *utf8FontFile) parseHMTXTable(numberOfHMetrics, numSymbols int, symbolToChar map[int][]int, scale float64) {
   583  	var widths int
   584  	start := utf.SeekTable("hmtx")
   585  	arrayWidths := 0
   586  	var arr []int
   587  	utf.CharWidths = make([]int, 256*256)
   588  	charCount := 0
   589  	arr = unpackUint16Array(utf.getRange(start, numberOfHMetrics*4))
   590  	for symbol := 0; symbol < numberOfHMetrics; symbol++ {
   591  		arrayWidths = arr[(symbol*2)+1]
   592  		if _, OK := symbolToChar[symbol]; OK || symbol == 0 {
   593  
   594  			if arrayWidths >= (1 << 15) {
   595  				arrayWidths = 0
   596  			}
   597  			if symbol == 0 {
   598  				utf.DefaultWidth = scale * float64(arrayWidths)
   599  				continue
   600  			}
   601  			for _, char := range symbolToChar[symbol] {
   602  				if char != 0 && char != 65535 {
   603  					widths = int(math.Round(scale * float64(arrayWidths)))
   604  					if widths == 0 {
   605  						widths = 65535
   606  					}
   607  					if char < 196608 {
   608  						utf.CharWidths[char] = widths
   609  						charCount++
   610  					}
   611  				}
   612  			}
   613  		}
   614  	}
   615  	diff := numSymbols - numberOfHMetrics
   616  	for pos := 0; pos < diff; pos++ {
   617  		symbol := pos + numberOfHMetrics
   618  		if _, OK := symbolToChar[symbol]; OK {
   619  			for _, char := range symbolToChar[symbol] {
   620  				if char != 0 && char != 65535 {
   621  					widths = int(math.Round(scale * float64(arrayWidths)))
   622  					if widths == 0 {
   623  						widths = 65535
   624  					}
   625  					if char < 196608 {
   626  						utf.CharWidths[char] = widths
   627  						charCount++
   628  					}
   629  				}
   630  			}
   631  		}
   632  	}
   633  	utf.CharWidths[0] = charCount
   634  }
   635  
   636  func (utf *utf8FontFile) getMetrics(metricCount, gid int) []byte {
   637  	start := utf.SeekTable("hmtx")
   638  	var metrics []byte
   639  	if gid < metricCount {
   640  		utf.seek(start + (gid * 4))
   641  		metrics = utf.fileReader.Read(4)
   642  	} else {
   643  		utf.seek(start + ((metricCount - 1) * 4))
   644  		metrics = utf.fileReader.Read(2)
   645  		utf.seek(start + (metricCount * 2) + (gid * 2))
   646  		metrics = append(metrics, utf.fileReader.Read(2)...)
   647  	}
   648  	return metrics
   649  }
   650  
   651  func (utf *utf8FontFile) parseLOCATable(format, numSymbols int) {
   652  	start := utf.SeekTable("loca")
   653  	utf.symbolPosition = make([]int, 0)
   654  	if format == 0 {
   655  		data := utf.getRange(start, (numSymbols*2)+2)
   656  		arr := unpackUint16Array(data)
   657  		for n := 0; n <= numSymbols; n++ {
   658  			utf.symbolPosition = append(utf.symbolPosition, arr[n+1]*2)
   659  		}
   660  	} else if format == 1 {
   661  		data := utf.getRange(start, (numSymbols*4)+4)
   662  		arr := unpackUint32Array(data)
   663  		for n := 0; n <= numSymbols; n++ {
   664  			utf.symbolPosition = append(utf.symbolPosition, arr[n+1])
   665  		}
   666  	} else {
   667  		fmt.Printf("Unknown loca table format %d\n", format)
   668  		return
   669  	}
   670  }
   671  
   672  func (utf *utf8FontFile) generateSCCSDictionaries(runeCmapPosition int, symbolCharDictionary map[int][]int, charSymbolDictionary map[int]int) {
   673  	maxRune := 0
   674  	utf.seek(runeCmapPosition + 2)
   675  	size := utf.readUint16()
   676  	rim := runeCmapPosition + size
   677  	utf.skip(2)
   678  
   679  	segmentSize := utf.readUint16() / 2
   680  	utf.skip(6)
   681  	completers := make([]int, 0)
   682  	for i := 0; i < segmentSize; i++ {
   683  		completers = append(completers, utf.readUint16())
   684  	}
   685  	utf.skip(2)
   686  	beginners := make([]int, 0)
   687  	for i := 0; i < segmentSize; i++ {
   688  		beginners = append(beginners, utf.readUint16())
   689  	}
   690  	sizes := make([]int, 0)
   691  	for i := 0; i < segmentSize; i++ {
   692  		sizes = append(sizes, int(utf.readInt16()))
   693  	}
   694  	readerPositionStart := utf.fileReader.readerPosition
   695  	positions := make([]int, 0)
   696  	for i := 0; i < segmentSize; i++ {
   697  		positions = append(positions, utf.readUint16())
   698  	}
   699  	var symbol int
   700  	for n := 0; n < segmentSize; n++ {
   701  		completePosition := completers[n] + 1
   702  		for char := beginners[n]; char < completePosition; char++ {
   703  			if positions[n] == 0 {
   704  				symbol = (char + sizes[n]) & 0xFFFF
   705  			} else {
   706  				position := (char-beginners[n])*2 + positions[n]
   707  				position = int(readerPositionStart) + 2*n + position
   708  				if position >= rim {
   709  					symbol = 0
   710  				} else {
   711  					symbol = utf.getUint16(position)
   712  					if symbol != 0 {
   713  						symbol = (symbol + sizes[n]) & 0xFFFF
   714  					}
   715  				}
   716  			}
   717  			charSymbolDictionary[char] = symbol
   718  			if char < 196608 {
   719  				maxRune = max(char, maxRune)
   720  			}
   721  			symbolCharDictionary[symbol] = append(symbolCharDictionary[symbol], char)
   722  		}
   723  	}
   724  }
   725  
   726  func max(i, n int) int {
   727  	if n > i {
   728  		return n
   729  	}
   730  	return i
   731  }
   732  
   733  func (utf *utf8FontFile) assembleTables() []byte {
   734  	answer := make([]byte, 0)
   735  	tablesCount := len(utf.outTablesData)
   736  	findSize := 1
   737  	writer := 0
   738  	for findSize*2 <= tablesCount {
   739  		findSize = findSize * 2
   740  		writer = writer + 1
   741  	}
   742  	findSize = findSize * 16
   743  	rOffset := tablesCount*16 - findSize
   744  
   745  	answer = append(answer, packHeader(0x00010000, tablesCount, findSize, writer, rOffset)...)
   746  
   747  	tables := utf.outTablesData
   748  	tablesNames := keySortStrings(tables)
   749  
   750  	offset := 12 + tablesCount*16
   751  	begin := 0
   752  
   753  	for _, name := range tablesNames {
   754  		if name == "head" {
   755  			begin = offset
   756  		}
   757  		answer = append(answer, []byte(name)...)
   758  		checksum := utf.generateChecksum(tables[name])
   759  		answer = append(answer, pack2Uint16(checksum[0], checksum[1])...)
   760  		answer = append(answer, pack2Uint32(offset, len(tables[name]))...)
   761  		paddedLength := (len(tables[name]) + 3) &^ 3
   762  		offset = offset + paddedLength
   763  	}
   764  
   765  	for _, key := range tablesNames {
   766  		data := append([]byte{}, tables[key]...)
   767  		data = append(data, []byte{0, 0, 0}...)
   768  		answer = append(answer, data[:(len(data)&^3)]...)
   769  	}
   770  
   771  	checksum := utf.generateChecksum([]byte(answer))
   772  	checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum)
   773  	answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1]))
   774  	return answer
   775  }
   776  
   777  func unpackUint16Array(data []byte) []int {
   778  	answer := make([]int, 1)
   779  	r := bytes.NewReader(data)
   780  	bs := make([]byte, 2)
   781  	var e error
   782  	var c int
   783  	c, e = r.Read(bs)
   784  	for e == nil && c > 0 {
   785  		answer = append(answer, int(binary.BigEndian.Uint16(bs)))
   786  		c, e = r.Read(bs)
   787  	}
   788  	return answer
   789  }
   790  
   791  func unpackUint32Array(data []byte) []int {
   792  	answer := make([]int, 1)
   793  	r := bytes.NewReader(data)
   794  	bs := make([]byte, 4)
   795  	var e error
   796  	var c int
   797  	c, e = r.Read(bs)
   798  	for e == nil && c > 0 {
   799  		answer = append(answer, int(binary.BigEndian.Uint32(bs)))
   800  		c, e = r.Read(bs)
   801  	}
   802  	return answer
   803  }
   804  
   805  func unpackUint16(data []byte) int {
   806  	return int(binary.BigEndian.Uint16(data))
   807  }
   808  
   809  func packHeader(N uint32, n1, n2, n3, n4 int) []byte {
   810  	answer := make([]byte, 0)
   811  	bs4 := make([]byte, 4)
   812  	binary.BigEndian.PutUint32(bs4, N)
   813  	answer = append(answer, bs4...)
   814  	bs := make([]byte, 2)
   815  	binary.BigEndian.PutUint16(bs, uint16(n1))
   816  	answer = append(answer, bs...)
   817  	binary.BigEndian.PutUint16(bs, uint16(n2))
   818  	answer = append(answer, bs...)
   819  	binary.BigEndian.PutUint16(bs, uint16(n3))
   820  	answer = append(answer, bs...)
   821  	binary.BigEndian.PutUint16(bs, uint16(n4))
   822  	answer = append(answer, bs...)
   823  	return answer
   824  }
   825  
   826  func pack2Uint16(n1, n2 int) []byte {
   827  	answer := make([]byte, 0)
   828  	bs := make([]byte, 2)
   829  	binary.BigEndian.PutUint16(bs, uint16(n1))
   830  	answer = append(answer, bs...)
   831  	binary.BigEndian.PutUint16(bs, uint16(n2))
   832  	answer = append(answer, bs...)
   833  	return answer
   834  }
   835  
   836  func pack2Uint32(n1, n2 int) []byte {
   837  	answer := make([]byte, 0)
   838  	bs := make([]byte, 4)
   839  	binary.BigEndian.PutUint32(bs, uint32(n1))
   840  	answer = append(answer, bs...)
   841  	binary.BigEndian.PutUint32(bs, uint32(n2))
   842  	answer = append(answer, bs...)
   843  	return answer
   844  }
   845  
   846  func packUint32(n1 int) []byte {
   847  	bs := make([]byte, 4)
   848  	binary.BigEndian.PutUint32(bs, uint32(n1))
   849  	return bs
   850  }
   851  
   852  func packUint16(n1 int) []byte {
   853  	bs := make([]byte, 2)
   854  	binary.BigEndian.PutUint16(bs, uint16(n1))
   855  	return bs
   856  }
   857  
   858  func keySortStrings(s map[string][]byte) []string {
   859  	keys := make([]string, len(s))
   860  	i := 0
   861  	for key := range s {
   862  		keys[i] = key
   863  		i++
   864  	}
   865  	sort.Strings(keys)
   866  	return keys
   867  }
   868  
   869  func keySortInt(s map[int]int) []int {
   870  	keys := make([]int, len(s))
   871  	i := 0
   872  	for key := range s {
   873  		keys[i] = key
   874  		i++
   875  	}
   876  	sort.Ints(keys)
   877  	return keys
   878  }
   879  
   880  func keySortArrayRangeMap(s map[int][]int) []int {
   881  	keys := make([]int, len(s))
   882  	i := 0
   883  	for key := range s {
   884  		keys[i] = key
   885  		i++
   886  	}
   887  	sort.Ints(keys)
   888  	return keys
   889  }
   890  
   891  // UTF8CutFont is a utility function that generates a TrueType font composed
   892  // only of the runes included in cutset. The rune glyphs are copied from This
   893  // function is demonstrated in ExampleUTF8CutFont().
   894  func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) {
   895  	f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf})
   896  	runes := map[int]int{}
   897  	for i, r := range cutset {
   898  		runes[i] = int(r)
   899  	}
   900  	outBuf = f.GenerateCutFont(runes)
   901  	return
   902  }
   903  

View as plain text