fix wrong git ignore

This commit is contained in:
Adrian Zürcher
2025-12-15 17:44:00 +01:00
parent ed9f31bb96
commit 8f313c00f0
126 changed files with 70589 additions and 1 deletions

15
internal/pdf/ps/const.go Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

245
internal/pdf/ps/parser.go Normal file
View 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
View 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
}