github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/pdf/ps.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pdf 6 7 import ( 8 "fmt" 9 "io" 10 ) 11 12 // A Stack represents a stack of values. 13 type Stack struct { 14 stack []Value 15 } 16 17 func (stk *Stack) Len() int { 18 return len(stk.stack) 19 } 20 21 func (stk *Stack) Push(v Value) { 22 stk.stack = append(stk.stack, v) 23 } 24 25 func (stk *Stack) Pop() Value { 26 n := len(stk.stack) 27 if n == 0 { 28 return Value{} 29 } 30 v := stk.stack[n-1] 31 stk.stack[n-1] = Value{} 32 stk.stack = stk.stack[:n-1] 33 return v 34 } 35 36 func newDict() Value { 37 return Value{nil, objptr{}, make(dict)} 38 } 39 40 // Interpret interprets the content in a stream as a basic PostScript program, 41 // pushing values onto a stack and then calling the do function to execute 42 // operators. The do function may push or pop values from the stack as needed 43 // to implement op. 44 // 45 // Interpret handles the operators "dict", "currentdict", "begin", "end", "def", and "pop" itself. 46 // 47 // Interpret is not a full-blown PostScript interpreter. Its job is to handle the 48 // very limited PostScript found in certain supporting file formats embedded 49 // in PDF files, such as cmap files that describe the mapping from font code 50 // points to Unicode code points. 51 // 52 // There is no support for executable blocks, among other limitations. 53 // 54 func Interpret(strm Value, do func(stk *Stack, op string)) { 55 rd := strm.Reader() 56 b := newBuffer(rd, 0) 57 b.allowEOF = true 58 b.allowObjptr = false 59 b.allowStream = false 60 var stk Stack 61 var dicts []dict 62 Reading: 63 for { 64 tok := b.readToken() 65 if tok == io.EOF { 66 break 67 } 68 if kw, ok := tok.(keyword); ok { 69 switch kw { 70 case "null", "[", "]", "<<", ">>": 71 break 72 default: 73 for i := len(dicts) - 1; i >= 0; i-- { 74 if v, ok := dicts[i][name(kw)]; ok { 75 stk.Push(Value{nil, objptr{}, v}) 76 continue Reading 77 } 78 } 79 do(&stk, string(kw)) 80 continue 81 case "dict": 82 stk.Pop() 83 stk.Push(Value{nil, objptr{}, make(dict)}) 84 continue 85 case "currentdict": 86 if len(dicts) == 0 { 87 panic("no current dictionary") 88 } 89 stk.Push(Value{nil, objptr{}, dicts[len(dicts)-1]}) 90 continue 91 case "begin": 92 d := stk.Pop() 93 if d.Kind() != Dict { 94 panic("cannot begin non-dict") 95 } 96 dicts = append(dicts, d.data.(dict)) 97 continue 98 case "end": 99 if len(dicts) <= 0 { 100 panic("mismatched begin/end") 101 } 102 dicts = dicts[:len(dicts)-1] 103 continue 104 case "def": 105 if len(dicts) <= 0 { 106 panic("def without open dict") 107 } 108 val := stk.Pop() 109 key, ok := stk.Pop().data.(name) 110 if !ok { 111 panic("def of non-name") 112 } 113 dicts[len(dicts)-1][key] = val.data 114 continue 115 case "pop": 116 stk.Pop() 117 continue 118 } 119 } 120 b.unreadToken(tok) 121 obj := b.readObject() 122 stk.Push(Value{nil, objptr{}, obj}) 123 } 124 } 125 126 type seqReader struct { 127 rd io.Reader 128 offset int64 129 } 130 131 func (r *seqReader) ReadAt(buf []byte, offset int64) (int, error) { 132 if offset != r.offset { 133 return 0, fmt.Errorf("non-sequential read of stream") 134 } 135 n, err := io.ReadFull(r.rd, buf) 136 r.offset += int64(n) 137 return n, err 138 }