codeberg.org/go-pdf/fpdf@v0.11.1/utf8fontfile.go (about)

     1  // Copyright ©2023 The go-pdf Authors. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6   * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy)
     7   *
     8   * Permission to use, copy, modify, and distribute this software for any
     9   * purpose with or without fee is hereby granted, provided that the above
    10   * copyright notice and this permission notice appear in all copies.
    11   *
    12   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    13   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    14   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    15   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    16   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    17   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    18   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    19   */
    20  
    21  package fpdf
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/binary"
    26  	"fmt"
    27  	"math"
    28  	"sort"
    29  )
    30  
    31  // flags
    32  const symbolWords = 1 << 0
    33  const symbolScale = 1 << 3
    34  const symbolContinue = 1 << 5
    35  const symbolAllScale = 1 << 6
    36  const symbol2x2 = 1 << 7
    37  
    38  // CID map Init
    39  const toUnicode = "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo\n<</Registry (Adobe)\n/Ordering (UCS)\n/Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n1 beginbfrange\n<0000> <FFFF> <0000>\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend"
    40  
    41  type utf8FontFile struct {
    42  	fileReader           *fileReader
    43  	LastRune             int
    44  	tableDescriptions    map[string]*tableDescription
    45  	outTablesData        map[string][]byte
    46  	symbolPosition       []int
    47  	charSymbolDictionary map[int]int
    48  	Ascent               int
    49  	Descent              int
    50  	fontElementSize      int
    51  	Bbox                 fontBoxType
    52  	CapHeight            int
    53  	StemV                int
    54  	ItalicAngle          int
    55  	Flags                int
    56  	UnderlinePosition    float64
    57  	UnderlineThickness   float64
    58  	CharWidths           []int
    59  	DefaultWidth         float64
    60  	symbolData           map[int]map[string][]int
    61  	CodeSymbolDictionary map[int]int
    62  }
    63  
    64  type tableDescription struct {
    65  	name     string
    66  	checksum []int
    67  	position int
    68  	size     int
    69  }
    70  
    71  type fileReader struct {
    72  	readerPosition int64
    73  	array          []byte
    74  }
    75  
    76  func (fr *fileReader) Read(s int) []byte {
    77  	b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)]
    78  	fr.readerPosition += int64(s)
    79  	return b
    80  }
    81  
    82  func (fr *fileReader) seek(shift int64, flag int) (int64, error) {
    83  	if flag == 0 {
    84  		fr.readerPosition = shift
    85  	} else if flag == 1 {
    86  		fr.readerPosition += shift
    87  	} else if flag == 2 {
    88  		fr.readerPosition = int64(len(fr.array)) - shift
    89  	}
    90  	return int64(fr.readerPosition), nil
    91  }
    92  
    93  func newUTF8Font(reader *fileReader) *utf8FontFile {
    94  	utf := utf8FontFile{
    95  		fileReader: reader,
    96  	}
    97  	return &utf
    98  }
    99  
   100  func (utf *utf8FontFile) parseFile() error {
   101  	utf.fileReader.readerPosition = 0
   102  	utf.symbolPosition = make([]int, 0)
   103  	utf.charSymbolDictionary = make(map[int]int)
   104  	utf.tableDescriptions = make(map[string]*tableDescription)
   105  	utf.outTablesData = make(map[string][]byte)
   106  	utf.Ascent = 0
   107  	utf.Descent = 0
   108  	codeType := uint32(utf.readUint32())
   109  	if codeType == 0x4F54544F {
   110  		return fmt.Errorf("not supported\n ")
   111  	}
   112  	if codeType == 0x74746366 {
   113  		return fmt.Errorf("not supported\n ")
   114  	}
   115  	if codeType != 0x00010000 && codeType != 0x74727565 {
   116  		return fmt.Errorf("not a TrueType font: codeType=%v\n ", codeType)
   117  	}
   118  	utf.generateTableDescriptions()
   119  	utf.parseTables()
   120  	return nil
   121  }
   122  
   123  func (utf *utf8FontFile) generateTableDescriptions() {
   124  
   125  	tablesCount := utf.readUint16()
   126  	_ = utf.readUint16()
   127  	_ = utf.readUint16()
   128  	_ = utf.readUint16()
   129  	utf.tableDescriptions = make(map[string]*tableDescription)
   130  
   131  	for i := 0; i < tablesCount; i++ {
   132  		record := tableDescription{
   133  			name:     utf.readTableName(),
   134  			checksum: []int{utf.readUint16(), utf.readUint16()},
   135  			position: utf.readUint32(),
   136  			size:     utf.readUint32(),
   137  		}
   138  		utf.tableDescriptions[record.name] = &record
   139  	}
   140  }
   141  
   142  func (utf *utf8FontFile) readTableName() string {
   143  	return string(utf.fileReader.Read(4))
   144  }
   145  
   146  func (utf *utf8FontFile) readUint16() int {
   147  	s := utf.fileReader.Read(2)
   148  	return (int(s[0]) << 8) + int(s[1])
   149  }
   150  
   151  func (utf *utf8FontFile) readUint32() int {
   152  	s := utf.fileReader.Read(4)
   153  	return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 	16777216  = 1<<24
   154  }
   155  
   156  func (utf *utf8FontFile) calcInt32(x, y []int) []int {
   157  	answer := make([]int, 2)
   158  	if y[1] > x[1] {
   159  		x[1] += 1 << 16
   160  		x[0]++
   161  	}
   162  	answer[1] = x[1] - y[1]
   163  	if y[0] > x[0] {
   164  		x[0] += 1 << 16
   165  	}
   166  	answer[0] = x[0] - y[0]
   167  	answer[0] = answer[0] & 0xFFFF
   168  	return answer
   169  }
   170  
   171  func (utf *utf8FontFile) generateChecksum(data []byte) []int {
   172  	if (len(data) % 4) != 0 {
   173  		for i := 0; (len(data) % 4) != 0; i++ {
   174  			data = append(data, 0)
   175  		}
   176  	}
   177  	answer := []int{0x0000, 0x0000}
   178  	for i := 0; i < len(data); i += 4 {
   179  		answer[0] += (int(data[i]) << 8) + int(data[i+1])
   180  		answer[1] += (int(data[i+2]) << 8) + int(data[i+3])
   181  		answer[0] += answer[1] >> 16
   182  		answer[1] = answer[1] & 0xFFFF
   183  		answer[0] = answer[0] & 0xFFFF
   184  	}
   185  	return answer
   186  }
   187  
   188  func (utf *utf8FontFile) seek(shift int) {
   189  	_, _ = utf.fileReader.seek(int64(shift), 0)
   190  }
   191  
   192  func (utf *utf8FontFile) skip(delta int) {
   193  	_, _ = utf.fileReader.seek(int64(delta), 1)
   194  }
   195  
   196  // SeekTable position
   197  func (utf *utf8FontFile) SeekTable(name string) int {
   198  	return utf.seekTable(name, 0)
   199  }
   200  
   201  func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int {
   202  	_, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0)
   203  	return int(utf.fileReader.readerPosition)
   204  }
   205  
   206  func (utf *utf8FontFile) readInt16() int16 {
   207  	s := utf.fileReader.Read(2)
   208  	a := (int16(s[0]) << 8) + int16(s[1])
   209  	if (int(a) & (1 << 15)) == 0 {
   210  		a = int16(int(a) - (1 << 16))
   211  	}
   212  	return a
   213  }
   214  
   215  func (utf *utf8FontFile) getUint16(pos int) int {
   216  	_, _ = utf.fileReader.seek(int64(pos), 0)
   217  	s := utf.fileReader.Read(2)
   218  	return (int(s[0]) << 8) + int(s[1])
   219  }
   220  
   221  func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte {
   222  	stream = append([]byte{}, stream...)
   223  	return append(append(stream[:offset], value...), stream[offset+len(value):]...)
   224  }
   225  
   226  func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte {
   227  	up := make([]byte, 2)
   228  	binary.BigEndian.PutUint16(up, uint16(value))
   229  	return utf.splice(stream, offset, up)
   230  }
   231  
   232  func (utf *utf8FontFile) getRange(pos, length int) []byte {
   233  	_, _ = utf.fileReader.seek(int64(pos), 0)
   234  	if length < 1 {
   235  		return make([]byte, 0)
   236  	}
   237  	s := utf.fileReader.Read(length)
   238  	return s
   239  }
   240  
   241  func (utf *utf8FontFile) getTableData(name string) []byte {
   242  	desckrip := utf.tableDescriptions[name]
   243  	if desckrip == nil {
   244  		return nil
   245  	}
   246  	if desckrip.size == 0 {
   247  		return nil
   248  	}
   249  	_, _ = utf.fileReader.seek(int64(desckrip.position), 0)
   250  	s := utf.fileReader.Read(desckrip.size)
   251  	return s
   252  }
   253  
   254  func (utf *utf8FontFile) setOutTable(name string, data []byte) {
   255  	if data == nil {
   256  		return
   257  	}
   258  	if name == "head" {
   259  		data = utf.splice(data, 8, []byte{0, 0, 0, 0})
   260  	}
   261  	utf.outTablesData[name] = data
   262  }
   263  
   264  func arrayKeys(arr map[int]string) []int {
   265  	answer := make([]int, len(arr))
   266  	i := 0
   267  	for key := range arr {
   268  		answer[i] = key
   269  		i++
   270  	}
   271  	return answer
   272  }
   273  
   274  func inArray(s int, arr []int) bool {
   275  	for _, i := range arr {
   276  		if s == i {
   277  			return true
   278  		}
   279  	}
   280  	return false
   281  }
   282  
   283  func (utf *utf8FontFile) parseNAMETable() int {
   284  	namePosition := utf.SeekTable("name")
   285  	format := utf.readUint16()
   286  	if format != 0 {
   287  		fmt.Printf("Illegal format %d\n", format)
   288  		return format
   289  	}
   290  	nameCount := utf.readUint16()
   291  	stringDataPosition := namePosition + utf.readUint16()
   292  	names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""}
   293  	keys := arrayKeys(names)
   294  	counter := len(names)
   295  	for i := 0; i < nameCount; i++ {
   296  		system := utf.readUint16()
   297  		code := utf.readUint16()
   298  		local := utf.readUint16()
   299  		nameID := utf.readUint16()
   300  		size := utf.readUint16()
   301  		position := utf.readUint16()
   302  		if !inArray(nameID, keys) {
   303  			continue
   304  		}
   305  		currentName := ""
   306  		if system == 3 && code == 1 && local == 0x409 {
   307  			oldPos := utf.fileReader.readerPosition
   308  			utf.seek(stringDataPosition + position)
   309  			if size%2 != 0 {
   310  				fmt.Printf("name is not binar byte format\n")
   311  				return format
   312  			}
   313  			size /= 2
   314  			currentName = ""
   315  			for size > 0 {
   316  				char := utf.readUint16()
   317  				currentName += string(rune(char))
   318  				size--
   319  			}
   320  			utf.fileReader.readerPosition = oldPos
   321  			utf.seek(int(oldPos))
   322  		} else if system == 1 && code == 0 && local == 0 {
   323  			oldPos := utf.fileReader.readerPosition
   324  			currentName = string(utf.getRange(stringDataPosition+position, size))
   325  			utf.fileReader.readerPosition = oldPos
   326  			utf.seek(int(oldPos))
   327  		}
   328  		if currentName != "" && names[nameID] == "" {
   329  			names[nameID] = currentName
   330  			counter--
   331  			if counter == 0 {
   332  				break
   333  			}
   334  		}
   335  	}
   336  	return format
   337  }
   338  
   339  func (utf *utf8FontFile) parseHEADTable() {
   340  	utf.SeekTable("head")
   341  	utf.skip(18)
   342  	utf.fontElementSize = utf.readUint16()
   343  	scale := 1000.0 / float64(utf.fontElementSize)
   344  	utf.skip(16)
   345  	xMin := utf.readInt16()
   346  	yMin := utf.readInt16()
   347  	xMax := utf.readInt16()
   348  	yMax := utf.readInt16()
   349  	utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)}
   350  	utf.skip(3 * 2)
   351  	_ = utf.readUint16()
   352  	symbolDataFormat := utf.readUint16()
   353  	if symbolDataFormat != 0 {
   354  		fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat)
   355  		return
   356  	}
   357  }
   358  
   359  func (utf *utf8FontFile) parseHHEATable() int {
   360  	metricsCount := 0
   361  	if _, OK := utf.tableDescriptions["hhea"]; OK {
   362  		scale := 1000.0 / float64(utf.fontElementSize)
   363  		utf.SeekTable("hhea")
   364  		utf.skip(4)
   365  		hheaAscender := utf.readInt16()
   366  		hheaDescender := utf.readInt16()
   367  		utf.Ascent = int(float64(hheaAscender) * scale)
   368  		utf.Descent = int(float64(hheaDescender) * scale)
   369  		utf.skip(24)
   370  		metricDataFormat := utf.readUint16()
   371  		if metricDataFormat != 0 {
   372  			fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat)
   373  			return 0
   374  		}
   375  		metricsCount = utf.readUint16()
   376  		if metricsCount == 0 {
   377  			fmt.Printf("Number of horizontal metrics is 0\n")
   378  			return 0
   379  		}
   380  	}
   381  	return metricsCount
   382  }
   383  
   384  func (utf *utf8FontFile) parseOS2Table() int {
   385  	var weightType int
   386  	scale := 1000.0 / float64(utf.fontElementSize)
   387  	if _, OK := utf.tableDescriptions["OS/2"]; OK {
   388  		utf.SeekTable("OS/2")
   389  		version := utf.readUint16()
   390  		utf.skip(2)
   391  		weightType = utf.readUint16()
   392  		utf.skip(2)
   393  		fsType := utf.readUint16()
   394  		if fsType == 0x0002 || (fsType&0x0300) != 0 {
   395  			fmt.Printf("ERROR - copyright restrictions.\n")
   396  			return 0
   397  		}
   398  		utf.skip(20)
   399  		_ = utf.readInt16()
   400  
   401  		utf.skip(36)
   402  		sTypoAscender := utf.readInt16()
   403  		sTypoDescender := utf.readInt16()
   404  		if utf.Ascent == 0 {
   405  			utf.Ascent = int(float64(sTypoAscender) * scale)
   406  		}
   407  		if utf.Descent == 0 {
   408  			utf.Descent = int(float64(sTypoDescender) * scale)
   409  		}
   410  		if version > 1 {
   411  			utf.skip(16)
   412  			sCapHeight := utf.readInt16()
   413  			utf.CapHeight = int(float64(sCapHeight) * scale)
   414  		} else {
   415  			utf.CapHeight = utf.Ascent
   416  		}
   417  	} else {
   418  		weightType = 500
   419  		if utf.Ascent == 0 {
   420  			utf.Ascent = int(float64(utf.Bbox.Ymax) * scale)
   421  		}
   422  		if utf.Descent == 0 {
   423  			utf.Descent = int(float64(utf.Bbox.Ymin) * scale)
   424  		}
   425  		utf.CapHeight = utf.Ascent
   426  	}
   427  	utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2))
   428  	return weightType
   429  }
   430  
   431  func (utf *utf8FontFile) parsePOSTTable(weight int) {
   432  	utf.SeekTable("post")
   433  	utf.skip(4)
   434  	utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0
   435  	scale := 1000.0 / float64(utf.fontElementSize)
   436  	utf.UnderlinePosition = float64(utf.readInt16()) * scale
   437  	utf.UnderlineThickness = float64(utf.readInt16()) * scale
   438  	fixed := utf.readUint32()
   439  
   440  	utf.Flags = 4
   441  
   442  	if utf.ItalicAngle != 0 {
   443  		utf.Flags = utf.Flags | 64
   444  	}
   445  	if weight >= 600 {
   446  		utf.Flags = utf.Flags | 262144
   447  	}
   448  	if fixed != 0 {
   449  		utf.Flags = utf.Flags | 1
   450  	}
   451  }
   452  
   453  func (utf *utf8FontFile) parseCMAPTable(format int) int {
   454  	cmapPosition := utf.SeekTable("cmap")
   455  	utf.skip(2)
   456  	cmapTableCount := utf.readUint16()
   457  	cidCMAPPosition := 0
   458  	for i := 0; i < cmapTableCount; i++ {
   459  		system := utf.readUint16()
   460  		coded := utf.readUint16()
   461  		position := utf.readUint32()
   462  		oldReaderPosition := utf.fileReader.readerPosition
   463  		if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode
   464  			v := utf.getUint16(cmapPosition + position)
   465  			if v == 4 {
   466  				if cidCMAPPosition == 0 {
   467  					cidCMAPPosition = cmapPosition + position
   468  				}
   469  				break
   470  			}
   471  		}
   472  		utf.seek(int(oldReaderPosition))
   473  	}
   474  	if cidCMAPPosition == 0 {
   475  		fmt.Printf("Font does not have cmap for Unicode\n")
   476  		return cidCMAPPosition
   477  	}
   478  	return cidCMAPPosition
   479  }
   480  
   481  func (utf *utf8FontFile) parseTables() {
   482  	f := utf.parseNAMETable()
   483  	utf.parseHEADTable()
   484  	n := utf.parseHHEATable()
   485  	w := utf.parseOS2Table()
   486  	utf.parsePOSTTable(w)
   487  	runeCMAPPosition := utf.parseCMAPTable(f)
   488  
   489  	utf.SeekTable("maxp")
   490  	utf.skip(4)
   491  	numSymbols := utf.readUint16()
   492  
   493  	symbolCharDictionary := make(map[int][]int)
   494  	charSymbolDictionary := make(map[int]int)
   495  	utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary)
   496  
   497  	scale := 1000.0 / float64(utf.fontElementSize)
   498  	utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale)
   499  }
   500  
   501  func (utf *utf8FontFile) generateCMAP() map[int][]int {
   502  	cmapPosition := utf.SeekTable("cmap")
   503  	utf.skip(2)
   504  	cmapTableCount := utf.readUint16()
   505  	runeCmapPosition := 0
   506  	for i := 0; i < cmapTableCount; i++ {
   507  		system := utf.readUint16()
   508  		coder := utf.readUint16()
   509  		position := utf.readUint32()
   510  		oldPosition := utf.fileReader.readerPosition
   511  		if (system == 3 && coder == 1) || system == 0 {
   512  			format := utf.getUint16(cmapPosition + position)
   513  			if format == 4 {
   514  				runeCmapPosition = cmapPosition + position
   515  				break
   516  			}
   517  		}
   518  		utf.seek(int(oldPosition))
   519  	}
   520  
   521  	if runeCmapPosition == 0 {
   522  		fmt.Printf("Font does not have cmap for Unicode\n")
   523  		return nil
   524  	}
   525  
   526  	symbolCharDictionary := make(map[int][]int)
   527  	charSymbolDictionary := make(map[int]int)
   528  	utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary)
   529  
   530  	utf.charSymbolDictionary = charSymbolDictionary
   531  
   532  	return symbolCharDictionary
   533  }
   534  
   535  func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) {
   536  	symbolCollection := map[int]int{0: 0}
   537  	charSymbolPairCollection := make(map[int]int)
   538  	for _, char := range usedRunes {
   539  		if _, OK := utf.charSymbolDictionary[char]; OK {
   540  			symbolCollection[utf.charSymbolDictionary[char]] = char
   541  			charSymbolPairCollection[char] = utf.charSymbolDictionary[char]
   542  
   543  		}
   544  		utf.LastRune = max(utf.LastRune, char)
   545  	}
   546  
   547  	begin := utf.tableDescriptions["glyf"].position
   548  
   549  	symbolArray := make(map[int]int)
   550  	symbolCollectionKeys := keySortInt(symbolCollection)
   551  
   552  	symbolCounter := 0
   553  	maxRune := 0
   554  	for _, oldSymbolIndex := range symbolCollectionKeys {
   555  		maxRune = max(maxRune, symbolCollection[oldSymbolIndex])
   556  		symbolArray[oldSymbolIndex] = symbolCounter
   557  		symbolCounter++
   558  	}
   559  	charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection)
   560  	runeSymbolPairCollection := make(map[int]int)
   561  	for _, runa := range charSymbolPairCollectionKeys {
   562  		runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]]
   563  	}
   564  	utf.CodeSymbolDictionary = runeSymbolPairCollection
   565  
   566  	symbolCollectionKeys = keySortInt(symbolCollection)
   567  	for _, oldSymbolIndex := range symbolCollectionKeys {
   568  		_, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys)
   569  	}
   570  
   571  	return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys
   572  }
   573  
   574  func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte {
   575  	cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection)
   576  	cidID := 0
   577  	cidArray := make(map[int][]int)
   578  	prevCid := -2
   579  	prevSymbol := -1
   580  	for _, cid := range cidSymbolPairCollectionKeys {
   581  		if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) {
   582  			if n, OK := cidArray[cidID]; !OK || n == nil {
   583  				cidArray[cidID] = make([]int, 0)
   584  			}
   585  			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
   586  		} else {
   587  			cidID = cid
   588  			cidArray[cidID] = make([]int, 0)
   589  			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
   590  		}
   591  		prevCid = cid
   592  		prevSymbol = cidSymbolPairCollection[cid]
   593  	}
   594  	cidArrayKeys := keySortArrayRangeMap(cidArray)
   595  	segCount := len(cidArray) + 1
   596  
   597  	searchRange := 1
   598  	entrySelector := 0
   599  	for searchRange*2 <= segCount {
   600  		searchRange = searchRange * 2
   601  		entrySelector = entrySelector + 1
   602  	}
   603  	searchRange = searchRange * 2
   604  	rangeShift := segCount*2 - searchRange
   605  	length := 16 + (8 * segCount) + (numSymbols + 1)
   606  	cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift}
   607  
   608  	for _, start := range cidArrayKeys {
   609  		endCode := start + (len(cidArray[start]) - 1)
   610  		cmap = append(cmap, endCode)
   611  	}
   612  	cmap = append(cmap, 0xFFFF)
   613  	cmap = append(cmap, 0)
   614  
   615  	cmap = append(cmap, cidArrayKeys...)
   616  	cmap = append(cmap, 0xFFFF)
   617  	for _, cidKey := range cidArrayKeys {
   618  		idDelta := -(cidKey - cidArray[cidKey][0])
   619  		cmap = append(cmap, idDelta)
   620  	}
   621  	cmap = append(cmap, 1)
   622  	for range cidArray {
   623  		cmap = append(cmap, 0)
   624  
   625  	}
   626  	cmap = append(cmap, 0)
   627  	for _, start := range cidArrayKeys {
   628  		cmap = append(cmap, cidArray[start]...)
   629  	}
   630  	cmap = append(cmap, 0)
   631  	cmapstr := make([]byte, 0)
   632  	for _, cm := range cmap {
   633  		cmapstr = append(cmapstr, packUint16(cm)...)
   634  	}
   635  	return cmapstr
   636  }
   637  
   638  // GenerateCutFont fill utf8FontFile from .utf file, only with runes from usedRunes
   639  func (utf *utf8FontFile) GenerateCutFont(usedRunes map[int]int) []byte {
   640  	utf.fileReader.readerPosition = 0
   641  	utf.symbolPosition = make([]int, 0)
   642  	utf.charSymbolDictionary = make(map[int]int)
   643  	utf.tableDescriptions = make(map[string]*tableDescription)
   644  	utf.outTablesData = make(map[string][]byte)
   645  	utf.Ascent = 0
   646  	utf.Descent = 0
   647  	utf.skip(4)
   648  	utf.LastRune = 0
   649  	utf.generateTableDescriptions()
   650  
   651  	utf.SeekTable("head")
   652  	utf.skip(50)
   653  	LocaFormat := utf.readUint16()
   654  
   655  	utf.SeekTable("hhea")
   656  	utf.skip(34)
   657  	metricsCount := utf.readUint16()
   658  	oldMetrics := metricsCount
   659  
   660  	utf.SeekTable("maxp")
   661  	utf.skip(4)
   662  	numSymbols := utf.readUint16()
   663  
   664  	symbolCharDictionary := utf.generateCMAP()
   665  	if symbolCharDictionary == nil {
   666  		return nil
   667  	}
   668  
   669  	utf.parseHMTXTable(metricsCount, numSymbols, symbolCharDictionary, 1.0)
   670  
   671  	utf.parseLOCATable(LocaFormat, numSymbols)
   672  
   673  	cidSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys := utf.parseSymbols(usedRunes)
   674  
   675  	metricsCount = len(symbolCollection)
   676  	numSymbols = metricsCount
   677  
   678  	utf.setOutTable("name", utf.getTableData("name"))
   679  	utf.setOutTable("cvt ", utf.getTableData("cvt "))
   680  	utf.setOutTable("fpgm", utf.getTableData("fpgm"))
   681  	utf.setOutTable("prep", utf.getTableData("prep"))
   682  	utf.setOutTable("gasp", utf.getTableData("gasp"))
   683  
   684  	postTable := utf.getTableData("post")
   685  	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}...)
   686  	utf.setOutTable("post", postTable)
   687  
   688  	delete(cidSymbolPairCollection, 0)
   689  
   690  	utf.setOutTable("cmap", utf.generateCMAPTable(cidSymbolPairCollection, numSymbols))
   691  
   692  	symbolData := utf.getTableData("glyf")
   693  
   694  	offsets := make([]int, 0)
   695  	glyfData := make([]byte, 0)
   696  	pos := 0
   697  	hmtxData := make([]byte, 0)
   698  	utf.symbolData = make(map[int]map[string][]int)
   699  
   700  	for _, originalSymbolIdx := range symbolCollectionKeys {
   701  		hm := utf.getMetrics(oldMetrics, originalSymbolIdx)
   702  		hmtxData = append(hmtxData, hm...)
   703  
   704  		offsets = append(offsets, pos)
   705  		symbolPos := utf.symbolPosition[originalSymbolIdx]
   706  		symbolLen := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
   707  		data := symbolData[symbolPos : symbolPos+symbolLen]
   708  		var up int
   709  		if symbolLen > 0 {
   710  			up = unpackUint16(data[0:2])
   711  		}
   712  
   713  		if symbolLen > 2 && (up&(1<<15)) != 0 {
   714  			posInSymbol := 10
   715  			flags := symbolContinue
   716  			nComponentElements := 0
   717  			for (flags & symbolContinue) != 0 {
   718  				nComponentElements++
   719  				up = unpackUint16(data[posInSymbol : posInSymbol+2])
   720  				flags = up
   721  				up = unpackUint16(data[posInSymbol+2 : posInSymbol+4])
   722  				symbolIdx := up
   723  				if _, OK := utf.symbolData[originalSymbolIdx]; !OK {
   724  					utf.symbolData[originalSymbolIdx] = make(map[string][]int)
   725  				}
   726  				if _, OK := utf.symbolData[originalSymbolIdx]["compSymbols"]; !OK {
   727  					utf.symbolData[originalSymbolIdx]["compSymbols"] = make([]int, 0)
   728  				}
   729  				utf.symbolData[originalSymbolIdx]["compSymbols"] = append(utf.symbolData[originalSymbolIdx]["compSymbols"], symbolIdx)
   730  				data = utf.insertUint16(data, posInSymbol+2, symbolArray[symbolIdx])
   731  				posInSymbol += 4
   732  				if (flags & symbolWords) != 0 {
   733  					posInSymbol += 4
   734  				} else {
   735  					posInSymbol += 2
   736  				}
   737  				if (flags & symbolScale) != 0 {
   738  					posInSymbol += 2
   739  				} else if (flags & symbolAllScale) != 0 {
   740  					posInSymbol += 4
   741  				} else if (flags & symbol2x2) != 0 {
   742  					posInSymbol += 8
   743  				}
   744  			}
   745  		}
   746  
   747  		glyfData = append(glyfData, data...)
   748  		pos += symbolLen
   749  		if pos%4 != 0 {
   750  			padding := 4 - (pos % 4)
   751  			glyfData = append(glyfData, make([]byte, padding)...)
   752  			pos += padding
   753  		}
   754  	}
   755  
   756  	offsets = append(offsets, pos)
   757  	utf.setOutTable("glyf", glyfData)
   758  
   759  	utf.setOutTable("hmtx", hmtxData)
   760  
   761  	locaData := make([]byte, 0)
   762  	if ((pos + 1) >> 1) > 0xFFFF {
   763  		LocaFormat = 1
   764  		for _, offset := range offsets {
   765  			locaData = append(locaData, packUint32(offset)...)
   766  		}
   767  	} else {
   768  		LocaFormat = 0
   769  		for _, offset := range offsets {
   770  			locaData = append(locaData, packUint16(offset/2)...)
   771  		}
   772  	}
   773  	utf.setOutTable("loca", locaData)
   774  
   775  	headData := utf.getTableData("head")
   776  	headData = utf.insertUint16(headData, 50, LocaFormat)
   777  	utf.setOutTable("head", headData)
   778  
   779  	hheaData := utf.getTableData("hhea")
   780  	hheaData = utf.insertUint16(hheaData, 34, metricsCount)
   781  	utf.setOutTable("hhea", hheaData)
   782  
   783  	maxp := utf.getTableData("maxp")
   784  	maxp = utf.insertUint16(maxp, 4, numSymbols)
   785  	utf.setOutTable("maxp", maxp)
   786  
   787  	os2Data := utf.getTableData("OS/2")
   788  	utf.setOutTable("OS/2", os2Data)
   789  
   790  	return utf.assembleTables()
   791  }
   792  
   793  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) {
   794  	symbolPos := utf.symbolPosition[originalSymbolIdx]
   795  	symbolSize := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
   796  	if symbolSize == 0 {
   797  		return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
   798  	}
   799  	utf.seek(*start + symbolPos)
   800  
   801  	lineCount := utf.readInt16()
   802  
   803  	if lineCount < 0 {
   804  		utf.skip(8)
   805  		flags := symbolContinue
   806  		for flags&symbolContinue != 0 {
   807  			flags = utf.readUint16()
   808  			symbolIndex := utf.readUint16()
   809  			if _, OK := symbolSet[symbolIndex]; !OK {
   810  				symbolSet[symbolIndex] = len(SymbolsCollection)
   811  				SymbolsCollection[symbolIndex] = 1
   812  				SymbolsCollectionKeys = append(SymbolsCollectionKeys, symbolIndex)
   813  			}
   814  			oldPosition, _ := utf.fileReader.seek(0, 1)
   815  			_, _, _, SymbolsCollectionKeys = utf.getSymbols(symbolIndex, start, symbolSet, SymbolsCollection, SymbolsCollectionKeys)
   816  			utf.seek(int(oldPosition))
   817  			if flags&symbolWords != 0 {
   818  				utf.skip(4)
   819  			} else {
   820  				utf.skip(2)
   821  			}
   822  			if flags&symbolScale != 0 {
   823  				utf.skip(2)
   824  			} else if flags&symbolAllScale != 0 {
   825  				utf.skip(4)
   826  			} else if flags&symbol2x2 != 0 {
   827  				utf.skip(8)
   828  			}
   829  		}
   830  	}
   831  	return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
   832  }
   833  
   834  func (utf *utf8FontFile) parseHMTXTable(numberOfHMetrics, numSymbols int, symbolToChar map[int][]int, scale float64) {
   835  	var widths int
   836  	start := utf.SeekTable("hmtx")
   837  	arrayWidths := 0
   838  	var arr []int
   839  	utf.CharWidths = make([]int, 256*256)
   840  	charCount := 0
   841  	arr = unpackUint16Array(utf.getRange(start, numberOfHMetrics*4))
   842  	for symbol := 0; symbol < numberOfHMetrics; symbol++ {
   843  		arrayWidths = arr[(symbol*2)+1]
   844  		if _, OK := symbolToChar[symbol]; OK || symbol == 0 {
   845  
   846  			if arrayWidths >= (1 << 15) {
   847  				arrayWidths = 0
   848  			}
   849  			if symbol == 0 {
   850  				utf.DefaultWidth = scale * float64(arrayWidths)
   851  				continue
   852  			}
   853  			for _, char := range symbolToChar[symbol] {
   854  				if char != 0 && char != 65535 {
   855  					widths = int(math.Round(scale * float64(arrayWidths)))
   856  					if widths == 0 {
   857  						widths = 65535
   858  					}
   859  					if char < 196608 {
   860  						utf.CharWidths[char] = widths
   861  						charCount++
   862  					}
   863  				}
   864  			}
   865  		}
   866  	}
   867  	diff := numSymbols - numberOfHMetrics
   868  	for pos := 0; pos < diff; pos++ {
   869  		symbol := pos + numberOfHMetrics
   870  		if _, OK := symbolToChar[symbol]; OK {
   871  			for _, char := range symbolToChar[symbol] {
   872  				if char != 0 && char != 65535 {
   873  					widths = int(math.Round(scale * float64(arrayWidths)))
   874  					if widths == 0 {
   875  						widths = 65535
   876  					}
   877  					if char < 196608 {
   878  						utf.CharWidths[char] = widths
   879  						charCount++
   880  					}
   881  				}
   882  			}
   883  		}
   884  	}
   885  	utf.CharWidths[0] = charCount
   886  }
   887  
   888  func (utf *utf8FontFile) getMetrics(metricCount, gid int) []byte {
   889  	start := utf.SeekTable("hmtx")
   890  	var metrics []byte
   891  	if gid < metricCount {
   892  		utf.seek(start + (gid * 4))
   893  		metrics = utf.fileReader.Read(4)
   894  	} else {
   895  		utf.seek(start + ((metricCount - 1) * 4))
   896  		metrics = utf.fileReader.Read(2)
   897  		utf.seek(start + (metricCount * 2) + (gid * 2))
   898  		metrics = append(metrics, utf.fileReader.Read(2)...)
   899  	}
   900  	return metrics
   901  }
   902  
   903  func (utf *utf8FontFile) parseLOCATable(format, numSymbols int) {
   904  	start := utf.SeekTable("loca")
   905  	utf.symbolPosition = make([]int, 0)
   906  	if format == 0 {
   907  		data := utf.getRange(start, (numSymbols*2)+2)
   908  		arr := unpackUint16Array(data)
   909  		for n := 0; n <= numSymbols; n++ {
   910  			utf.symbolPosition = append(utf.symbolPosition, arr[n+1]*2)
   911  		}
   912  	} else if format == 1 {
   913  		data := utf.getRange(start, (numSymbols*4)+4)
   914  		arr := unpackUint32Array(data)
   915  		for n := 0; n <= numSymbols; n++ {
   916  			utf.symbolPosition = append(utf.symbolPosition, arr[n+1])
   917  		}
   918  	} else {
   919  		fmt.Printf("Unknown loca table format %d\n", format)
   920  		return
   921  	}
   922  }
   923  
   924  func (utf *utf8FontFile) generateSCCSDictionaries(runeCmapPosition int, symbolCharDictionary map[int][]int, charSymbolDictionary map[int]int) {
   925  	maxRune := 0
   926  	utf.seek(runeCmapPosition + 2)
   927  	size := utf.readUint16()
   928  	rim := runeCmapPosition + size
   929  	utf.skip(2)
   930  
   931  	segmentSize := utf.readUint16() / 2
   932  	utf.skip(6)
   933  	completers := make([]int, 0)
   934  	for i := 0; i < segmentSize; i++ {
   935  		completers = append(completers, utf.readUint16())
   936  	}
   937  	utf.skip(2)
   938  	beginners := make([]int, 0)
   939  	for i := 0; i < segmentSize; i++ {
   940  		beginners = append(beginners, utf.readUint16())
   941  	}
   942  	sizes := make([]int, 0)
   943  	for i := 0; i < segmentSize; i++ {
   944  		sizes = append(sizes, int(utf.readInt16()))
   945  	}
   946  	readerPositionStart := utf.fileReader.readerPosition
   947  	positions := make([]int, 0)
   948  	for i := 0; i < segmentSize; i++ {
   949  		positions = append(positions, utf.readUint16())
   950  	}
   951  	var symbol int
   952  	for n := 0; n < segmentSize; n++ {
   953  		completePosition := completers[n] + 1
   954  		for char := beginners[n]; char < completePosition; char++ {
   955  			if positions[n] == 0 {
   956  				symbol = (char + sizes[n]) & 0xFFFF
   957  			} else {
   958  				position := (char-beginners[n])*2 + positions[n]
   959  				position = int(readerPositionStart) + 2*n + position
   960  				if position >= rim {
   961  					symbol = 0
   962  				} else {
   963  					symbol = utf.getUint16(position)
   964  					if symbol != 0 {
   965  						symbol = (symbol + sizes[n]) & 0xFFFF
   966  					}
   967  				}
   968  			}
   969  			charSymbolDictionary[char] = symbol
   970  			if char < 196608 {
   971  				maxRune = max(char, maxRune)
   972  			}
   973  			symbolCharDictionary[symbol] = append(symbolCharDictionary[symbol], char)
   974  		}
   975  	}
   976  }
   977  
   978  func max(i, n int) int {
   979  	if n > i {
   980  		return n
   981  	}
   982  	return i
   983  }
   984  
   985  func (utf *utf8FontFile) assembleTables() []byte {
   986  	answer := make([]byte, 0)
   987  	tablesCount := len(utf.outTablesData)
   988  	findSize := 1
   989  	writer := 0
   990  	for findSize*2 <= tablesCount {
   991  		findSize = findSize * 2
   992  		writer = writer + 1
   993  	}
   994  	findSize = findSize * 16
   995  	rOffset := tablesCount*16 - findSize
   996  
   997  	answer = append(answer, packHeader(0x00010000, tablesCount, findSize, writer, rOffset)...)
   998  
   999  	tables := make(map[string][]byte, len(utf.outTablesData))
  1000  	for k, v := range utf.outTablesData {
  1001  		tables[k] = make([]byte, len(v))
  1002  		copy(tables[k], v)
  1003  	}
  1004  	tablesNames := keySortStrings(tables)
  1005  
  1006  	offset := 12 + tablesCount*16
  1007  	begin := 0
  1008  
  1009  	for _, name := range tablesNames {
  1010  		if name == "head" {
  1011  			begin = offset
  1012  		}
  1013  		answer = append(answer, []byte(name)...)
  1014  		checksum := utf.generateChecksum(tables[name])
  1015  		answer = append(answer, pack2Uint16(checksum[0], checksum[1])...)
  1016  		answer = append(answer, pack2Uint32(offset, len(tables[name]))...)
  1017  		paddedLength := (len(tables[name]) + 3) &^ 3
  1018  		offset = offset + paddedLength
  1019  	}
  1020  
  1021  	for _, key := range tablesNames {
  1022  		data := append([]byte{}, tables[key]...)
  1023  		data = append(data, []byte{0, 0, 0}...)
  1024  		answer = append(answer, data[:(len(data)&^3)]...)
  1025  	}
  1026  
  1027  	checksum := utf.generateChecksum([]byte(answer))
  1028  	checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum)
  1029  	answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1]))
  1030  	return answer
  1031  }
  1032  
  1033  func unpackUint16Array(data []byte) []int {
  1034  	answer := make([]int, 1)
  1035  	r := bytes.NewReader(data)
  1036  	bs := make([]byte, 2)
  1037  	var e error
  1038  	var c int
  1039  	c, e = r.Read(bs)
  1040  	for e == nil && c > 0 {
  1041  		answer = append(answer, int(binary.BigEndian.Uint16(bs)))
  1042  		c, e = r.Read(bs)
  1043  	}
  1044  	return answer
  1045  }
  1046  
  1047  func unpackUint32Array(data []byte) []int {
  1048  	answer := make([]int, 1)
  1049  	r := bytes.NewReader(data)
  1050  	bs := make([]byte, 4)
  1051  	var e error
  1052  	var c int
  1053  	c, e = r.Read(bs)
  1054  	for e == nil && c > 0 {
  1055  		answer = append(answer, int(binary.BigEndian.Uint32(bs)))
  1056  		c, e = r.Read(bs)
  1057  	}
  1058  	return answer
  1059  }
  1060  
  1061  func unpackUint16(data []byte) int {
  1062  	return int(binary.BigEndian.Uint16(data))
  1063  }
  1064  
  1065  func packHeader(N uint32, n1, n2, n3, n4 int) []byte {
  1066  	answer := make([]byte, 0)
  1067  	bs4 := make([]byte, 4)
  1068  	binary.BigEndian.PutUint32(bs4, N)
  1069  	answer = append(answer, bs4...)
  1070  	bs := make([]byte, 2)
  1071  	binary.BigEndian.PutUint16(bs, uint16(n1))
  1072  	answer = append(answer, bs...)
  1073  	binary.BigEndian.PutUint16(bs, uint16(n2))
  1074  	answer = append(answer, bs...)
  1075  	binary.BigEndian.PutUint16(bs, uint16(n3))
  1076  	answer = append(answer, bs...)
  1077  	binary.BigEndian.PutUint16(bs, uint16(n4))
  1078  	answer = append(answer, bs...)
  1079  	return answer
  1080  }
  1081  
  1082  func pack2Uint16(n1, n2 int) []byte {
  1083  	answer := make([]byte, 0)
  1084  	bs := make([]byte, 2)
  1085  	binary.BigEndian.PutUint16(bs, uint16(n1))
  1086  	answer = append(answer, bs...)
  1087  	binary.BigEndian.PutUint16(bs, uint16(n2))
  1088  	answer = append(answer, bs...)
  1089  	return answer
  1090  }
  1091  
  1092  func pack2Uint32(n1, n2 int) []byte {
  1093  	answer := make([]byte, 0)
  1094  	bs := make([]byte, 4)
  1095  	binary.BigEndian.PutUint32(bs, uint32(n1))
  1096  	answer = append(answer, bs...)
  1097  	binary.BigEndian.PutUint32(bs, uint32(n2))
  1098  	answer = append(answer, bs...)
  1099  	return answer
  1100  }
  1101  
  1102  func packUint32(n1 int) []byte {
  1103  	bs := make([]byte, 4)
  1104  	binary.BigEndian.PutUint32(bs, uint32(n1))
  1105  	return bs
  1106  }
  1107  
  1108  func packUint16(n1 int) []byte {
  1109  	bs := make([]byte, 2)
  1110  	binary.BigEndian.PutUint16(bs, uint16(n1))
  1111  	return bs
  1112  }
  1113  
  1114  func keySortStrings(s map[string][]byte) []string {
  1115  	keys := make([]string, len(s))
  1116  	i := 0
  1117  	for key := range s {
  1118  		keys[i] = key
  1119  		i++
  1120  	}
  1121  	sort.Strings(keys)
  1122  	return keys
  1123  }
  1124  
  1125  func keySortInt(s map[int]int) []int {
  1126  	keys := make([]int, len(s))
  1127  	i := 0
  1128  	for key := range s {
  1129  		keys[i] = key
  1130  		i++
  1131  	}
  1132  	sort.Ints(keys)
  1133  	return keys
  1134  }
  1135  
  1136  func keySortArrayRangeMap(s map[int][]int) []int {
  1137  	keys := make([]int, len(s))
  1138  	i := 0
  1139  	for key := range s {
  1140  		keys[i] = key
  1141  		i++
  1142  	}
  1143  	sort.Ints(keys)
  1144  	return keys
  1145  }
  1146  
  1147  // UTF8CutFont is a utility function that generates a TrueType font composed
  1148  // only of the runes included in cutset. The rune glyphs are copied from This
  1149  // function is demonstrated in ExampleUTF8CutFont().
  1150  func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) {
  1151  	f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf})
  1152  	runes := map[int]int{}
  1153  	for i, r := range cutset {
  1154  		runes[i] = int(r)
  1155  	}
  1156  	outBuf = f.GenerateCutFont(runes)
  1157  	return
  1158  }