package pdf import ( "fmt" "io" ) // A Stack represents a stack of values. type Stack struct { stack []Value } func (stk *Stack) Len() int { return len(stk.stack) } func (stk *Stack) Push(v Value) { stk.stack = append(stk.stack, v) } func (stk *Stack) Pop() Value { n := len(stk.stack) if n == 0 { return Value{} } v := stk.stack[n-1] stk.stack[n-1] = Value{} stk.stack = stk.stack[:n-1] return v } func newDict() Value { return Value{nil, objptr{}, make(dict)} } // Interpret interprets the content in a stream as a basic PostScript program, // pushing values onto a stack and then calling the do function to execute // operators. The do function may push or pop values from the stack as needed // to implement op. // // Interpret handles the operators "dict", "currentdict", "begin", "end", "def", and "pop" itself. // // Interpret is not a full-blown PostScript interpreter. Its job is to handle the // very limited PostScript found in certain supporting file formats embedded // in PDF files, such as cmap files that describe the mapping from font code // points to Unicode code points. // // There is no support for executable blocks, among other limitations. func Interpret(strm Value, do func(stk *Stack, op string)) { rd := strm.Reader() b := newBuffer(rd, 0) b.allowEOF = true b.allowObjptr = false b.allowStream = false var stk Stack var dicts []dict Reading: for { tok := b.readToken() if tok == io.EOF { break } if kw, ok := tok.(keyword); ok { switch kw { case "null", "[", "]", "<<", ">>": break default: for i := len(dicts) - 1; i >= 0; i-- { if v, ok := dicts[i][name(kw)]; ok { stk.Push(Value{nil, objptr{}, v}) continue Reading } } do(&stk, string(kw)) continue case "dict": stk.Pop() stk.Push(Value{nil, objptr{}, make(dict)}) continue case "currentdict": if len(dicts) == 0 { panic("no current dictionary") } stk.Push(Value{nil, objptr{}, dicts[len(dicts)-1]}) continue case "begin": d := stk.Pop() if d.Kind() != Dict { panic("cannot begin non-dict") } dicts = append(dicts, d.data.(dict)) continue case "end": if len(dicts) <= 0 { panic("mismatched begin/end") } dicts = dicts[:len(dicts)-1] continue case "def": if len(dicts) <= 0 { panic("def without open dict") } val := stk.Pop() key, ok := stk.Pop().data.(name) if !ok { panic("def of non-name") } dicts[len(dicts)-1][key] = val.data continue case "pop": stk.Pop() continue } } b.unreadToken(tok) obj := b.readObject() stk.Push(Value{nil, objptr{}, obj}) } } type seqReader struct { rd io.Reader offset int64 } func (r *seqReader) ReadAt(buf []byte, offset int64) (int, error) { if offset != r.offset { return 0, fmt.Errorf("non-sequential read of stream") } n, err := io.ReadFull(r.rd, buf) r.offset += int64(n) return n, err }