2023-05-31 23:30:11 +00:00
/ *
2023-06-01 01:37:14 +00:00
* The following code is part of https : //github.com/jung-kurt/gofpdf
2023-05-31 23:30:11 +00:00
*
* 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 font
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
type fontBoxType struct {
Xmin , Ymin , Xmax , Ymax int
}
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 { 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
2023-05-31 23:46:11 +00:00
data := [ ] int { 0 , 1 , 3 , 1 , 0 , 12 , 4 }
cmap := [ ] int { segCount * 2 , searchRange , entrySelector , rangeShift }
2023-05-31 23:30:11 +00:00
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 )
// Calculating cmap length based off of fpdf https://github.com/Setasign/FPDF/blob/f4104a04c9a3f95c4c26a0a0531abebcc980987a/makefont/ttfparser.php#L476
2023-06-01 01:30:21 +00:00
// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values
// The 3 extra bytes are for encodingID, offset, format, which are also part of the cmap
length := ( 3 + len ( cmap ) ) * 2
data = append ( data , length , 0 )
2023-05-31 23:46:11 +00:00
data = append ( data , cmap ... )
2023-05-31 23:30:11 +00:00
cmapstr := make ( [ ] byte , 0 )
2023-05-31 23:46:11 +00:00
for _ , cm := range data {
2023-05-31 23:30:11 +00:00
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
}