12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154 |
- /*
- * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy)
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- package gofpdf
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- "math"
- "sort"
- )
- // flags
- const symbolWords = 1 << 0
- const symbolScale = 1 << 3
- const symbolContinue = 1 << 5
- const symbolAllScale = 1 << 6
- const symbol2x2 = 1 << 7
- // CID map Init
- 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"
- type utf8FontFile struct {
- fileReader *fileReader
- LastRune int
- tableDescriptions map[string]*tableDescription
- outTablesData map[string][]byte
- symbolPosition []int
- charSymbolDictionary map[int]int
- Ascent int
- Descent int
- fontElementSize int
- Bbox fontBoxType
- CapHeight int
- StemV int
- ItalicAngle int
- Flags int
- UnderlinePosition float64
- UnderlineThickness float64
- CharWidths []int
- DefaultWidth float64
- symbolData map[int]map[string][]int
- CodeSymbolDictionary map[int]int
- }
- type tableDescription struct {
- name string
- checksum []int
- position int
- size int
- }
- type fileReader struct {
- readerPosition int64
- array []byte
- }
- func (fr *fileReader) Read(s int) []byte {
- b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)]
- fr.readerPosition += int64(s)
- return b
- }
- func (fr *fileReader) seek(shift int64, flag int) (int64, error) {
- if flag == 0 {
- fr.readerPosition = shift
- } else if flag == 1 {
- fr.readerPosition += shift
- } else if flag == 2 {
- fr.readerPosition = int64(len(fr.array)) - shift
- }
- return int64(fr.readerPosition), nil
- }
- func newUTF8Font(reader *fileReader) *utf8FontFile {
- utf := utf8FontFile{
- fileReader: reader,
- }
- return &utf
- }
- func (utf *utf8FontFile) parseFile() error {
- utf.fileReader.readerPosition = 0
- utf.symbolPosition = make([]int, 0)
- utf.charSymbolDictionary = make(map[int]int)
- utf.tableDescriptions = make(map[string]*tableDescription)
- utf.outTablesData = make(map[string][]byte)
- utf.Ascent = 0
- utf.Descent = 0
- codeType := uint32(utf.readUint32())
- if codeType == 0x4F54544F {
- return fmt.Errorf("not supported\n ")
- }
- if codeType == 0x74746366 {
- return fmt.Errorf("not supported\n ")
- }
- if codeType != 0x00010000 && codeType != 0x74727565 {
- return fmt.Errorf("Not a TrueType font: codeType=%v\n ", codeType)
- }
- utf.generateTableDescriptions()
- utf.parseTables()
- return nil
- }
- func (utf *utf8FontFile) generateTableDescriptions() {
- tablesCount := utf.readUint16()
- _ = utf.readUint16()
- _ = utf.readUint16()
- _ = utf.readUint16()
- utf.tableDescriptions = make(map[string]*tableDescription)
- for i := 0; i < tablesCount; i++ {
- record := tableDescription{
- name: utf.readTableName(),
- checksum: []int{utf.readUint16(), utf.readUint16()},
- position: utf.readUint32(),
- size: utf.readUint32(),
- }
- utf.tableDescriptions[record.name] = &record
- }
- }
- func (utf *utf8FontFile) readTableName() string {
- return string(utf.fileReader.Read(4))
- }
- func (utf *utf8FontFile) readUint16() int {
- s := utf.fileReader.Read(2)
- return (int(s[0]) << 8) + int(s[1])
- }
- func (utf *utf8FontFile) readUint32() int {
- s := utf.fileReader.Read(4)
- return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 16777216 = 1<<24
- }
- func (utf *utf8FontFile) calcInt32(x, y []int) []int {
- answer := make([]int, 2)
- if y[1] > x[1] {
- x[1] += 1 << 16
- x[0]++
- }
- answer[1] = x[1] - y[1]
- if y[0] > x[0] {
- x[0] += 1 << 16
- }
- answer[0] = x[0] - y[0]
- answer[0] = answer[0] & 0xFFFF
- return answer
- }
- func (utf *utf8FontFile) generateChecksum(data []byte) []int {
- if (len(data) % 4) != 0 {
- for i := 0; (len(data) % 4) != 0; i++ {
- data = append(data, 0)
- }
- }
- answer := []int{0x0000, 0x0000}
- for i := 0; i < len(data); i += 4 {
- answer[0] += (int(data[i]) << 8) + int(data[i+1])
- answer[1] += (int(data[i+2]) << 8) + int(data[i+3])
- answer[0] += answer[1] >> 16
- answer[1] = answer[1] & 0xFFFF
- answer[0] = answer[0] & 0xFFFF
- }
- return answer
- }
- func (utf *utf8FontFile) seek(shift int) {
- _, _ = utf.fileReader.seek(int64(shift), 0)
- }
- func (utf *utf8FontFile) skip(delta int) {
- _, _ = utf.fileReader.seek(int64(delta), 1)
- }
- //SeekTable position
- func (utf *utf8FontFile) SeekTable(name string) int {
- return utf.seekTable(name, 0)
- }
- func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int {
- _, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0)
- return int(utf.fileReader.readerPosition)
- }
- func (utf *utf8FontFile) readInt16() int16 {
- s := utf.fileReader.Read(2)
- a := (int16(s[0]) << 8) + int16(s[1])
- if (int(a) & (1 << 15)) == 0 {
- a = int16(int(a) - (1 << 16))
- }
- return a
- }
- func (utf *utf8FontFile) getUint16(pos int) int {
- _, _ = utf.fileReader.seek(int64(pos), 0)
- s := utf.fileReader.Read(2)
- return (int(s[0]) << 8) + int(s[1])
- }
- func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte {
- stream = append([]byte{}, stream...)
- return append(append(stream[:offset], value...), stream[offset+len(value):]...)
- }
- func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte {
- up := make([]byte, 2)
- binary.BigEndian.PutUint16(up, uint16(value))
- return utf.splice(stream, offset, up)
- }
- func (utf *utf8FontFile) getRange(pos, length int) []byte {
- _, _ = utf.fileReader.seek(int64(pos), 0)
- if length < 1 {
- return make([]byte, 0)
- }
- s := utf.fileReader.Read(length)
- return s
- }
- func (utf *utf8FontFile) getTableData(name string) []byte {
- desckrip := utf.tableDescriptions[name]
- if desckrip == nil {
- return nil
- }
- if desckrip.size == 0 {
- return nil
- }
- _, _ = utf.fileReader.seek(int64(desckrip.position), 0)
- s := utf.fileReader.Read(desckrip.size)
- return s
- }
- func (utf *utf8FontFile) setOutTable(name string, data []byte) {
- if data == nil {
- return
- }
- if name == "head" {
- data = utf.splice(data, 8, []byte{0, 0, 0, 0})
- }
- utf.outTablesData[name] = data
- }
- func arrayKeys(arr map[int]string) []int {
- answer := make([]int, len(arr))
- i := 0
- for key := range arr {
- answer[i] = key
- i++
- }
- return answer
- }
- func inArray(s int, arr []int) bool {
- for _, i := range arr {
- if s == i {
- return true
- }
- }
- return false
- }
- func (utf *utf8FontFile) parseNAMETable() int {
- namePosition := utf.SeekTable("name")
- format := utf.readUint16()
- if format != 0 {
- fmt.Printf("Illegal format %d\n", format)
- return format
- }
- nameCount := utf.readUint16()
- stringDataPosition := namePosition + utf.readUint16()
- names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""}
- keys := arrayKeys(names)
- counter := len(names)
- for i := 0; i < nameCount; i++ {
- system := utf.readUint16()
- code := utf.readUint16()
- local := utf.readUint16()
- nameID := utf.readUint16()
- size := utf.readUint16()
- position := utf.readUint16()
- if !inArray(nameID, keys) {
- continue
- }
- currentName := ""
- if system == 3 && code == 1 && local == 0x409 {
- oldPos := utf.fileReader.readerPosition
- utf.seek(stringDataPosition + position)
- if size%2 != 0 {
- fmt.Printf("name is not binar byte format\n")
- return format
- }
- size /= 2
- currentName = ""
- for size > 0 {
- char := utf.readUint16()
- currentName += string(rune(char))
- size--
- }
- utf.fileReader.readerPosition = oldPos
- utf.seek(int(oldPos))
- } else if system == 1 && code == 0 && local == 0 {
- oldPos := utf.fileReader.readerPosition
- currentName = string(utf.getRange(stringDataPosition+position, size))
- utf.fileReader.readerPosition = oldPos
- utf.seek(int(oldPos))
- }
- if currentName != "" && names[nameID] == "" {
- names[nameID] = currentName
- counter--
- if counter == 0 {
- break
- }
- }
- }
- return format
- }
- func (utf *utf8FontFile) parseHEADTable() {
- utf.SeekTable("head")
- utf.skip(18)
- utf.fontElementSize = utf.readUint16()
- scale := 1000.0 / float64(utf.fontElementSize)
- utf.skip(16)
- xMin := utf.readInt16()
- yMin := utf.readInt16()
- xMax := utf.readInt16()
- yMax := utf.readInt16()
- utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)}
- utf.skip(3 * 2)
- _ = utf.readUint16()
- symbolDataFormat := utf.readUint16()
- if symbolDataFormat != 0 {
- fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat)
- return
- }
- }
- func (utf *utf8FontFile) parseHHEATable() int {
- metricsCount := 0
- if _, OK := utf.tableDescriptions["hhea"]; OK {
- scale := 1000.0 / float64(utf.fontElementSize)
- utf.SeekTable("hhea")
- utf.skip(4)
- hheaAscender := utf.readInt16()
- hheaDescender := utf.readInt16()
- utf.Ascent = int(float64(hheaAscender) * scale)
- utf.Descent = int(float64(hheaDescender) * scale)
- utf.skip(24)
- metricDataFormat := utf.readUint16()
- if metricDataFormat != 0 {
- fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat)
- return 0
- }
- metricsCount = utf.readUint16()
- if metricsCount == 0 {
- fmt.Printf("Number of horizontal metrics is 0\n")
- return 0
- }
- }
- return metricsCount
- }
- func (utf *utf8FontFile) parseOS2Table() int {
- var weightType int
- scale := 1000.0 / float64(utf.fontElementSize)
- if _, OK := utf.tableDescriptions["OS/2"]; OK {
- utf.SeekTable("OS/2")
- version := utf.readUint16()
- utf.skip(2)
- weightType = utf.readUint16()
- utf.skip(2)
- fsType := utf.readUint16()
- if fsType == 0x0002 || (fsType&0x0300) != 0 {
- fmt.Printf("ERROR - copyright restrictions.\n")
- return 0
- }
- utf.skip(20)
- _ = utf.readInt16()
- utf.skip(36)
- sTypoAscender := utf.readInt16()
- sTypoDescender := utf.readInt16()
- if utf.Ascent == 0 {
- utf.Ascent = int(float64(sTypoAscender) * scale)
- }
- if utf.Descent == 0 {
- utf.Descent = int(float64(sTypoDescender) * scale)
- }
- if version > 1 {
- utf.skip(16)
- sCapHeight := utf.readInt16()
- utf.CapHeight = int(float64(sCapHeight) * scale)
- } else {
- utf.CapHeight = utf.Ascent
- }
- } else {
- weightType = 500
- if utf.Ascent == 0 {
- utf.Ascent = int(float64(utf.Bbox.Ymax) * scale)
- }
- if utf.Descent == 0 {
- utf.Descent = int(float64(utf.Bbox.Ymin) * scale)
- }
- utf.CapHeight = utf.Ascent
- }
- utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2))
- return weightType
- }
- func (utf *utf8FontFile) parsePOSTTable(weight int) {
- utf.SeekTable("post")
- utf.skip(4)
- utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0
- scale := 1000.0 / float64(utf.fontElementSize)
- utf.UnderlinePosition = float64(utf.readInt16()) * scale
- utf.UnderlineThickness = float64(utf.readInt16()) * scale
- fixed := utf.readUint32()
- utf.Flags = 4
- if utf.ItalicAngle != 0 {
- utf.Flags = utf.Flags | 64
- }
- if weight >= 600 {
- utf.Flags = utf.Flags | 262144
- }
- if fixed != 0 {
- utf.Flags = utf.Flags | 1
- }
- }
- func (utf *utf8FontFile) parseCMAPTable(format int) int {
- cmapPosition := utf.SeekTable("cmap")
- utf.skip(2)
- cmapTableCount := utf.readUint16()
- cidCMAPPosition := 0
- for i := 0; i < cmapTableCount; i++ {
- system := utf.readUint16()
- coded := utf.readUint16()
- position := utf.readUint32()
- oldReaderPosition := utf.fileReader.readerPosition
- if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode
- format = utf.getUint16(cmapPosition + position)
- if format == 4 {
- if cidCMAPPosition == 0 {
- cidCMAPPosition = cmapPosition + position
- }
- break
- }
- }
- utf.seek(int(oldReaderPosition))
- }
- if cidCMAPPosition == 0 {
- fmt.Printf("Font does not have cmap for Unicode\n")
- return cidCMAPPosition
- }
- return cidCMAPPosition
- }
- func (utf *utf8FontFile) parseTables() {
- f := utf.parseNAMETable()
- utf.parseHEADTable()
- n := utf.parseHHEATable()
- w := utf.parseOS2Table()
- utf.parsePOSTTable(w)
- runeCMAPPosition := utf.parseCMAPTable(f)
- utf.SeekTable("maxp")
- utf.skip(4)
- numSymbols := utf.readUint16()
- symbolCharDictionary := make(map[int][]int)
- charSymbolDictionary := make(map[int]int)
- utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary)
- scale := 1000.0 / float64(utf.fontElementSize)
- utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale)
- }
- func (utf *utf8FontFile) generateCMAP() map[int][]int {
- cmapPosition := utf.SeekTable("cmap")
- utf.skip(2)
- cmapTableCount := utf.readUint16()
- runeCmapPosition := 0
- for i := 0; i < cmapTableCount; i++ {
- system := utf.readUint16()
- coder := utf.readUint16()
- position := utf.readUint32()
- oldPosition := utf.fileReader.readerPosition
- if (system == 3 && coder == 1) || system == 0 {
- format := utf.getUint16(cmapPosition + position)
- if format == 4 {
- runeCmapPosition = cmapPosition + position
- break
- }
- }
- utf.seek(int(oldPosition))
- }
- if runeCmapPosition == 0 {
- fmt.Printf("Font does not have cmap for Unicode\n")
- return nil
- }
- symbolCharDictionary := make(map[int][]int)
- charSymbolDictionary := make(map[int]int)
- utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary)
- utf.charSymbolDictionary = charSymbolDictionary
- return symbolCharDictionary
- }
- func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) {
- symbolCollection := map[int]int{0: 0}
- charSymbolPairCollection := make(map[int]int)
- for _, char := range usedRunes {
- if _, OK := utf.charSymbolDictionary[char]; OK {
- symbolCollection[utf.charSymbolDictionary[char]] = char
- charSymbolPairCollection[char] = utf.charSymbolDictionary[char]
- }
- utf.LastRune = max(utf.LastRune, char)
- }
- begin := utf.tableDescriptions["glyf"].position
- symbolArray := make(map[int]int)
- symbolCollectionKeys := keySortInt(symbolCollection)
- symbolCounter := 0
- maxRune := 0
- for _, oldSymbolIndex := range symbolCollectionKeys {
- maxRune = max(maxRune, symbolCollection[oldSymbolIndex])
- symbolArray[oldSymbolIndex] = symbolCounter
- symbolCounter++
- }
- charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection)
- runeSymbolPairCollection := make(map[int]int)
- for _, runa := range charSymbolPairCollectionKeys {
- runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]]
- }
- utf.CodeSymbolDictionary = runeSymbolPairCollection
- symbolCollectionKeys = keySortInt(symbolCollection)
- for _, oldSymbolIndex := range symbolCollectionKeys {
- _, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys)
- }
- return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys
- }
- func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte {
- cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection)
- cidID := 0
- cidArray := make(map[int][]int)
- prevCid := -2
- prevSymbol := -1
- for _, cid := range cidSymbolPairCollectionKeys {
- if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) {
- if n, OK := cidArray[cidID]; !OK || n == nil {
- cidArray[cidID] = make([]int, 0)
- }
- cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
- } else {
- cidID = cid
- cidArray[cidID] = make([]int, 0)
- cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid])
- }
- prevCid = cid
- prevSymbol = cidSymbolPairCollection[cid]
- }
- cidArrayKeys := keySortArrayRangeMap(cidArray)
- segCount := len(cidArray) + 1
- searchRange := 1
- entrySelector := 0
- for searchRange*2 <= segCount {
- searchRange = searchRange * 2
- entrySelector = entrySelector + 1
- }
- searchRange = searchRange * 2
- rangeShift := segCount*2 - searchRange
- length := 16 + (8 * segCount) + (numSymbols + 1)
- cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift}
- for _, start := range cidArrayKeys {
- endCode := start + (len(cidArray[start]) - 1)
- cmap = append(cmap, endCode)
- }
- cmap = append(cmap, 0xFFFF)
- cmap = append(cmap, 0)
- for _, cidKey := range cidArrayKeys {
- cmap = append(cmap, cidKey)
- }
- cmap = append(cmap, 0xFFFF)
- for _, cidKey := range cidArrayKeys {
- idDelta := -(cidKey - cidArray[cidKey][0])
- cmap = append(cmap, idDelta)
- }
- cmap = append(cmap, 1)
- for range cidArray {
- cmap = append(cmap, 0)
- }
- cmap = append(cmap, 0)
- for _, start := range cidArrayKeys {
- for _, glidx := range cidArray[start] {
- cmap = append(cmap, glidx)
- }
- }
- cmap = append(cmap, 0)
- cmapstr := make([]byte, 0)
- for _, cm := range cmap {
- cmapstr = append(cmapstr, packUint16(cm)...)
- }
- return cmapstr
- }
- //GenerateCutFont fill utf8FontFile from .utf file, only with runes from usedRunes
- func (utf *utf8FontFile) GenerateCutFont(usedRunes map[int]int) []byte {
- utf.fileReader.readerPosition = 0
- utf.symbolPosition = make([]int, 0)
- utf.charSymbolDictionary = make(map[int]int)
- utf.tableDescriptions = make(map[string]*tableDescription)
- utf.outTablesData = make(map[string][]byte)
- utf.Ascent = 0
- utf.Descent = 0
- utf.skip(4)
- utf.LastRune = 0
- utf.generateTableDescriptions()
- utf.SeekTable("head")
- utf.skip(50)
- LocaFormat := utf.readUint16()
- utf.SeekTable("hhea")
- utf.skip(34)
- metricsCount := utf.readUint16()
- oldMetrics := metricsCount
- utf.SeekTable("maxp")
- utf.skip(4)
- numSymbols := utf.readUint16()
- symbolCharDictionary := utf.generateCMAP()
- if symbolCharDictionary == nil {
- return nil
- }
- utf.parseHMTXTable(metricsCount, numSymbols, symbolCharDictionary, 1.0)
- utf.parseLOCATable(LocaFormat, numSymbols)
- cidSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys := utf.parseSymbols(usedRunes)
- metricsCount = len(symbolCollection)
- numSymbols = metricsCount
- utf.setOutTable("name", utf.getTableData("name"))
- utf.setOutTable("cvt ", utf.getTableData("cvt "))
- utf.setOutTable("fpgm", utf.getTableData("fpgm"))
- utf.setOutTable("prep", utf.getTableData("prep"))
- utf.setOutTable("gasp", utf.getTableData("gasp"))
- postTable := utf.getTableData("post")
- 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}...)
- utf.setOutTable("post", postTable)
- delete(cidSymbolPairCollection, 0)
- utf.setOutTable("cmap", utf.generateCMAPTable(cidSymbolPairCollection, numSymbols))
- symbolData := utf.getTableData("glyf")
- offsets := make([]int, 0)
- glyfData := make([]byte, 0)
- pos := 0
- hmtxData := make([]byte, 0)
- utf.symbolData = make(map[int]map[string][]int, 0)
- for _, originalSymbolIdx := range symbolCollectionKeys {
- hm := utf.getMetrics(oldMetrics, originalSymbolIdx)
- hmtxData = append(hmtxData, hm...)
- offsets = append(offsets, pos)
- symbolPos := utf.symbolPosition[originalSymbolIdx]
- symbolLen := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
- data := symbolData[symbolPos : symbolPos+symbolLen]
- var up int
- if symbolLen > 0 {
- up = unpackUint16(data[0:2])
- }
- if symbolLen > 2 && (up&(1<<15)) != 0 {
- posInSymbol := 10
- flags := symbolContinue
- nComponentElements := 0
- for (flags & symbolContinue) != 0 {
- nComponentElements++
- up = unpackUint16(data[posInSymbol : posInSymbol+2])
- flags = up
- up = unpackUint16(data[posInSymbol+2 : posInSymbol+4])
- symbolIdx := up
- if _, OK := utf.symbolData[originalSymbolIdx]; !OK {
- utf.symbolData[originalSymbolIdx] = make(map[string][]int)
- }
- if _, OK := utf.symbolData[originalSymbolIdx]["compSymbols"]; !OK {
- utf.symbolData[originalSymbolIdx]["compSymbols"] = make([]int, 0)
- }
- utf.symbolData[originalSymbolIdx]["compSymbols"] = append(utf.symbolData[originalSymbolIdx]["compSymbols"], symbolIdx)
- data = utf.insertUint16(data, posInSymbol+2, symbolArray[symbolIdx])
- posInSymbol += 4
- if (flags & symbolWords) != 0 {
- posInSymbol += 4
- } else {
- posInSymbol += 2
- }
- if (flags & symbolScale) != 0 {
- posInSymbol += 2
- } else if (flags & symbolAllScale) != 0 {
- posInSymbol += 4
- } else if (flags & symbol2x2) != 0 {
- posInSymbol += 8
- }
- }
- }
- glyfData = append(glyfData, data...)
- pos += symbolLen
- if pos%4 != 0 {
- padding := 4 - (pos % 4)
- glyfData = append(glyfData, make([]byte, padding)...)
- pos += padding
- }
- }
- offsets = append(offsets, pos)
- utf.setOutTable("glyf", glyfData)
- utf.setOutTable("hmtx", hmtxData)
- locaData := make([]byte, 0)
- if ((pos + 1) >> 1) > 0xFFFF {
- LocaFormat = 1
- for _, offset := range offsets {
- locaData = append(locaData, packUint32(offset)...)
- }
- } else {
- LocaFormat = 0
- for _, offset := range offsets {
- locaData = append(locaData, packUint16(offset/2)...)
- }
- }
- utf.setOutTable("loca", locaData)
- headData := utf.getTableData("head")
- headData = utf.insertUint16(headData, 50, LocaFormat)
- utf.setOutTable("head", headData)
- hheaData := utf.getTableData("hhea")
- hheaData = utf.insertUint16(hheaData, 34, metricsCount)
- utf.setOutTable("hhea", hheaData)
- maxp := utf.getTableData("maxp")
- maxp = utf.insertUint16(maxp, 4, numSymbols)
- utf.setOutTable("maxp", maxp)
- os2Data := utf.getTableData("OS/2")
- utf.setOutTable("OS/2", os2Data)
- return utf.assembleTables()
- }
- 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) {
- symbolPos := utf.symbolPosition[originalSymbolIdx]
- symbolSize := utf.symbolPosition[originalSymbolIdx+1] - symbolPos
- if symbolSize == 0 {
- return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
- }
- utf.seek(*start + symbolPos)
- lineCount := utf.readInt16()
- if lineCount < 0 {
- utf.skip(8)
- flags := symbolContinue
- for flags&symbolContinue != 0 {
- flags = utf.readUint16()
- symbolIndex := utf.readUint16()
- if _, OK := symbolSet[symbolIndex]; !OK {
- symbolSet[symbolIndex] = len(SymbolsCollection)
- SymbolsCollection[symbolIndex] = 1
- SymbolsCollectionKeys = append(SymbolsCollectionKeys, symbolIndex)
- }
- oldPosition, _ := utf.fileReader.seek(0, 1)
- _, _, _, SymbolsCollectionKeys = utf.getSymbols(symbolIndex, start, symbolSet, SymbolsCollection, SymbolsCollectionKeys)
- utf.seek(int(oldPosition))
- if flags&symbolWords != 0 {
- utf.skip(4)
- } else {
- utf.skip(2)
- }
- if flags&symbolScale != 0 {
- utf.skip(2)
- } else if flags&symbolAllScale != 0 {
- utf.skip(4)
- } else if flags&symbol2x2 != 0 {
- utf.skip(8)
- }
- }
- }
- return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys
- }
- func (utf *utf8FontFile) parseHMTXTable(numberOfHMetrics, numSymbols int, symbolToChar map[int][]int, scale float64) {
- var widths int
- start := utf.SeekTable("hmtx")
- arrayWidths := 0
- var arr []int
- utf.CharWidths = make([]int, 256*256)
- charCount := 0
- arr = unpackUint16Array(utf.getRange(start, numberOfHMetrics*4))
- for symbol := 0; symbol < numberOfHMetrics; symbol++ {
- arrayWidths = arr[(symbol*2)+1]
- if _, OK := symbolToChar[symbol]; OK || symbol == 0 {
- if arrayWidths >= (1 << 15) {
- arrayWidths = 0
- }
- if symbol == 0 {
- utf.DefaultWidth = scale * float64(arrayWidths)
- continue
- }
- for _, char := range symbolToChar[symbol] {
- if char != 0 && char != 65535 {
- widths = int(math.Round(scale * float64(arrayWidths)))
- if widths == 0 {
- widths = 65535
- }
- if char < 196608 {
- utf.CharWidths[char] = widths
- charCount++
- }
- }
- }
- }
- }
- diff := numSymbols - numberOfHMetrics
- for pos := 0; pos < diff; pos++ {
- symbol := pos + numberOfHMetrics
- if _, OK := symbolToChar[symbol]; OK {
- for _, char := range symbolToChar[symbol] {
- if char != 0 && char != 65535 {
- widths = int(math.Round(scale * float64(arrayWidths)))
- if widths == 0 {
- widths = 65535
- }
- if char < 196608 {
- utf.CharWidths[char] = widths
- charCount++
- }
- }
- }
- }
- }
- utf.CharWidths[0] = charCount
- }
- func (utf *utf8FontFile) getMetrics(metricCount, gid int) []byte {
- start := utf.SeekTable("hmtx")
- var metrics []byte
- if gid < metricCount {
- utf.seek(start + (gid * 4))
- metrics = utf.fileReader.Read(4)
- } else {
- utf.seek(start + ((metricCount - 1) * 4))
- metrics = utf.fileReader.Read(2)
- utf.seek(start + (metricCount * 2) + (gid * 2))
- metrics = append(metrics, utf.fileReader.Read(2)...)
- }
- return metrics
- }
- func (utf *utf8FontFile) parseLOCATable(format, numSymbols int) {
- start := utf.SeekTable("loca")
- utf.symbolPosition = make([]int, 0)
- if format == 0 {
- data := utf.getRange(start, (numSymbols*2)+2)
- arr := unpackUint16Array(data)
- for n := 0; n <= numSymbols; n++ {
- utf.symbolPosition = append(utf.symbolPosition, arr[n+1]*2)
- }
- } else if format == 1 {
- data := utf.getRange(start, (numSymbols*4)+4)
- arr := unpackUint32Array(data)
- for n := 0; n <= numSymbols; n++ {
- utf.symbolPosition = append(utf.symbolPosition, arr[n+1])
- }
- } else {
- fmt.Printf("Unknown loca table format %d\n", format)
- return
- }
- }
- func (utf *utf8FontFile) generateSCCSDictionaries(runeCmapPosition int, symbolCharDictionary map[int][]int, charSymbolDictionary map[int]int) {
- maxRune := 0
- utf.seek(runeCmapPosition + 2)
- size := utf.readUint16()
- rim := runeCmapPosition + size
- utf.skip(2)
- segmentSize := utf.readUint16() / 2
- utf.skip(6)
- completers := make([]int, 0)
- for i := 0; i < segmentSize; i++ {
- completers = append(completers, utf.readUint16())
- }
- utf.skip(2)
- beginners := make([]int, 0)
- for i := 0; i < segmentSize; i++ {
- beginners = append(beginners, utf.readUint16())
- }
- sizes := make([]int, 0)
- for i := 0; i < segmentSize; i++ {
- sizes = append(sizes, int(utf.readInt16()))
- }
- readerPositionStart := utf.fileReader.readerPosition
- positions := make([]int, 0)
- for i := 0; i < segmentSize; i++ {
- positions = append(positions, utf.readUint16())
- }
- var symbol int
- for n := 0; n < segmentSize; n++ {
- completePosition := completers[n] + 1
- for char := beginners[n]; char < completePosition; char++ {
- if positions[n] == 0 {
- symbol = (char + sizes[n]) & 0xFFFF
- } else {
- position := (char-beginners[n])*2 + positions[n]
- position = int(readerPositionStart) + 2*n + position
- if position >= rim {
- symbol = 0
- } else {
- symbol = utf.getUint16(position)
- if symbol != 0 {
- symbol = (symbol + sizes[n]) & 0xFFFF
- }
- }
- }
- charSymbolDictionary[char] = symbol
- if char < 196608 {
- maxRune = max(char, maxRune)
- }
- symbolCharDictionary[symbol] = append(symbolCharDictionary[symbol], char)
- }
- }
- }
- func max(i, n int) int {
- if n > i {
- return n
- }
- return i
- }
- func (utf *utf8FontFile) assembleTables() []byte {
- answer := make([]byte, 0)
- tablesCount := len(utf.outTablesData)
- findSize := 1
- writer := 0
- for findSize*2 <= tablesCount {
- findSize = findSize * 2
- writer = writer + 1
- }
- findSize = findSize * 16
- rOffset := tablesCount*16 - findSize
- answer = append(answer, packHeader(0x00010000, tablesCount, findSize, writer, rOffset)...)
- tables := utf.outTablesData
- tablesNames := keySortStrings(tables)
- offset := 12 + tablesCount*16
- begin := 0
- for _, name := range tablesNames {
- if name == "head" {
- begin = offset
- }
- answer = append(answer, []byte(name)...)
- checksum := utf.generateChecksum(tables[name])
- answer = append(answer, pack2Uint16(checksum[0], checksum[1])...)
- answer = append(answer, pack2Uint32(offset, len(tables[name]))...)
- paddedLength := (len(tables[name]) + 3) &^ 3
- offset = offset + paddedLength
- }
- for _, key := range tablesNames {
- data := append([]byte{}, tables[key]...)
- data = append(data, []byte{0, 0, 0}...)
- answer = append(answer, data[:(len(data)&^3)]...)
- }
- checksum := utf.generateChecksum([]byte(answer))
- checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum)
- answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1]))
- return answer
- }
- func unpackUint16Array(data []byte) []int {
- answer := make([]int, 1)
- r := bytes.NewReader(data)
- bs := make([]byte, 2)
- var e error
- var c int
- c, e = r.Read(bs)
- for e == nil && c > 0 {
- answer = append(answer, int(binary.BigEndian.Uint16(bs)))
- c, e = r.Read(bs)
- }
- return answer
- }
- func unpackUint32Array(data []byte) []int {
- answer := make([]int, 1)
- r := bytes.NewReader(data)
- bs := make([]byte, 4)
- var e error
- var c int
- c, e = r.Read(bs)
- for e == nil && c > 0 {
- answer = append(answer, int(binary.BigEndian.Uint32(bs)))
- c, e = r.Read(bs)
- }
- return answer
- }
- func unpackUint16(data []byte) int {
- return int(binary.BigEndian.Uint16(data))
- }
- func packHeader(N uint32, n1, n2, n3, n4 int) []byte {
- answer := make([]byte, 0)
- bs4 := make([]byte, 4)
- binary.BigEndian.PutUint32(bs4, N)
- answer = append(answer, bs4...)
- bs := make([]byte, 2)
- binary.BigEndian.PutUint16(bs, uint16(n1))
- answer = append(answer, bs...)
- binary.BigEndian.PutUint16(bs, uint16(n2))
- answer = append(answer, bs...)
- binary.BigEndian.PutUint16(bs, uint16(n3))
- answer = append(answer, bs...)
- binary.BigEndian.PutUint16(bs, uint16(n4))
- answer = append(answer, bs...)
- return answer
- }
- func pack2Uint16(n1, n2 int) []byte {
- answer := make([]byte, 0)
- bs := make([]byte, 2)
- binary.BigEndian.PutUint16(bs, uint16(n1))
- answer = append(answer, bs...)
- binary.BigEndian.PutUint16(bs, uint16(n2))
- answer = append(answer, bs...)
- return answer
- }
- func pack2Uint32(n1, n2 int) []byte {
- answer := make([]byte, 0)
- bs := make([]byte, 4)
- binary.BigEndian.PutUint32(bs, uint32(n1))
- answer = append(answer, bs...)
- binary.BigEndian.PutUint32(bs, uint32(n2))
- answer = append(answer, bs...)
- return answer
- }
- func packUint32(n1 int) []byte {
- bs := make([]byte, 4)
- binary.BigEndian.PutUint32(bs, uint32(n1))
- return bs
- }
- func packUint16(n1 int) []byte {
- bs := make([]byte, 2)
- binary.BigEndian.PutUint16(bs, uint16(n1))
- return bs
- }
- func keySortStrings(s map[string][]byte) []string {
- keys := make([]string, len(s))
- i := 0
- for key := range s {
- keys[i] = key
- i++
- }
- sort.Strings(keys)
- return keys
- }
- func keySortInt(s map[int]int) []int {
- keys := make([]int, len(s))
- i := 0
- for key := range s {
- keys[i] = key
- i++
- }
- sort.Ints(keys)
- return keys
- }
- func keySortArrayRangeMap(s map[int][]int) []int {
- keys := make([]int, len(s))
- i := 0
- for key := range s {
- keys[i] = key
- i++
- }
- sort.Ints(keys)
- return keys
- }
- // UTF8CutFont is a utility function that generates a TrueType font composed
- // only of the runes included in cutset. The rune glyphs are copied from This
- // function is demonstrated in ExampleUTF8CutFont().
- func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) {
- f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf})
- runes := map[int]int{}
- for i, r := range cutset {
- runes[i] = int(r)
- }
- outBuf = f.GenerateCutFont(runes)
- return
- }
|