1/*
2 * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy)
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17package gofpdf
18
19import (
20	"bytes"
21	"encoding/binary"
22	"fmt"
23	"math"
24	"sort"
25)
26
27// flags
28const symbolWords = 1 << 0
29const symbolScale = 1 << 3
30const symbolContinue = 1 << 5
31const symbolAllScale = 1 << 6
32const symbol2x2 = 1 << 7
33
34// CID map Init
35const 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"
36
37type utf8FontFile struct {
38	fileReader           *fileReader
39	LastRune             int
40	tableDescriptions    map[string]*tableDescription
41	outTablesData        map[string][]byte
42	symbolPosition       []int
43	charSymbolDictionary map[int]int
44	Ascent               int
45	Descent              int
46	fontElementSize      int
47	Bbox                 fontBoxType
48	CapHeight            int
49	StemV                int
50	ItalicAngle          int
51	Flags                int
52	UnderlinePosition    float64
53	UnderlineThickness   float64
54	CharWidths           []int
55	DefaultWidth         float64
56	symbolData           map[int]map[string][]int
57	CodeSymbolDictionary map[int]int
58}
59
60type tableDescription struct {
61	name     string
62	checksum []int
63	position int
64	size     int
65}
66
67type fileReader struct {
68	readerPosition int64
69	array          []byte
70}
71
72func (fr *fileReader) Read(s int) []byte {
73	b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)]
74	fr.readerPosition += int64(s)
75	return b
76}
77
78func (fr *fileReader) seek(shift int64, flag int) (int64, error) {
79	if flag == 0 {
80		fr.readerPosition = shift
81	} else if flag == 1 {
82		fr.readerPosition += shift
83	} else if flag == 2 {
84		fr.readerPosition = int64(len(fr.array)) - shift
85	}
86	return int64(fr.readerPosition), nil
87}
88
89func newUTF8Font(reader *fileReader) *utf8FontFile {
90	utf := utf8FontFile{
91		fileReader: reader,
92	}
93	return &utf
94}
95
96func (utf *utf8FontFile) parseFile() error {
97	utf.fileReader.readerPosition = 0
98	utf.symbolPosition = make([]int, 0)
99	utf.charSymbolDictionary = make(map[int]int)
100	utf.tableDescriptions = make(map[string]*tableDescription)
101	utf.outTablesData = make(map[string][]byte)
102	utf.Ascent = 0
103	utf.Descent = 0
104	codeType := uint32(utf.readUint32())
105	if codeType == 0x4F54544F {
106		return fmt.Errorf("not supported\n ")
107	}
108	if codeType == 0x74746366 {
109		return fmt.Errorf("not supported\n ")
110	}
111	if codeType != 0x00010000 && codeType != 0x74727565 {
112		return fmt.Errorf("Not a TrueType font: codeType=%v\n ", codeType)
113	}
114	utf.generateTableDescriptions()
115	utf.parseTables()
116	return nil
117}
118
119func (utf *utf8FontFile) generateTableDescriptions() {
120
121	tablesCount := utf.readUint16()
122	_ = utf.readUint16()
123	_ = utf.readUint16()
124	_ = utf.readUint16()
125	utf.tableDescriptions = make(map[string]*tableDescription)
126
127	for i := 0; i < tablesCount; i++ {
128		record := tableDescription{
129			name:     utf.readTableName(),
130			checksum: []int{utf.readUint16(), utf.readUint16()},
131			position: utf.readUint32(),
132			size:     utf.readUint32(),
133		}
134		utf.tableDescriptions[record.name] = &record
135	}
136}
137
138func (utf *utf8FontFile) readTableName() string {
139	return string(utf.fileReader.Read(4))
140}
141
142func (utf *utf8FontFile) readUint16() int {
143	s := utf.fileReader.Read(2)
144	return (int(s[0]) << 8) + int(s[1])
145}
146
147func (utf *utf8FontFile) readUint32() int {
148	s := utf.fileReader.Read(4)
149	return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 	16777216  = 1<<24
150}
151
152func (utf *utf8FontFile) calcInt32(x, y []int) []int {
153	answer := make([]int, 2)
154	if y[1] > x[1] {
155		x[1] += 1 << 16
156		x[0]++
157	}
158	answer[1] = x[1] - y[1]
159	if y[0] > x[0] {
160		x[0] += 1 << 16
161	}
162	answer[0] = x[0] - y[0]
163	answer[0] = answer[0] & 0xFFFF
164	return answer
165}
166
167func (utf *utf8FontFile) generateChecksum(data []byte) []int {
168	if (len(data) % 4) != 0 {
169		for i := 0; (len(data) % 4) != 0; i++ {
170			data = append(data, 0)
171		}
172	}
173	answer := []int{0x0000, 0x0000}
174	for i := 0; i < len(data); i += 4 {
175		answer[0] += (int(data[i]) << 8) + int(data[i+1])
176		answer[1] += (int(data[i+2]) << 8) + int(data[i+3])
177		answer[0] += answer[1] >> 16
178		answer[1] = answer[1] & 0xFFFF
179		answer[0] = answer[0] & 0xFFFF
180	}
181	return answer
182}
183
184func (utf *utf8FontFile) seek(shift int) {
185	_, _ = utf.fileReader.seek(int64(shift), 0)
186}
187
188func (utf *utf8FontFile) skip(delta int) {
189	_, _ = utf.fileReader.seek(int64(delta), 1)
190}
191
192//SeekTable position
193func (utf *utf8FontFile) SeekTable(name string) int {
194	return utf.seekTable(name, 0)
195}
196
197func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int {
198	_, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0)
199	return int(utf.fileReader.readerPosition)
200}
201
202func (utf *utf8FontFile) readInt16() int16 {
203	s := utf.fileReader.Read(2)
204	a := (int16(s[0]) << 8) + int16(s[1])
205	if (int(a) & (1 << 15)) == 0 {
206		a = int16(int(a) - (1 << 16))
207	}
208	return a
209}
210
211func (utf *utf8FontFile) getUint16(pos int) int {
212	_, _ = utf.fileReader.seek(int64(pos), 0)
213	s := utf.fileReader.Read(2)
214	return (int(s[0]) << 8) + int(s[1])
215}
216
217func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte {
218	stream = append([]byte{}, stream...)
219	return append(append(stream[:offset], value...), stream[offset+len(value):]...)
220}
221
222func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte {
223	up := make([]byte, 2)
224	binary.BigEndian.PutUint16(up, uint16(value))
225	return utf.splice(stream, offset, up)
226}
227
228func (utf *utf8FontFile) getRange(pos, length int) []byte {
229	_, _ = utf.fileReader.seek(int64(pos), 0)
230	if length < 1 {
231		return make([]byte, 0)
232	}
233	s := utf.fileReader.Read(length)
234	return s
235}
236
237func (utf *utf8FontFile) getTableData(name string) []byte {
238	desckrip := utf.tableDescriptions[name]
239	if desckrip == nil {
240		return nil
241	}
242	if desckrip.size == 0 {
243		return nil
244	}
245	_, _ = utf.fileReader.seek(int64(desckrip.position), 0)
246	s := utf.fileReader.Read(desckrip.size)
247	return s
248}
249
250func (utf *utf8FontFile) setOutTable(name string, data []byte) {
251	if data == nil {
252		return
253	}
254	if name == "head" {
255		data = utf.splice(data, 8, []byte{0, 0, 0, 0})
256	}
257	utf.outTablesData[name] = data
258}
259
260func arrayKeys(arr map[int]string) []int {
261	answer := make([]int, len(arr))
262	i := 0
263	for key := range arr {
264		answer[i] = key
265		i++
266	}
267	return answer
268}
269
270func inArray(s int, arr []int) bool {
271	for _, i := range arr {
272		if s == i {
273			return true
274		}
275	}
276	return false
277}
278
279func (utf *utf8FontFile) parseNAMETable() int {
280	namePosition := utf.SeekTable("name")
281	format := utf.readUint16()
282	if format != 0 {
283		fmt.Printf("Illegal format %d\n", format)
284		return format
285	}
286	nameCount := utf.readUint16()
287	stringDataPosition := namePosition + utf.readUint16()
288	names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""}
289	keys := arrayKeys(names)
290	counter := len(names)
291	for i := 0; i < nameCount; i++ {
292		system := utf.readUint16()
293		code := utf.readUint16()
294		local := utf.readUint16()
295		nameID := utf.readUint16()
296		size := utf.readUint16()
297		position := utf.readUint16()
298		if !inArray(nameID, keys) {
299			continue
300		}
301		currentName := ""
302		if system == 3 && code == 1 && local == 0x409 {
303			oldPos := utf.fileReader.readerPosition
304			utf.seek(stringDataPosition + position)
305			if size%2 != 0 {
306				fmt.Printf("name is not binar byte format\n")
307				return format
308			}
309			size /= 2
310			currentName = ""
311			for size > 0 {
312				char := utf.readUint16()
313				currentName += string(rune(char))
314				size--
315			}
316			utf.fileReader.readerPosition = oldPos
317			utf.seek(int(oldPos))
318		} else if system == 1 && code == 0 && local == 0 {
319			oldPos := utf.fileReader.readerPosition
320			currentName = string(utf.getRange(stringDataPosition+position, size))
321			utf.fileReader.readerPosition = oldPos
322			utf.seek(int(oldPos))
323		}
324		if currentName != "" && names[nameID] == "" {
325			names[nameID] = currentName
326			counter--
327			if counter == 0 {
328				break
329			}
330		}
331	}
332	return format
333}
334
335func (utf *utf8FontFile) parseHEADTable() {
336	utf.SeekTable("head")
337	utf.skip(18)
338	utf.fontElementSize = utf.readUint16()
339	scale := 1000.0 / float64(utf.fontElementSize)
340	utf.skip(16)
341	xMin := utf.readInt16()
342	yMin := utf.readInt16()
343	xMax := utf.readInt16()
344	yMax := utf.readInt16()
345	utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)}
346	utf.skip(3 * 2)
347	_ = utf.readUint16()
348	symbolDataFormat := utf.readUint16()
349	if symbolDataFormat != 0 {
350		fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat)
351		return
352	}
353}
354
355func (utf *utf8FontFile) parseHHEATable() int {
356	metricsCount := 0
357	if _, OK := utf.tableDescriptions["hhea"]; OK {
358		scale := 1000.0 / float64(utf.fontElementSize)
359		utf.SeekTable("hhea")
360		utf.skip(4)
361		hheaAscender := utf.readInt16()
362		hheaDescender := utf.readInt16()
363		utf.Ascent = int(float64(hheaAscender) * scale)
364		utf.Descent = int(float64(hheaDescender) * scale)
365		utf.skip(24)
366		metricDataFormat := utf.readUint16()
367		if metricDataFormat != 0 {
368			fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat)
369			return 0
370		}
371		metricsCount = utf.readUint16()
372		if metricsCount == 0 {
373			fmt.Printf("Number of horizontal metrics is 0\n")
374			return 0
375		}
376	}
377	return metricsCount
378}
379
380func (utf *utf8FontFile) parseOS2Table() int {
381	var weightType int
382	scale := 1000.0 / float64(utf.fontElementSize)
383	if _, OK := utf.tableDescriptions["OS/2"]; OK {
384		utf.SeekTable("OS/2")
385		version := utf.readUint16()
386		utf.skip(2)
387		weightType = utf.readUint16()
388		utf.skip(2)
389		fsType := utf.readUint16()
390		if fsType == 0x0002 || (fsType&0x0300) != 0 {
391			fmt.Printf("ERROR - copyright restrictions.\n")
392			return 0
393		}
394		utf.skip(20)
395		_ = utf.readInt16()
396
397		utf.skip(36)
398		sTypoAscender := utf.readInt16()
399		sTypoDescender := utf.readInt16()
400		if utf.Ascent == 0 {
401			utf.Ascent = int(float64(sTypoAscender) * scale)
402		}
403		if utf.Descent == 0 {
404			utf.Descent = int(float64(sTypoDescender) * scale)
405		}
406		if version > 1 {
407			utf.skip(16)
408			sCapHeight := utf.readInt16()
409			utf.CapHeight = int(float64(sCapHeight) * scale)
410		} else {
411			utf.CapHeight = utf.Ascent
412		}
413	} else {
414		weightType = 500
415		if utf.Ascent == 0 {
416			utf.Ascent = int(float64(utf.Bbox.Ymax) * scale)
417		}
418		if utf.Descent == 0 {
419			utf.Descent = int(float64(utf.Bbox.Ymin) * scale)
420		}
421		utf.CapHeight = utf.Ascent
422	}
423	utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2))
424	return weightType
425}
426
427func (utf *utf8FontFile) parsePOSTTable(weight int) {
428	utf.SeekTable("post")
429	utf.skip(4)
430	utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0
431	scale := 1000.0 / float64(utf.fontElementSize)
432	utf.UnderlinePosition = float64(utf.readInt16()) * scale
433	utf.UnderlineThickness = float64(utf.readInt16()) * scale
434	fixed := utf.readUint32()
435
436	utf.Flags = 4
437
438	if utf.ItalicAngle != 0 {
439		utf.Flags = utf.Flags | 64
440	}
441	if weight >= 600 {
442		utf.Flags = utf.Flags | 262144
443	}
444	if fixed != 0 {
445		utf.Flags = utf.Flags | 1
446	}
447}
448
449func (utf *utf8FontFile) parseCMAPTable(format int) int {
450	cmapPosition := utf.SeekTable("cmap")
451	utf.skip(2)
452	cmapTableCount := utf.readUint16()
453	cidCMAPPosition := 0
454	for i := 0; i < cmapTableCount; i++ {
455		system := utf.readUint16()
456		coded := utf.readUint16()
457		position := utf.readUint32()
458		oldReaderPosition := utf.fileReader.readerPosition
459		if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode
460			format = utf.getUint16(cmapPosition + position)
461			if format == 4 {
462				if cidCMAPPosition == 0 {
463					cidCMAPPosition = cmapPosition + position
464				}
465				break
466			}
467		}
468		utf.seek(int(oldReaderPosition))
469	}
470	if cidCMAPPosition == 0 {
471		fmt.Printf("Font does not have cmap for Unicode\n")
472		return cidCMAPPosition
473	}
474	return cidCMAPPosition
475}
476
477func (utf *utf8FontFile) parseTables() {
478	f := utf.parseNAMETable()
479	utf.parseHEADTable()
480	n := utf.parseHHEATable()
481	w := utf.parseOS2Table()
482	utf.parsePOSTTable(w)
483	runeCMAPPosition := utf.parseCMAPTable(f)
484
485	utf.SeekTable("maxp")
486	utf.skip(4)
487	numSymbols := utf.readUint16()
488
489	symbolCharDictionary := make(map[int][]int)
490	charSymbolDictionary := make(map[int]int)
491	utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary)
492
493	scale := 1000.0 / float64(utf.fontElementSize)
494	utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale)
495}
496
497func (utf *utf8FontFile) generateCMAP() map[int][]int {
498	cmapPosition := utf.SeekTable("cmap")
499	utf.skip(2)
500	cmapTableCount := utf.readUint16()
501	runeCmapPosition := 0
502	for i := 0; i < cmapTableCount; i++ {
503		system := utf.readUint16()
504		coder := utf.readUint16()
505		position := utf.readUint32()
506		oldPosition := utf.fileReader.readerPosition
507		if (system == 3 && coder == 1) || system == 0 {
508			format := utf.getUint16(cmapPosition + position)
509			if format == 4 {
510				runeCmapPosition = cmapPosition + position
511				break
512			}
513		}
514		utf.seek(int(oldPosition))
515	}
516
517	if runeCmapPosition == 0 {
518		fmt.Printf("Font does not have cmap for Unicode\n")
519		return nil
520	}
521
522	symbolCharDictionary := make(map[int][]int)
523	charSymbolDictionary := make(map[int]int)
524	utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary)
525
526	utf.charSymbolDictionary = charSymbolDictionary
527
528	return symbolCharDictionary
529}
530
531func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) {
532	symbolCollection := map[int]int{0: 0}
533	charSymbolPairCollection := make(map[int]int)
534	for _, char := range usedRunes {
535		if _, OK := utf.charSymbolDictionary[char]; OK {
536			symbolCollection[utf.charSymbolDictionary[char]] = char
537			charSymbolPairCollection[char] = utf.charSymbolDictionary[char]
538
539		}
540		utf.LastRune = max(utf.LastRune, char)
541	}
542
543	begin := utf.tableDescriptions["glyf"].position
544
545	symbolArray := make(map[int]int)
546	symbolCollectionKeys := keySortInt(symbolCollection)
547
548	symbolCounter := 0
549	maxRune := 0
550	for _, oldSymbolIndex := range symbolCollectionKeys {
551		maxRune = max(maxRune, symbolCollection[oldSymbolIndex])
552		symbolArray[oldSymbolIndex] = symbolCounter
553		symbolCounter++
554	}
555	charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection)
556	runeSymbolPairCollection := make(map[int]int)
557	for _, runa := range charSymbolPairCollectionKeys {
558		runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]]
559	}
560	utf.CodeSymbolDictionary = runeSymbolPairCollection
561
562	symbolCollectionKeys = keySortInt(symbolCollection)
563	for _, oldSymbolIndex := range symbolCollectionKeys {
564		_, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys)
565	}
566
567	return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys
568}
569
570func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte {
571	cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection)
572	cidID := 0
573	cidArray := make(map[int][]int)
574	prevCid := -2
575	prevSymbol := -1
576	for _, cid := range cidSymbolPairCollectionKeys {
577		if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) {
578			if n, OK := cidArray[cidID]; !OK || n == nil {
579				cidArray[cidID] = make([]int, 0)
580			}
581			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
582		} else {
583			cidID = cid
584			cidArray[cidID] = make([]int, 0)
585			cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
586		}
587		prevCid = cid
588		prevSymbol = cidSymbolPairCollection[cid]
589	}
590	cidArrayKeys := keySortArrayRangeMap(cidArray)
591	segCount := len(cidArray) + 1
592
593	searchRange := 1
594	entrySelector := 0
595	for searchRange*2 <= segCount {
596		searchRange = searchRange * 2
597		entrySelector = entrySelector + 1
598	}
599	searchRange = searchRange * 2
600	rangeShift := segCount*2 - searchRange
601	length := 16 + (8 * segCount) + (numSymbols + 1)
602	cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift}
603
604	for _, start := range cidArrayKeys {
605		endCode := start + (len(cidArray[start]) - 1)
606		cmap = append(cmap, endCode)
607	}
608	cmap = append(cmap, 0xFFFF)
609	cmap = append(cmap, 0)
610
611	for _, cidKey := range cidArrayKeys {
612		cmap = append(cmap, cidKey)
613	}
614	cmap = append(cmap, 0xFFFF)
615	for _, cidKey := range cidArrayKeys {
616		idDelta := -(cidKey - cidArray[cidKey][0])
617		cmap = append(cmap, idDelta)
618	}
619	cmap = append(cmap, 1)
620	for range cidArray {
621		cmap = append(cmap, 0)
622
623	}
624	cmap = append(cmap, 0)
625	for _, start := range cidArrayKeys {
626		for _, glidx := range cidArray[start] {
627			cmap = append(cmap, glidx)
628		}
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
639func (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, 0)
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
793func (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
834func (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
888func (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
903func (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
924func (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
978func max(i, n int) int {
979	if n > i {
980		return n
981	}
982	return i
983}
984
985func (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 := utf.outTablesData
1000	tablesNames := keySortStrings(tables)
1001
1002	offset := 12 + tablesCount*16
1003	begin := 0
1004
1005	for _, name := range tablesNames {
1006		if name == "head" {
1007			begin = offset
1008		}
1009		answer = append(answer, []byte(name)...)
1010		checksum := utf.generateChecksum(tables[name])
1011		answer = append(answer, pack2Uint16(checksum[0], checksum[1])...)
1012		answer = append(answer, pack2Uint32(offset, len(tables[name]))...)
1013		paddedLength := (len(tables[name]) + 3) &^ 3
1014		offset = offset + paddedLength
1015	}
1016
1017	for _, key := range tablesNames {
1018		data := append([]byte{}, tables[key]...)
1019		data = append(data, []byte{0, 0, 0}...)
1020		answer = append(answer, data[:(len(data)&^3)]...)
1021	}
1022
1023	checksum := utf.generateChecksum([]byte(answer))
1024	checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum)
1025	answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1]))
1026	return answer
1027}
1028
1029func unpackUint16Array(data []byte) []int {
1030	answer := make([]int, 1)
1031	r := bytes.NewReader(data)
1032	bs := make([]byte, 2)
1033	var e error
1034	var c int
1035	c, e = r.Read(bs)
1036	for e == nil && c > 0 {
1037		answer = append(answer, int(binary.BigEndian.Uint16(bs)))
1038		c, e = r.Read(bs)
1039	}
1040	return answer
1041}
1042
1043func unpackUint32Array(data []byte) []int {
1044	answer := make([]int, 1)
1045	r := bytes.NewReader(data)
1046	bs := make([]byte, 4)
1047	var e error
1048	var c int
1049	c, e = r.Read(bs)
1050	for e == nil && c > 0 {
1051		answer = append(answer, int(binary.BigEndian.Uint32(bs)))
1052		c, e = r.Read(bs)
1053	}
1054	return answer
1055}
1056
1057func unpackUint16(data []byte) int {
1058	return int(binary.BigEndian.Uint16(data))
1059}
1060
1061func packHeader(N uint32, n1, n2, n3, n4 int) []byte {
1062	answer := make([]byte, 0)
1063	bs4 := make([]byte, 4)
1064	binary.BigEndian.PutUint32(bs4, N)
1065	answer = append(answer, bs4...)
1066	bs := make([]byte, 2)
1067	binary.BigEndian.PutUint16(bs, uint16(n1))
1068	answer = append(answer, bs...)
1069	binary.BigEndian.PutUint16(bs, uint16(n2))
1070	answer = append(answer, bs...)
1071	binary.BigEndian.PutUint16(bs, uint16(n3))
1072	answer = append(answer, bs...)
1073	binary.BigEndian.PutUint16(bs, uint16(n4))
1074	answer = append(answer, bs...)
1075	return answer
1076}
1077
1078func pack2Uint16(n1, n2 int) []byte {
1079	answer := make([]byte, 0)
1080	bs := make([]byte, 2)
1081	binary.BigEndian.PutUint16(bs, uint16(n1))
1082	answer = append(answer, bs...)
1083	binary.BigEndian.PutUint16(bs, uint16(n2))
1084	answer = append(answer, bs...)
1085	return answer
1086}
1087
1088func pack2Uint32(n1, n2 int) []byte {
1089	answer := make([]byte, 0)
1090	bs := make([]byte, 4)
1091	binary.BigEndian.PutUint32(bs, uint32(n1))
1092	answer = append(answer, bs...)
1093	binary.BigEndian.PutUint32(bs, uint32(n2))
1094	answer = append(answer, bs...)
1095	return answer
1096}
1097
1098func packUint32(n1 int) []byte {
1099	bs := make([]byte, 4)
1100	binary.BigEndian.PutUint32(bs, uint32(n1))
1101	return bs
1102}
1103
1104func packUint16(n1 int) []byte {
1105	bs := make([]byte, 2)
1106	binary.BigEndian.PutUint16(bs, uint16(n1))
1107	return bs
1108}
1109
1110func keySortStrings(s map[string][]byte) []string {
1111	keys := make([]string, len(s))
1112	i := 0
1113	for key := range s {
1114		keys[i] = key
1115		i++
1116	}
1117	sort.Strings(keys)
1118	return keys
1119}
1120
1121func keySortInt(s map[int]int) []int {
1122	keys := make([]int, len(s))
1123	i := 0
1124	for key := range s {
1125		keys[i] = key
1126		i++
1127	}
1128	sort.Ints(keys)
1129	return keys
1130}
1131
1132func keySortArrayRangeMap(s map[int][]int) []int {
1133	keys := make([]int, len(s))
1134	i := 0
1135	for key := range s {
1136		keys[i] = key
1137		i++
1138	}
1139	sort.Ints(keys)
1140	return keys
1141}
1142
1143// UTF8CutFont is a utility function that generates a TrueType font composed
1144// only of the runes included in cutset. The rune glyphs are copied from This
1145// function is demonstrated in ExampleUTF8CutFont().
1146func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) {
1147	f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf})
1148	runes := map[int]int{}
1149	for i, r := range cutset {
1150		runes[i] = int(r)
1151	}
1152	outBuf = f.GenerateCutFont(runes)
1153	return
1154}
1155