d2/lib/font/cutFont.go
2023-03-27 19:26:49 -07:00

876 lines
23 KiB
Go

// https://github.com/jung-kurt/gofpdf/blob/master/utf8fontfile.go
package font
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"sort"
)
type fontBoxType struct {
Xmin, Ymin, Xmax, Ymax int
}
// flags
const symbolWords = 1 << 0
const symbolScale = 1 << 3
const symbolContinue = 1 << 5
const symbolAllScale = 1 << 6
const symbol2x2 = 1 << 7
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
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) 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 (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{}
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)
cmap = append(cmap, cidArrayKeys...)
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 {
cmap = append(cmap, cidArray[start]...)
}
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 CutFont(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 outBuf
}