github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/cff/charstring.go (about) 1 package cff 2 3 import ( 4 "fmt" 5 6 "github.com/benoitkugler/textlayout/fonts" 7 ps "github.com/benoitkugler/textlayout/fonts/psinterpreter" 8 ) 9 10 type Glyph struct { 11 Outlines []fonts.Segment 12 Width int 13 Bounds ps.PathBounds 14 } 15 16 // LoadGlyph parses the glyph charstring to compute segments and path bounds. 17 // It returns an error if the glyph is invalid or if decoding the charstring fails. 18 func (f *Font) LoadGlyph(glyph fonts.GID) (Glyph, error) { 19 var ( 20 psi ps.Machine 21 loader type2CharstringHandler 22 index byte = 0 23 err error 24 ) 25 if f.fdSelect != nil { 26 index, err = f.fdSelect.fontDictIndex(glyph) 27 if err != nil { 28 return Glyph{}, err 29 } 30 } 31 if int(glyph) >= len(f.charstrings) { 32 return Glyph{}, fmt.Errorf("invalid glyph index %d", glyph) 33 } 34 35 subrs := f.localSubrs[index] 36 priv := f.priv[index] 37 loader.nominalWidthX = priv.nominalWidthX 38 loader.width = priv.defaultWidthX 39 err = psi.Run(f.charstrings[glyph], subrs, f.globalSubrs, &loader) 40 return Glyph{ 41 Outlines: loader.cs.Segments, 42 Width: int(loader.width), 43 Bounds: loader.cs.Bounds, 44 }, nil 45 } 46 47 // type2CharstringHandler implements operators needed to fetch Type2 charstring metrics 48 type type2CharstringHandler struct { 49 cs ps.CharstringReader 50 51 // found in private DICT, needed since we can't differenciate 52 // no width set from 0 width 53 // `width` must be initialized to default width 54 nominalWidthX int32 55 width int32 56 } 57 58 func (type2CharstringHandler) Context() ps.PsContext { return ps.Type2Charstring } 59 60 func (met *type2CharstringHandler) Apply(op ps.PsOperator, state *ps.Machine) error { 61 var err error 62 if !op.IsEscaped { 63 switch op.Operator { 64 case 11: // return 65 return state.Return() // do not clear the arg stack 66 case 14: // endchar 67 if state.ArgStack.Top > 0 { // width is optional 68 met.width = met.nominalWidthX + state.ArgStack.Vals[0] 69 } 70 met.cs.ClosePath() 71 return ps.ErrInterrupt 72 case 10: // callsubr 73 return ps.LocalSubr(state) // do not clear the arg stack 74 case 29: // callgsubr 75 return ps.GlobalSubr(state) // do not clear the arg stack 76 case 21: // rmoveto 77 if state.ArgStack.Top > 2 { // width is optional 78 met.width = met.nominalWidthX + state.ArgStack.Vals[0] 79 } 80 err = met.cs.Rmoveto(state) 81 case 22: // hmoveto 82 if state.ArgStack.Top > 1 { // width is optional 83 met.width = met.nominalWidthX + state.ArgStack.Vals[0] 84 } 85 err = met.cs.Hmoveto(state) 86 case 4: // vmoveto 87 if state.ArgStack.Top > 1 { // width is optional 88 met.width = met.nominalWidthX + state.ArgStack.Vals[0] 89 } 90 err = met.cs.Vmoveto(state) 91 case 1, 18: // hstem, hstemhm 92 met.cs.Hstem(state) 93 case 3, 23: // vstem, vstemhm 94 met.cs.Vstem(state) 95 case 19, 20: // hintmask, cntrmask 96 // variable number of arguments, but always even 97 // for xxxmask, if there are arguments on the stack, then this is an impliied stem 98 if state.ArgStack.Top&1 != 0 { 99 met.width = met.nominalWidthX + state.ArgStack.Vals[0] 100 } 101 met.cs.Hintmask(state) 102 // the stack is managed by the previous call 103 return nil 104 105 case 5: // rlineto 106 met.cs.Rlineto(state) 107 case 6: // hlineto 108 met.cs.Hlineto(state) 109 case 7: // vlineto 110 met.cs.Vlineto(state) 111 case 8: // rrcurveto 112 met.cs.Rrcurveto(state) 113 case 24: // rcurveline 114 err = met.cs.Rcurveline(state) 115 case 25: // rlinecurve 116 err = met.cs.Rlinecurve(state) 117 case 26: // vvcurveto 118 met.cs.Vvcurveto(state) 119 case 27: // hhcurveto 120 met.cs.Hhcurveto(state) 121 case 30: // vhcurveto 122 met.cs.Vhcurveto(state) 123 case 31: // hvcurveto 124 met.cs.Hvcurveto(state) 125 default: 126 // no other operands are allowed before the ones handled above 127 err = fmt.Errorf("invalid operator %s in charstring", op) 128 } 129 } else { 130 switch op.Operator { 131 case 34: // hflex 132 err = met.cs.Hflex(state) 133 case 35: // flex 134 err = met.cs.Flex(state) 135 case 36: // hflex1 136 err = met.cs.Hflex1(state) 137 case 37: // flex1 138 err = met.cs.Flex1(state) 139 default: 140 // no other operands are allowed before the ones handled above 141 err = fmt.Errorf("invalid operator %s in charstring", op) 142 } 143 } 144 state.ArgStack.Clear() 145 return err 146 }