package cff
import (
"encoding/binary"
"errors"
"fmt"
ps "github.com/go-text/typesetting/font/cff/interpreter"
"github.com/go-text/typesetting/font/opentype/tables"
)
type CFF2 struct {
fdSelect fdSelect
Charstrings [][]byte
globalSubrs [][]byte
fonts []privateFonts
VarStore tables .ItemVarStore
}
type privateFonts struct {
localSubrs [][]byte
defaultVSIndex int32
}
func ParseCFF2 (src []byte ) (*CFF2 , error ) {
if L := len (src ); L < 5 {
return nil , fmt .Errorf ("reading header: EOF: expected length: 5, got %d" , L )
}
var header header2
header .mustParse (src )
topDictEnd := int (header .headerSize ) + int (header .topDictLength )
if L := len (src ); L < topDictEnd {
return nil , fmt .Errorf ("reading topDict: EOF: expected length: %d, got %d" , topDictEnd , L )
}
topDictSrc := src [header .headerSize :topDictEnd ]
var (
tp topDict2
psi ps .Machine
)
if err := psi .Run (topDictSrc , nil , nil , &tp ); err != nil {
return nil , fmt .Errorf ("reading top dict: %s" , err )
}
var (
out CFF2
err error
)
out .globalSubrs , err = parseIndex2 (src , topDictEnd )
if err != nil {
return nil , err
}
out .Charstrings , err = parseIndex2 (src , int (tp .charStrings ))
if err != nil {
return nil , err
}
fdIndex , err := parseIndex2 (src , int (tp .fdArray ))
if err != nil {
return nil , err
}
out .fonts = make ([]privateFonts , len (fdIndex ))
for i , font := range fdIndex {
var fd fontDict2
err = psi .Run (font , nil , nil , &fd )
if err != nil {
return nil , fmt .Errorf ("reading font dict: %s" , err )
}
end := int (fd .privateDictOffset + fd .privateDictSize )
if L := len (src ); L < end {
return nil , fmt .Errorf ("reading private dict: EOF: expected length: %d, got %d" , end , L )
}
var pd privateDict2
err = psi .Run (src [fd .privateDictOffset :end ], nil , nil , &pd )
if err != nil {
return nil , fmt .Errorf ("reading private dict: %s" , err )
}
out .fonts [i ].defaultVSIndex = pd .vsindex
if pd .subrsOffset != 0 {
out .fonts [i ].localSubrs , err = parseIndex2 (src , int (pd .subrsOffset ))
if err != nil {
return nil , err
}
}
}
if len (fdIndex ) > 1 {
if L := len (src ); L < int (tp .fdSelect ) {
return nil , fmt .Errorf ("reading fdSelect: EOF: expected length: %d, got %d" , tp .fdSelect , L )
}
out .fdSelect , _, err = parseFdSelect (src [tp .fdSelect :], len (out .Charstrings ))
if err != nil {
return nil , err
}
indexExtent := out .fdSelect .extent ()
if len (fdIndex ) < indexExtent {
return nil , fmt .Errorf ("invalid number of font dicts: %d (for %d)" , len (fdIndex ), indexExtent )
}
}
if tp .vstore != 0 {
if E , L := int (tp .vstore )+2 , len (src ); L < E {
return nil , fmt .Errorf ("reading variation store: EOF: expected length: %d, got %d" , E , L )
}
size := int (binary .BigEndian .Uint16 (src [tp .vstore :]))
end := int (tp .vstore ) + 2 + size
if L := len (src ); L < end {
return nil , fmt .Errorf ("reading variation store: EOF: expected length: %d, got %d" , end , L )
}
vstore := src [tp .vstore +2 : end ]
out .VarStore , _, err = tables .ParseItemVarStore (vstore )
if err != nil {
return nil , err
}
}
return &out , nil
}
func parseIndex2(src []byte , offset int ) ([][]byte , error ) {
if L := len (src ); L < offset +5 {
return nil , fmt .Errorf ("reading INDEX: EOF: expected length: %d, got %d" , offset +5 , L )
}
var is indexStart
is .mustParse (src [offset :])
out , _ , err := parseIndexContent (src [offset +5 :], is )
return out , err
}
type topDict2 struct {
charStrings int32
fdArray int32
fdSelect int32
vstore int32
}
func (tp *topDict2 ) Context () ps .Context { return ps .TopDict }
func (tp *topDict2 ) Apply (state *ps .Machine , op ps .Operator ) error {
switch op {
case ps .Operator {Operator : 7 , IsEscaped : true }:
state .ArgStack .Clear ()
return nil
case ps .Operator {Operator : 17 , IsEscaped : false }:
if state .ArgStack .Top < 1 {
return fmt .Errorf ("invalid number of arguments for operator %s in Top Dict" , op )
}
tp .charStrings = int32 (state .ArgStack .Pop ())
case ps .Operator {Operator : 36 , IsEscaped : true }:
if state .ArgStack .Top < 1 {
return fmt .Errorf ("invalid number of arguments for operator %s in Top Dict" , op )
}
tp .fdArray = int32 (state .ArgStack .Pop ())
case ps .Operator {Operator : 37 , IsEscaped : true }:
if state .ArgStack .Top < 1 {
return fmt .Errorf ("invalid number of arguments for operator %s in Top Dict" , op )
}
tp .fdSelect = int32 (state .ArgStack .Pop ())
case ps .Operator {Operator : 24 , IsEscaped : false }:
if state .ArgStack .Top < 1 {
return fmt .Errorf ("invalid number of arguments for operator %s in Top Dict" , op )
}
tp .vstore = int32 (state .ArgStack .Pop ())
default :
return fmt .Errorf ("invalid operator %s in Top Dict" , op )
}
return nil
}
type fontDict2 struct {
privateDictSize int32
privateDictOffset int32
}
func (fd *fontDict2 ) Context () ps .Context { return ps .TopDict }
func (fd *fontDict2 ) Apply (state *ps .Machine , op ps .Operator ) error {
switch op {
case ps .Operator {Operator : 18 , IsEscaped : false }:
if state .ArgStack .Top < 2 {
return fmt .Errorf ("invalid number of arguments for operator %s in Font Dict" , op )
}
fd .privateDictOffset = int32 (state .ArgStack .Pop ())
fd .privateDictSize = int32 (state .ArgStack .Pop ())
return nil
default :
return fmt .Errorf ("invalid operator %s in Font Dict" , op )
}
}
type privateDict2 struct {
subrsOffset int32
vsindex int32
}
func (privateDict2 ) Context () ps .Context { return ps .PrivateDict }
func (priv *privateDict2 ) Apply (state *ps .Machine , op ps .Operator ) error {
if !op .IsEscaped {
switch op .Operator {
case 6 , 7 , 8 , 9 :
return state .ArgStack .PopN (-2 )
case 10 , 11 :
return state .ArgStack .PopN (1 )
case 19 :
if state .ArgStack .Top < 1 {
return errors .New ("invalid stack size for 'subrs' in private Dict charstring" )
}
priv .subrsOffset = int32 (state .ArgStack .Pop ())
return nil
case 22 :
if state .ArgStack .Top < 1 {
return fmt .Errorf ("invalid stack size for %s in private Dict" , op )
}
priv .vsindex = int32 (state .ArgStack .Pop ())
return nil
case 23 :
return nil
}
} else {
switch op .Operator {
case 9 , 10 , 11 , 17 , 18 :
return state .ArgStack .PopN (1 )
case 12 , 13 :
return state .ArgStack .PopN (-2 )
}
}
return errors .New ("invalid operand in private Dict charstring" )
}
The pages are generated with Golds v0.8.7 . (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 .