mirror of https://github.com/perkeep/perkeep.git
139 lines
3.1 KiB
Go
139 lines
3.1 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
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
|
|
}
|