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  }