fix wrong git ignore
This commit is contained in:
15
internal/pdf/ps/const.go
Normal file
15
internal/pdf/ps/const.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package ps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Tolerance for comparing real values.
|
||||
const TOLERANCE = 0.000001
|
||||
|
||||
// Common errors.
|
||||
var ErrStackUnderflow = errors.New("Stack underflow")
|
||||
var ErrStackOverflow = errors.New("Stack overflow")
|
||||
var ErrTypeCheck = errors.New("Type check error")
|
||||
var ErrRangeCheck = errors.New("Range check error")
|
||||
var ErrUndefinedResult = errors.New("Undefined result error")
|
||||
60
internal/pdf/ps/exec.go
Normal file
60
internal/pdf/ps/exec.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package ps
|
||||
|
||||
// A limited postscript parser for PDF function type 4.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
)
|
||||
|
||||
// A PSExecutor has its own execution stack and is used to executre a PS routine (program).
|
||||
type PSExecutor struct {
|
||||
Stack *PSStack
|
||||
program *PSProgram
|
||||
}
|
||||
|
||||
func NewPSExecutor(program *PSProgram) *PSExecutor {
|
||||
executor := &PSExecutor{}
|
||||
executor.Stack = NewPSStack()
|
||||
executor.program = program
|
||||
return executor
|
||||
}
|
||||
|
||||
func PSObjectArrayToFloat64Array(objects []PSObject) ([]float64, error) {
|
||||
vals := []float64{}
|
||||
|
||||
for _, obj := range objects {
|
||||
if number, is := obj.(*PSInteger); is {
|
||||
vals = append(vals, float64(number.Val))
|
||||
} else if number, is := obj.(*PSReal); is {
|
||||
vals = append(vals, number.Val)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Type error")
|
||||
}
|
||||
}
|
||||
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
func (this *PSExecutor) Execute(objects []PSObject) ([]PSObject, error) {
|
||||
// Add the arguments on stack
|
||||
// [obj1 obj2 ...]
|
||||
for _, obj := range objects {
|
||||
err := this.Stack.Push(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err := this.program.Exec(this.Stack)
|
||||
if err != nil {
|
||||
common.Log.Debug("Exec failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := []PSObject(*this.Stack)
|
||||
this.Stack.Empty()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
31
internal/pdf/ps/handy.go
Normal file
31
internal/pdf/ps/handy.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package ps
|
||||
|
||||
func MakeReal(val float64) PSObject {
|
||||
obj := PSReal{}
|
||||
obj.Val = val
|
||||
return &obj
|
||||
}
|
||||
|
||||
func MakeInteger(val int) PSObject {
|
||||
obj := PSInteger{}
|
||||
obj.Val = val
|
||||
return &obj
|
||||
}
|
||||
|
||||
func MakeBool(val bool) *PSBoolean {
|
||||
obj := PSBoolean{}
|
||||
obj.Val = val
|
||||
return &obj
|
||||
}
|
||||
|
||||
func MakeOperand(val string) *PSOperand {
|
||||
obj := PSOperand(val)
|
||||
return &obj
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
1367
internal/pdf/ps/object.go
Normal file
1367
internal/pdf/ps/object.go
Normal file
File diff suppressed because it is too large
Load Diff
245
internal/pdf/ps/parser.go
Normal file
245
internal/pdf/ps/parser.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package ps
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
)
|
||||
|
||||
type PSParser struct {
|
||||
reader *bufio.Reader
|
||||
}
|
||||
|
||||
// Create a new instance of the PDF Postscript parser from input data.
|
||||
func NewPSParser(content []byte) *PSParser {
|
||||
parser := PSParser{}
|
||||
|
||||
buffer := bytes.NewBuffer(content)
|
||||
parser.reader = bufio.NewReader(buffer)
|
||||
|
||||
return &parser
|
||||
}
|
||||
|
||||
// Parse the postscript and store as a program that can be executed.
|
||||
func (this *PSParser) Parse() (*PSProgram, error) {
|
||||
this.skipSpaces()
|
||||
bb, err := this.reader.Peek(2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != '{' {
|
||||
return nil, fmt.Errorf("invalid PS Program not starting with {")
|
||||
}
|
||||
|
||||
program, err := this.parseFunction()
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return program, err
|
||||
}
|
||||
|
||||
// Detect the signature at the current parse position and parse
|
||||
// the corresponding object.
|
||||
func (this *PSParser) parseFunction() (*PSProgram, error) {
|
||||
c, _ := this.reader.ReadByte()
|
||||
if c != '{' {
|
||||
return nil, errors.New("invalid function")
|
||||
}
|
||||
|
||||
function := NewPSProgram()
|
||||
|
||||
for {
|
||||
this.skipSpaces()
|
||||
bb, err := this.reader.Peek(2)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
common.Log.Trace("Peek string: %s", string(bb))
|
||||
// Determine type.
|
||||
if bb[0] == '}' {
|
||||
common.Log.Trace("EOF function")
|
||||
this.reader.ReadByte()
|
||||
break
|
||||
} else if bb[0] == '{' {
|
||||
common.Log.Trace("Function!")
|
||||
inlineF, err := this.parseFunction()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
function.Append(inlineF)
|
||||
} else if pdfcore.IsDecimalDigit(bb[0]) || (bb[0] == '-' && pdfcore.IsDecimalDigit(bb[1])) {
|
||||
common.Log.Trace("->Number!")
|
||||
number, err := this.parseNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
function.Append(number)
|
||||
} else {
|
||||
common.Log.Trace("->Operand or bool?")
|
||||
// Let's peek farther to find out.
|
||||
bb, _ = this.reader.Peek(5)
|
||||
peekStr := string(bb)
|
||||
common.Log.Trace("Peek str: %s", peekStr)
|
||||
|
||||
if (len(peekStr) > 4) && (peekStr[:5] == "false") {
|
||||
b, err := this.parseBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
function.Append(b)
|
||||
} else if (len(peekStr) > 3) && (peekStr[:4] == "true") {
|
||||
b, err := this.parseBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
function.Append(b)
|
||||
} else {
|
||||
operand, err := this.parseOperand()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
function.Append(operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function, nil
|
||||
}
|
||||
|
||||
// Skip over any spaces. Returns the number of spaces skipped and
|
||||
// an error if any.
|
||||
func (this *PSParser) skipSpaces() (int, error) {
|
||||
cnt := 0
|
||||
for {
|
||||
bb, err := this.reader.Peek(1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if pdfcore.IsWhiteSpace(bb[0]) {
|
||||
this.reader.ReadByte()
|
||||
cnt++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return cnt, nil
|
||||
}
|
||||
|
||||
// Numeric objects.
|
||||
// Integer or Real numbers.
|
||||
func (this *PSParser) parseNumber() (PSObject, error) {
|
||||
isFloat := false
|
||||
allowSigns := true
|
||||
numStr := ""
|
||||
for {
|
||||
common.Log.Trace("Parsing number \"%s\"", numStr)
|
||||
bb, err := this.reader.Peek(1)
|
||||
if err == io.EOF {
|
||||
// GH: EOF handling. Handle EOF like end of line. Can happen with
|
||||
// encoded object streams that the object is at the end.
|
||||
// In other cases, we will get the EOF error elsewhere at any rate.
|
||||
break // Handle like EOF
|
||||
}
|
||||
if err != nil {
|
||||
common.Log.Error("error %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if allowSigns && (bb[0] == '-' || bb[0] == '+') {
|
||||
// Only appear in the beginning, otherwise serves as a delimiter.
|
||||
b, _ := this.reader.ReadByte()
|
||||
numStr += string(b)
|
||||
allowSigns = false // Only allowed in beginning, and after e (exponential).
|
||||
} else if pdfcore.IsDecimalDigit(bb[0]) {
|
||||
b, _ := this.reader.ReadByte()
|
||||
numStr += string(b)
|
||||
} else if bb[0] == '.' {
|
||||
b, _ := this.reader.ReadByte()
|
||||
numStr += string(b)
|
||||
isFloat = true
|
||||
} else if bb[0] == 'e' {
|
||||
// Exponential number format.
|
||||
// XXX Is this supported in PS?
|
||||
b, _ := this.reader.ReadByte()
|
||||
numStr += string(b)
|
||||
isFloat = true
|
||||
allowSigns = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isFloat {
|
||||
fVal, err := strconv.ParseFloat(numStr, 64)
|
||||
o := MakeReal(fVal)
|
||||
return o, err
|
||||
} else {
|
||||
intVal, err := strconv.ParseInt(numStr, 10, 64)
|
||||
o := MakeInteger(int(intVal))
|
||||
return o, err
|
||||
}
|
||||
}
|
||||
|
||||
// Parse bool object.
|
||||
func (this *PSParser) parseBool() (*PSBoolean, error) {
|
||||
bb, err := this.reader.Peek(4)
|
||||
if err != nil {
|
||||
return MakeBool(false), err
|
||||
}
|
||||
if (len(bb) >= 4) && (string(bb[:4]) == "true") {
|
||||
this.reader.Discard(4)
|
||||
return MakeBool(true), nil
|
||||
}
|
||||
|
||||
bb, err = this.reader.Peek(5)
|
||||
if err != nil {
|
||||
return MakeBool(false), err
|
||||
}
|
||||
if (len(bb) >= 5) && (string(bb[:5]) == "false") {
|
||||
this.reader.Discard(5)
|
||||
return MakeBool(false), nil
|
||||
}
|
||||
|
||||
return MakeBool(false), errors.New("Unexpected boolean string")
|
||||
}
|
||||
|
||||
// An operand is a text command represented by a word.
|
||||
func (this *PSParser) parseOperand() (*PSOperand, error) {
|
||||
bytes := []byte{}
|
||||
for {
|
||||
bb, err := this.reader.Peek(1)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if pdfcore.IsDelimiter(bb[0]) {
|
||||
break
|
||||
}
|
||||
if pdfcore.IsWhiteSpace(bb[0]) {
|
||||
break
|
||||
}
|
||||
|
||||
b, _ := this.reader.ReadByte()
|
||||
bytes = append(bytes, b)
|
||||
}
|
||||
|
||||
if len(bytes) == 0 {
|
||||
return nil, fmt.Errorf("invalid operand (empty)")
|
||||
}
|
||||
|
||||
return MakeOperand(string(bytes)), nil
|
||||
}
|
||||
83
internal/pdf/ps/stack.go
Normal file
83
internal/pdf/ps/stack.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package ps
|
||||
|
||||
type PSStack []PSObject
|
||||
|
||||
func NewPSStack() *PSStack {
|
||||
return &PSStack{}
|
||||
}
|
||||
|
||||
func (stack *PSStack) Empty() {
|
||||
*stack = []PSObject{}
|
||||
}
|
||||
|
||||
func (stack *PSStack) Push(obj PSObject) error {
|
||||
if len(*stack) > 100 {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
*stack = append(*stack, obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *PSStack) Pop() (PSObject, error) {
|
||||
if len(*stack) < 1 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
|
||||
obj := (*stack)[len(*stack)-1]
|
||||
*stack = (*stack)[0 : len(*stack)-1]
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (stack *PSStack) PopInteger() (int, error) {
|
||||
obj, err := stack.Pop()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if number, is := obj.(*PSInteger); is {
|
||||
return number.Val, nil
|
||||
} else {
|
||||
return 0, ErrTypeCheck
|
||||
}
|
||||
}
|
||||
|
||||
// Pop and return the numeric value of the top of the stack as a float64.
|
||||
// Real or integer only.
|
||||
func (stack *PSStack) PopNumberAsFloat64() (float64, error) {
|
||||
obj, err := stack.Pop()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if number, is := obj.(*PSReal); is {
|
||||
return number.Val, nil
|
||||
} else if number, is := obj.(*PSInteger); is {
|
||||
return float64(number.Val), nil
|
||||
} else {
|
||||
return 0, ErrTypeCheck
|
||||
}
|
||||
}
|
||||
|
||||
func (this *PSStack) String() string {
|
||||
s := "[ "
|
||||
for _, obj := range *this {
|
||||
s += obj.String()
|
||||
s += " "
|
||||
}
|
||||
s += "]"
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (this *PSStack) DebugString() string {
|
||||
s := "[ "
|
||||
for _, obj := range *this {
|
||||
s += obj.DebugString()
|
||||
s += " "
|
||||
}
|
||||
s += "]"
|
||||
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user