package sfnt
import (
"fmt"
"math"
"strconv"
"golang.org/x/image/math/fixed"
)
const (
psArgStackSize = 48
psCallStackSize = 10
)
func bigEndian(b []byte ) uint32 {
switch len (b ) {
case 1 :
return uint32 (b [0 ])
case 2 :
return uint32 (b [0 ])<<8 | uint32 (b [1 ])
case 3 :
return uint32 (b [0 ])<<16 | uint32 (b [1 ])<<8 | uint32 (b [2 ])
case 4 :
return uint32 (b [0 ])<<24 | uint32 (b [1 ])<<16 | uint32 (b [2 ])<<8 | uint32 (b [3 ])
}
panic ("unreachable" )
}
type fdSelect struct {
format uint8
numRanges uint16
offset int32
}
func (t *fdSelect ) lookup (f *Font , b *Buffer , x GlyphIndex ) (int , error ) {
switch t .format {
case 0 :
buf , err := b .view (&f .src , int (t .offset )+int (x ), 1 )
if err != nil {
return 0 , err
}
return int (buf [0 ]), nil
case 3 :
lo , hi := 0 , int (t .numRanges )
for lo < hi {
i := (lo + hi ) / 2
buf , err := b .view (&f .src , int (t .offset )+3 *i , 3 +2 )
if err != nil {
return 0 , err
}
if xlo := GlyphIndex (u16 (buf [0 :])); x < xlo {
hi = i
continue
}
if xhi := GlyphIndex (u16 (buf [3 :])); xhi <= x {
lo = i + 1
continue
}
return int (buf [2 ]), nil
}
}
return 0 , ErrNotFound
}
type cffParser struct {
src *source
base int
offset int
end int
err error
buf []byte
locBuf [2 ]uint32
psi psInterpreter
}
func (p *cffParser ) parse (numGlyphs int32 ) (ret glyphData , err error ) {
{
if !p .read (4 ) {
return glyphData {}, p .err
}
if p .buf [0 ] != 1 || p .buf [1 ] != 0 || p .buf [2 ] != 4 {
return glyphData {}, errUnsupportedCFFVersion
}
}
{
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count != 1 {
return glyphData {}, errInvalidCFFTable
}
if !p .parseIndexLocations (p .locBuf [:2 ], count , offSize ) {
return glyphData {}, p .err
}
p .offset = int (p .locBuf [1 ])
}
p .psi .topDict .initialize ()
{
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count != 1 {
return glyphData {}, errInvalidCFFTable
}
if !p .parseIndexLocations (p .locBuf [:2 ], count , offSize ) {
return glyphData {}, p .err
}
if !p .read (int (p .locBuf [1 ] - p .locBuf [0 ])) {
return glyphData {}, p .err
}
if p .err = p .psi .run (psContextTopDict , p .buf , 0 , 0 ); p .err != nil {
return glyphData {}, p .err
}
}
{
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count != 0 {
if !p .skip (int (count * offSize )) {
return glyphData {}, p .err
}
if !p .read (int (offSize )) {
return glyphData {}, p .err
}
loc := bigEndian (p .buf ) - 1
if uint32 (p .end -p .offset ) < loc {
return glyphData {}, errInvalidCFFTable
}
if !p .skip (int (loc )) {
return glyphData {}, p .err
}
}
}
{
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count != 0 {
if count > maxNumSubroutines {
return glyphData {}, errUnsupportedNumberOfSubroutines
}
ret .gsubrs = make ([]uint32 , count +1 )
if !p .parseIndexLocations (ret .gsubrs , count , offSize ) {
return glyphData {}, p .err
}
}
}
{
if !p .seekFromBase (p .psi .topDict .charStringsOffset ) {
return glyphData {}, errInvalidCFFTable
}
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count == 0 || int32 (count ) != numGlyphs {
return glyphData {}, errInvalidCFFTable
}
ret .locations = make ([]uint32 , count +1 )
if !p .parseIndexLocations (ret .locations , count , offSize ) {
return glyphData {}, p .err
}
}
if !p .psi .topDict .isCIDFont {
ret .singleSubrs , err = p .parsePrivateDICT (
p .psi .topDict .privateDictOffset ,
p .psi .topDict .privateDictLength ,
)
if err != nil {
return glyphData {}, err
}
} else {
ret .fdSelect , err = p .parseFDSelect (p .psi .topDict .fdSelect , numGlyphs )
if err != nil {
return glyphData {}, err
}
if !p .seekFromBase (p .psi .topDict .fdArray ) {
return glyphData {}, errInvalidCFFTable
}
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return glyphData {}, p .err
}
if count > maxNumFontDicts {
return glyphData {}, errUnsupportedNumberOfFontDicts
}
fdLocations := make ([]uint32 , count +1 )
if !p .parseIndexLocations (fdLocations , count , offSize ) {
return glyphData {}, p .err
}
privateDicts := make ([]struct {
offset , length int32
}, count )
for i := range privateDicts {
length := fdLocations [i +1 ] - fdLocations [i ]
if !p .read (int (length )) {
return glyphData {}, errInvalidCFFTable
}
p .psi .topDict .initialize ()
if p .err = p .psi .run (psContextTopDict , p .buf , 0 , 0 ); p .err != nil {
return glyphData {}, p .err
}
privateDicts [i ].offset = p .psi .topDict .privateDictOffset
privateDicts [i ].length = p .psi .topDict .privateDictLength
}
ret .multiSubrs = make ([][]uint32 , count )
for i , pd := range privateDicts {
ret .multiSubrs [i ], err = p .parsePrivateDICT (pd .offset , pd .length )
if err != nil {
return glyphData {}, err
}
}
}
return ret , err
}
func (p *cffParser ) parseFDSelect (offset int32 , numGlyphs int32 ) (ret fdSelect , err error ) {
if !p .seekFromBase (p .psi .topDict .fdSelect ) {
return fdSelect {}, errInvalidCFFTable
}
if !p .read (1 ) {
return fdSelect {}, p .err
}
ret .format = p .buf [0 ]
switch ret .format {
case 0 :
if p .end -p .offset < int (numGlyphs ) {
return fdSelect {}, errInvalidCFFTable
}
ret .offset = int32 (p .offset )
return ret , nil
case 3 :
if !p .read (2 ) {
return fdSelect {}, p .err
}
ret .numRanges = u16 (p .buf )
if p .end -p .offset < 3 *int (ret .numRanges )+2 {
return fdSelect {}, errInvalidCFFTable
}
ret .offset = int32 (p .offset )
return ret , nil
}
return fdSelect {}, errUnsupportedCFFFDSelectTable
}
func (p *cffParser ) parsePrivateDICT (offset , length int32 ) (subrs []uint32 , err error ) {
p .psi .privateDict .initialize ()
if length != 0 {
fullLength := int32 (p .end - p .base )
if offset <= 0 || fullLength < offset || fullLength -offset < length || length < 0 {
return nil , errInvalidCFFTable
}
p .offset = p .base + int (offset )
if !p .read (int (length )) {
return nil , p .err
}
if p .err = p .psi .run (psContextPrivateDict , p .buf , 0 , 0 ); p .err != nil {
return nil , p .err
}
}
if p .psi .privateDict .subrsOffset != 0 {
if !p .seekFromBase (offset + p .psi .privateDict .subrsOffset ) {
return nil , errInvalidCFFTable
}
count , offSize , ok := p .parseIndexHeader ()
if !ok {
return nil , p .err
}
if count != 0 {
if count > maxNumSubroutines {
return nil , errUnsupportedNumberOfSubroutines
}
subrs = make ([]uint32 , count +1 )
if !p .parseIndexLocations (subrs , count , offSize ) {
return nil , p .err
}
}
}
return subrs , err
}
func (p *cffParser ) read (n int ) (ok bool ) {
if n < 0 || p .end -p .offset < n {
p .err = errInvalidCFFTable
return false
}
p .buf , p .err = p .src .view (p .buf , p .offset , n )
p .offset += n
return p .err == nil
}
func (p *cffParser ) skip (n int ) (ok bool ) {
if p .end -p .offset < n {
p .err = errInvalidCFFTable
return false
}
p .offset += n
return true
}
func (p *cffParser ) seekFromBase (offset int32 ) (ok bool ) {
if offset < 0 || int32 (p .end -p .base ) < offset {
return false
}
p .offset = p .base + int (offset )
return true
}
func (p *cffParser ) parseIndexHeader () (count , offSize int32 , ok bool ) {
if !p .read (2 ) {
return 0 , 0 , false
}
count = int32 (u16 (p .buf [:2 ]))
if count == 0 {
return count , 0 , true
}
if !p .read (1 ) {
return 0 , 0 , false
}
offSize = int32 (p .buf [0 ])
if offSize < 1 || 4 < offSize {
p .err = errInvalidCFFTable
return 0 , 0 , false
}
return count , offSize , true
}
func (p *cffParser ) parseIndexLocations (dst []uint32 , count , offSize int32 ) (ok bool ) {
if count == 0 {
return true
}
if len (dst ) != int (count +1 ) {
panic ("unreachable" )
}
if !p .read (len (dst ) * int (offSize )) {
return false
}
buf , prev := p .buf , uint32 (0 )
for i := range dst {
loc := bigEndian (buf [:offSize ])
buf = buf [offSize :]
if loc == 0 {
p .err = errInvalidCFFTable
return false
}
loc --
if i == 0 {
if loc != 0 {
p .err = errInvalidCFFTable
break
}
} else if loc <= prev {
p .err = errInvalidCFFTable
break
}
if uint32 (p .end -p .offset ) < loc {
p .err = errInvalidCFFTable
break
}
dst [i ] = uint32 (p .offset ) + loc
prev = loc
}
return p .err == nil
}
type psCallStackEntry struct {
offset, length uint32
}
type psContext uint32
const (
psContextTopDict psContext = iota
psContextPrivateDict
psContextType2Charstring
)
type psTopDictData struct {
charStringsOffset int32
fdArray int32
fdSelect int32
isCIDFont bool
privateDictOffset int32
privateDictLength int32
}
func (d *psTopDictData ) initialize () {
*d = psTopDictData {}
}
type psPrivateDictData struct {
subrsOffset int32
}
func (d *psPrivateDictData ) initialize () {
*d = psPrivateDictData {}
}
type psType2CharstringsData struct {
f *Font
b *Buffer
x int32
y int32
firstX int32
firstY int32
hintBits int32
seenWidth bool
ended bool
glyphIndex GlyphIndex
fdSelectIndexPlusOne int32
}
func (d *psType2CharstringsData ) initialize (f *Font , b *Buffer , glyphIndex GlyphIndex ) {
*d = psType2CharstringsData {
f : f ,
b : b ,
glyphIndex : glyphIndex ,
}
}
func (d *psType2CharstringsData ) closePath () {
if d .x != d .firstX || d .y != d .firstY {
d .b .segments = append (d .b .segments , Segment {
Op : SegmentOpLineTo ,
Args : [3 ]fixed .Point26_6 {{
X : fixed .Int26_6 (d .firstX ),
Y : fixed .Int26_6 (d .firstY ),
}},
})
}
}
func (d *psType2CharstringsData ) moveTo (dx , dy int32 ) {
d .closePath ()
d .x += dx
d .y += dy
d .b .segments = append (d .b .segments , Segment {
Op : SegmentOpMoveTo ,
Args : [3 ]fixed .Point26_6 {{
X : fixed .Int26_6 (d .x ),
Y : fixed .Int26_6 (d .y ),
}},
})
d .firstX = d .x
d .firstY = d .y
}
func (d *psType2CharstringsData ) lineTo (dx , dy int32 ) {
d .x += dx
d .y += dy
d .b .segments = append (d .b .segments , Segment {
Op : SegmentOpLineTo ,
Args : [3 ]fixed .Point26_6 {{
X : fixed .Int26_6 (d .x ),
Y : fixed .Int26_6 (d .y ),
}},
})
}
func (d *psType2CharstringsData ) cubeTo (dxa , dya , dxb , dyb , dxc , dyc int32 ) {
d .x += dxa
d .y += dya
xa := fixed .Int26_6 (d .x )
ya := fixed .Int26_6 (d .y )
d .x += dxb
d .y += dyb
xb := fixed .Int26_6 (d .x )
yb := fixed .Int26_6 (d .y )
d .x += dxc
d .y += dyc
xc := fixed .Int26_6 (d .x )
yc := fixed .Int26_6 (d .y )
d .b .segments = append (d .b .segments , Segment {
Op : SegmentOpCubeTo ,
Args : [3 ]fixed .Point26_6 {{X : xa , Y : ya }, {X : xb , Y : yb }, {X : xc , Y : yc }},
})
}
type psInterpreter struct {
ctx psContext
instructions []byte
instrOffset uint32
instrLength uint32
argStack struct {
a [psArgStackSize ]int32
top int32
}
callStack struct {
a [psCallStackSize ]psCallStackEntry
top int32
}
parseNumberBuf [maxRealNumberStrLen ]byte
topDict psTopDictData
privateDict psPrivateDictData
type2Charstrings psType2CharstringsData
}
func (p *psInterpreter ) hasMoreInstructions () bool {
if len (p .instructions ) != 0 {
return true
}
for i := int32 (0 ); i < p .callStack .top ; i ++ {
if p .callStack .a [i ].length != 0 {
return true
}
}
return false
}
func (p *psInterpreter ) run (ctx psContext , instructions []byte , offset , length uint32 ) error {
p .ctx = ctx
p .instructions = instructions
p .instrOffset = offset
p .instrLength = length
p .argStack .top = 0
p .callStack .top = 0
loop :
for len (p .instructions ) > 0 {
if hasResult , err := p .parseNumber (); hasResult {
if err != nil {
return err
}
continue
}
b := p .instructions [0 ]
p .instructions = p .instructions [1 :]
for escaped , ops := false , psOperators [ctx ][0 ]; ; {
if b == escapeByte && !escaped {
if len (p .instructions ) <= 0 {
return errInvalidCFFTable
}
b = p .instructions [0 ]
p .instructions = p .instructions [1 :]
escaped = true
ops = psOperators [ctx ][1 ]
continue
}
if int (b ) < len (ops ) {
if op := ops [b ]; op .name != "" {
if p .argStack .top < op .numPop {
return errInvalidCFFTable
}
if op .run != nil {
if err := op .run (p ); err != nil {
return err
}
}
if op .numPop < 0 {
p .argStack .top = 0
} else {
p .argStack .top -= op .numPop
}
continue loop
}
}
if escaped {
return fmt .Errorf ("sfnt: unrecognized CFF 2-byte operator (12 %d)" , b )
} else {
return fmt .Errorf ("sfnt: unrecognized CFF 1-byte operator (%d)" , b )
}
}
}
return nil
}
func (p *psInterpreter ) parseNumber () (hasResult bool , err error ) {
number := int32 (0 )
switch b := p .instructions [0 ]; {
case b == 28 :
if len (p .instructions ) < 3 {
return true , errInvalidCFFTable
}
number , hasResult = int32 (int16 (u16 (p .instructions [1 :]))), true
p .instructions = p .instructions [3 :]
case b == 29 && p .ctx != psContextType2Charstring :
if len (p .instructions ) < 5 {
return true , errInvalidCFFTable
}
number , hasResult = int32 (u32 (p .instructions [1 :])), true
p .instructions = p .instructions [5 :]
case b == 30 && p .ctx != psContextType2Charstring :
s := p .parseNumberBuf [:0 ]
p .instructions = p .instructions [1 :]
loop :
for {
if len (p .instructions ) == 0 {
return true , errInvalidCFFTable
}
b := p .instructions [0 ]
p .instructions = p .instructions [1 :]
for i := 0 ; i < 2 ; i ++ {
nib := b >> 4
b = b << 4
if nib == 0x0f {
f , err := strconv .ParseFloat (string (s ), 32 )
if err != nil {
return true , errInvalidCFFTable
}
number , hasResult = int32 (math .Float32bits (float32 (f ))), true
break loop
}
if nib == 0x0d {
return true , errInvalidCFFTable
}
if len (s )+maxNibbleDefsLength > len (p .parseNumberBuf ) {
return true , errUnsupportedRealNumberEncoding
}
s = append (s , nibbleDefs [nib ]...)
}
}
case b < 32 :
case b < 247 :
p .instructions = p .instructions [1 :]
number , hasResult = int32 (b )-139 , true
case b < 251 :
if len (p .instructions ) < 2 {
return true , errInvalidCFFTable
}
b1 := p .instructions [1 ]
p .instructions = p .instructions [2 :]
number , hasResult = +int32 (b -247 )*256 +int32 (b1 )+108 , true
case b < 255 :
if len (p .instructions ) < 2 {
return true , errInvalidCFFTable
}
b1 := p .instructions [1 ]
p .instructions = p .instructions [2 :]
number , hasResult = -int32 (b -251 )*256 -int32 (b1 )-108 , true
case b == 255 && p .ctx == psContextType2Charstring :
if len (p .instructions ) < 5 {
return true , errInvalidCFFTable
}
number , hasResult = int32 (u32 (p .instructions [1 :])), true
p .instructions = p .instructions [5 :]
number = (number >> 16 ) + (1 & (number >> 15 ))
}
if hasResult {
if p .argStack .top == psArgStackSize {
return true , errInvalidCFFTable
}
p .argStack .a [p .argStack .top ] = number
p .argStack .top ++
}
return hasResult , nil
}
const maxNibbleDefsLength = len ("E-" )
var nibbleDefs = [16 ]string {
0x00 : "0" ,
0x01 : "1" ,
0x02 : "2" ,
0x03 : "3" ,
0x04 : "4" ,
0x05 : "5" ,
0x06 : "6" ,
0x07 : "7" ,
0x08 : "8" ,
0x09 : "9" ,
0x0a : "." ,
0x0b : "E" ,
0x0c : "E-" ,
0x0d : "" ,
0x0e : "-" ,
0x0f : "" ,
}
type psOperator struct {
numPop int32
name string
run func (*psInterpreter ) error
}
var psOperators = [...][2 ][]psOperator {
psContextTopDict : {{
0 : {+1 , "version" , nil },
1 : {+1 , "Notice" , nil },
2 : {+1 , "FullName" , nil },
3 : {+1 , "FamilyName" , nil },
4 : {+1 , "Weight" , nil },
5 : {-1 , "FontBBox" , nil },
13 : {+1 , "UniqueID" , nil },
14 : {-1 , "XUID" , nil },
15 : {+1 , "charset" , nil },
16 : {+1 , "Encoding" , nil },
17 : {+1 , "CharStrings" , func (p *psInterpreter ) error {
p .topDict .charStringsOffset = p .argStack .a [p .argStack .top -1 ]
return nil
}},
18 : {+2 , "Private" , func (p *psInterpreter ) error {
p .topDict .privateDictLength = p .argStack .a [p .argStack .top -2 ]
p .topDict .privateDictOffset = p .argStack .a [p .argStack .top -1 ]
return nil
}},
}, {
0 : {+1 , "Copyright" , nil },
1 : {+1 , "isFixedPitch" , nil },
2 : {+1 , "ItalicAngle" , nil },
3 : {+1 , "UnderlinePosition" , nil },
4 : {+1 , "UnderlineThickness" , nil },
5 : {+1 , "PaintType" , nil },
6 : {+1 , "CharstringType" , nil },
7 : {-1 , "FontMatrix" , nil },
8 : {+1 , "StrokeWidth" , nil },
20 : {+1 , "SyntheticBase" , nil },
21 : {+1 , "PostScript" , nil },
22 : {+1 , "BaseFontName" , nil },
23 : {-2 , "BaseFontBlend" , nil },
30 : {+3 , "ROS" , func (p *psInterpreter ) error {
p .topDict .isCIDFont = true
return nil
}},
31 : {+1 , "CIDFontVersion" , nil },
32 : {+1 , "CIDFontRevision" , nil },
33 : {+1 , "CIDFontType" , nil },
34 : {+1 , "CIDCount" , nil },
35 : {+1 , "UIDBase" , nil },
36 : {+1 , "FDArray" , func (p *psInterpreter ) error {
p .topDict .fdArray = p .argStack .a [p .argStack .top -1 ]
return nil
}},
37 : {+1 , "FDSelect" , func (p *psInterpreter ) error {
p .topDict .fdSelect = p .argStack .a [p .argStack .top -1 ]
return nil
}},
38 : {+1 , "FontName" , nil },
}},
psContextPrivateDict : {{
6 : {-2 , "BlueValues" , nil },
7 : {-2 , "OtherBlues" , nil },
8 : {-2 , "FamilyBlues" , nil },
9 : {-2 , "FamilyOtherBlues" , nil },
10 : {+1 , "StdHW" , nil },
11 : {+1 , "StdVW" , nil },
19 : {+1 , "Subrs" , func (p *psInterpreter ) error {
p .privateDict .subrsOffset = p .argStack .a [p .argStack .top -1 ]
return nil
}},
20 : {+1 , "defaultWidthX" , nil },
21 : {+1 , "nominalWidthX" , nil },
}, {
9 : {+1 , "BlueScale" , nil },
10 : {+1 , "BlueShift" , nil },
11 : {+1 , "BlueFuzz" , nil },
12 : {-2 , "StemSnapH" , nil },
13 : {-2 , "StemSnapV" , nil },
14 : {+1 , "ForceBold" , nil },
17 : {+1 , "LanguageGroup" , nil },
18 : {+1 , "ExpansionFactor" , nil },
19 : {+1 , "initialRandomSeed" , nil },
}},
psContextType2Charstring : {{
0 : {},
1 : {-1 , "hstem" , t2CStem },
2 : {},
3 : {-1 , "vstem" , t2CStem },
4 : {-1 , "vmoveto" , t2CVmoveto },
5 : {-1 , "rlineto" , t2CRlineto },
6 : {-1 , "hlineto" , t2CHlineto },
7 : {-1 , "vlineto" , t2CVlineto },
8 : {-1 , "rrcurveto" , t2CRrcurveto },
9 : {},
10 : {+1 , "callsubr" , t2CCallsubr },
11 : {+0 , "return" , t2CReturn },
12 : {},
13 : {},
14 : {-1 , "endchar" , t2CEndchar },
15 : {},
16 : {},
17 : {},
18 : {-1 , "hstemhm" , t2CStem },
19 : {-1 , "hintmask" , t2CMask },
20 : {-1 , "cntrmask" , t2CMask },
21 : {-1 , "rmoveto" , t2CRmoveto },
22 : {-1 , "hmoveto" , t2CHmoveto },
23 : {-1 , "vstemhm" , t2CStem },
24 : {-1 , "rcurveline" , t2CRcurveline },
25 : {-1 , "rlinecurve" , t2CRlinecurve },
26 : {-1 , "vvcurveto" , t2CVvcurveto },
27 : {-1 , "hhcurveto" , t2CHhcurveto },
28 : {},
29 : {+1 , "callgsubr" , t2CCallgsubr },
30 : {-1 , "vhcurveto" , t2CVhcurveto },
31 : {-1 , "hvcurveto" , t2CHvcurveto },
}, {
34 : {+7 , "hflex" , t2CHflex },
36 : {+9 , "hflex1" , t2CHflex1 },
}},
}
const escapeByte = 12
func t2CReadWidth(p *psInterpreter , nArgs int32 ) {
if p .type2Charstrings .seenWidth {
return
}
p .type2Charstrings .seenWidth = true
if nArgs >= 0 {
if p .argStack .top != nArgs +1 {
return
}
} else if p .argStack .top &1 == 0 {
return
}
copy (p .argStack .a [:p .argStack .top -1 ], p .argStack .a [1 :p .argStack .top ])
p .argStack .top --
}
func t2CStem(p *psInterpreter ) error {
t2CReadWidth (p , -1 )
if p .argStack .top %2 != 0 {
return errInvalidCFFTable
}
p .type2Charstrings .hintBits += p .argStack .top / 2
if p .type2Charstrings .hintBits > maxHintBits {
return errUnsupportedNumberOfHints
}
return nil
}
func t2CMask(p *psInterpreter ) error {
if p .argStack .top != 0 {
if err := t2CStem (p ); err != nil {
return err
}
} else if !p .type2Charstrings .seenWidth {
p .type2Charstrings .seenWidth = true
}
hintBytes := (p .type2Charstrings .hintBits + 7 ) / 8
if len (p .instructions ) < int (hintBytes ) {
return errInvalidCFFTable
}
p .instructions = p .instructions [hintBytes :]
return nil
}
func t2CHmoveto(p *psInterpreter ) error {
t2CReadWidth (p , 1 )
if p .argStack .top != 1 {
return errInvalidCFFTable
}
p .type2Charstrings .moveTo (p .argStack .a [0 ], 0 )
return nil
}
func t2CVmoveto(p *psInterpreter ) error {
t2CReadWidth (p , 1 )
if p .argStack .top != 1 {
return errInvalidCFFTable
}
p .type2Charstrings .moveTo (0 , p .argStack .a [0 ])
return nil
}
func t2CRmoveto(p *psInterpreter ) error {
t2CReadWidth (p , 2 )
if p .argStack .top != 2 {
return errInvalidCFFTable
}
p .type2Charstrings .moveTo (p .argStack .a [0 ], p .argStack .a [1 ])
return nil
}
func t2CHlineto(p *psInterpreter ) error { return t2CLineto (p , false ) }
func t2CVlineto(p *psInterpreter ) error { return t2CLineto (p , true ) }
func t2CLineto(p *psInterpreter , vertical bool ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 1 {
return errInvalidCFFTable
}
for i := int32 (0 ); i < p .argStack .top ; i , vertical = i +1 , !vertical {
dx , dy := p .argStack .a [i ], int32 (0 )
if vertical {
dx , dy = dy , dx
}
p .type2Charstrings .lineTo (dx , dy )
}
return nil
}
func t2CRlineto(p *psInterpreter ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 2 || p .argStack .top %2 != 0 {
return errInvalidCFFTable
}
for i := int32 (0 ); i < p .argStack .top ; i += 2 {
p .type2Charstrings .lineTo (p .argStack .a [i ], p .argStack .a [i +1 ])
}
return nil
}
func t2CRcurveline(p *psInterpreter ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 8 || p .argStack .top %6 != 2 {
return errInvalidCFFTable
}
i := int32 (0 )
for iMax := p .argStack .top - 2 ; i < iMax ; i += 6 {
p .type2Charstrings .cubeTo (
p .argStack .a [i +0 ],
p .argStack .a [i +1 ],
p .argStack .a [i +2 ],
p .argStack .a [i +3 ],
p .argStack .a [i +4 ],
p .argStack .a [i +5 ],
)
}
p .type2Charstrings .lineTo (p .argStack .a [i ], p .argStack .a [i +1 ])
return nil
}
func t2CRlinecurve(p *psInterpreter ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 8 || p .argStack .top %2 != 0 {
return errInvalidCFFTable
}
i := int32 (0 )
for iMax := p .argStack .top - 6 ; i < iMax ; i += 2 {
p .type2Charstrings .lineTo (p .argStack .a [i ], p .argStack .a [i +1 ])
}
p .type2Charstrings .cubeTo (
p .argStack .a [i +0 ],
p .argStack .a [i +1 ],
p .argStack .a [i +2 ],
p .argStack .a [i +3 ],
p .argStack .a [i +4 ],
p .argStack .a [i +5 ],
)
return nil
}
func t2CHhcurveto(p *psInterpreter ) error { return t2CCurveto (p , false , false ) }
func t2CVvcurveto(p *psInterpreter ) error { return t2CCurveto (p , false , true ) }
func t2CHvcurveto(p *psInterpreter ) error { return t2CCurveto (p , true , false ) }
func t2CVhcurveto(p *psInterpreter ) error { return t2CCurveto (p , true , true ) }
func t2CCurveto(p *psInterpreter , swap , vertical bool ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 4 {
return errInvalidCFFTable
}
i := int32 (0 )
switch p .argStack .top & 3 {
case 0 :
case 1 :
if swap {
break
}
i = 1
if vertical {
p .type2Charstrings .x += p .argStack .a [0 ]
} else {
p .type2Charstrings .y += p .argStack .a [0 ]
}
default :
return errInvalidCFFTable
}
for i != p .argStack .top {
i = t2CCurveto4 (p , swap , vertical , i )
if i < 0 {
return errInvalidCFFTable
}
if swap {
vertical = !vertical
}
}
return nil
}
func t2CCurveto4(p *psInterpreter , swap bool , vertical bool , i int32 ) (j int32 ) {
if i +4 > p .argStack .top {
return -1
}
dxa := p .argStack .a [i +0 ]
dya := int32 (0 )
dxb := p .argStack .a [i +1 ]
dyb := p .argStack .a [i +2 ]
dxc := p .argStack .a [i +3 ]
dyc := int32 (0 )
i += 4
if vertical {
dxa , dya = dya , dxa
}
if swap {
if i +1 == p .argStack .top {
dyc = p .argStack .a [i ]
i ++
}
}
if swap != vertical {
dxc , dyc = dyc , dxc
}
p .type2Charstrings .cubeTo (dxa , dya , dxb , dyb , dxc , dyc )
return i
}
func t2CRrcurveto(p *psInterpreter ) error {
if !p .type2Charstrings .seenWidth || p .argStack .top < 6 || p .argStack .top %6 != 0 {
return errInvalidCFFTable
}
for i := int32 (0 ); i != p .argStack .top ; i += 6 {
p .type2Charstrings .cubeTo (
p .argStack .a [i +0 ],
p .argStack .a [i +1 ],
p .argStack .a [i +2 ],
p .argStack .a [i +3 ],
p .argStack .a [i +4 ],
p .argStack .a [i +5 ],
)
}
return nil
}
func t2CHflex(p *psInterpreter ) error {
p .type2Charstrings .cubeTo (
p .argStack .a [0 ], 0 ,
p .argStack .a [1 ], +p .argStack .a [2 ],
p .argStack .a [3 ], 0 ,
)
p .type2Charstrings .cubeTo (
p .argStack .a [4 ], 0 ,
p .argStack .a [5 ], -p .argStack .a [2 ],
p .argStack .a [6 ], 0 ,
)
return nil
}
func t2CHflex1(p *psInterpreter ) error {
dy1 := p .argStack .a [1 ]
dy2 := p .argStack .a [3 ]
dy5 := p .argStack .a [7 ]
dy6 := -dy1 - dy2 - dy5
p .type2Charstrings .cubeTo (
p .argStack .a [0 ], dy1 ,
p .argStack .a [2 ], dy2 ,
p .argStack .a [4 ], 0 ,
)
p .type2Charstrings .cubeTo (
p .argStack .a [5 ], 0 ,
p .argStack .a [6 ], dy5 ,
p .argStack .a [8 ], dy6 ,
)
return nil
}
func subrBias(numSubroutines int ) int32 {
if numSubroutines < 1240 {
return 107
}
if numSubroutines < 33900 {
return 1131
}
return 32768
}
func t2CCallgsubr(p *psInterpreter ) error {
return t2CCall (p , p .type2Charstrings .f .cached .glyphData .gsubrs )
}
func t2CCallsubr(p *psInterpreter ) error {
t := &p .type2Charstrings
d := &t .f .cached .glyphData
subrs := d .singleSubrs
if d .multiSubrs != nil {
if t .fdSelectIndexPlusOne == 0 {
index , err := d .fdSelect .lookup (t .f , t .b , t .glyphIndex )
if err != nil {
return err
}
if index < 0 || len (d .multiSubrs ) <= index {
return errInvalidCFFTable
}
t .fdSelectIndexPlusOne = int32 (index + 1 )
}
subrs = d .multiSubrs [t .fdSelectIndexPlusOne -1 ]
}
return t2CCall (p , subrs )
}
func t2CCall(p *psInterpreter , subrs []uint32 ) error {
if p .callStack .top == psCallStackSize || len (subrs ) == 0 {
return errInvalidCFFTable
}
length := uint32 (len (p .instructions ))
p .callStack .a [p .callStack .top ] = psCallStackEntry {
offset : p .instrOffset + p .instrLength - length ,
length : length ,
}
p .callStack .top ++
subrIndex := p .argStack .a [p .argStack .top -1 ] + subrBias (len (subrs )-1 )
if subrIndex < 0 || int32 (len (subrs )-1 ) <= subrIndex {
return errInvalidCFFTable
}
i := subrs [subrIndex +0 ]
j := subrs [subrIndex +1 ]
if j < i {
return errInvalidCFFTable
}
if j -i > maxGlyphDataLength {
return errUnsupportedGlyphDataLength
}
buf , err := p .type2Charstrings .b .view (&p .type2Charstrings .f .src , int (i ), int (j -i ))
if err != nil {
return err
}
p .instructions = buf
p .instrOffset = i
p .instrLength = j - i
return nil
}
func t2CReturn(p *psInterpreter ) error {
if p .callStack .top <= 0 {
return errInvalidCFFTable
}
p .callStack .top --
o := p .callStack .a [p .callStack .top ].offset
n := p .callStack .a [p .callStack .top ].length
buf , err := p .type2Charstrings .b .view (&p .type2Charstrings .f .src , int (o ), int (n ))
if err != nil {
return err
}
p .instructions = buf
p .instrOffset = o
p .instrLength = n
return nil
}
func t2CEndchar(p *psInterpreter ) error {
t2CReadWidth (p , 0 )
if p .argStack .top != 0 || p .hasMoreInstructions () {
if p .argStack .top == 4 {
return errUnsupportedType2Charstring
}
return errInvalidCFFTable
}
p .type2Charstrings .closePath ()
p .type2Charstrings .ended = true
return nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .