Compare commits
No commits in common. "62206ed344097fa2232dbb75d96ed2ea60841ec4" and "43ca0e5035b6951297715a211b73d8db5f751f15" have entirely different histories.
62206ed344
...
43ca0e5035
300
pdf/pdf.go
300
pdf/pdf.go
@ -65,14 +65,21 @@ const (
|
|||||||
// Object is a PDF token/object thingy. Objects may be composed either from
|
// Object is a PDF token/object thingy. Objects may be composed either from
|
||||||
// one or a sequence of tokens. The PDF Reference doesn't actually speak
|
// one or a sequence of tokens. The PDF Reference doesn't actually speak
|
||||||
// of tokens.
|
// of tokens.
|
||||||
|
//
|
||||||
|
// TODO(p): We probably want constructors like NewString, NewBool, NewArray, ...
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Kind ObjectKind
|
Kind ObjectKind
|
||||||
|
|
||||||
String string // Comment/Keyword/Name/String
|
// End (error message), Comment/Keyword/Name/String
|
||||||
Number float64 // Bool, Numeric
|
String string
|
||||||
Array []Object // Array, Indirect
|
// Bool, Numeric
|
||||||
Dict map[string]Object // Dict, in the future also Stream
|
Number float64
|
||||||
N, Generation uint // Indirect, Reference
|
// Array, Indirect
|
||||||
|
Array []Object
|
||||||
|
// Dict, in the future also Stream
|
||||||
|
Dict map[string]Object
|
||||||
|
// Indirect, Reference
|
||||||
|
N, Generation uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInteger checks if the PDF object is an integer number.
|
// IsInteger checks if the PDF object is an integer number.
|
||||||
@ -86,48 +93,6 @@ func (o *Object) IsUint() bool {
|
|||||||
return o.IsInteger() && o.Number >= 0 && o.Number <= float64(^uint(0))
|
return o.IsInteger() && o.Number >= 0 && o.Number <= float64(^uint(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// A slew of constructors that will hopefully get all inlined.
|
|
||||||
|
|
||||||
// New returns a new Object of the given kind, with default values.
|
|
||||||
func New(kind ObjectKind) Object { return Object{Kind: kind} }
|
|
||||||
|
|
||||||
func NewComment(c string) Object { return Object{Kind: Comment, String: c} }
|
|
||||||
func NewKeyword(k string) Object { return Object{Kind: Keyword, String: k} }
|
|
||||||
|
|
||||||
func NewBool(b bool) Object {
|
|
||||||
var b64 float64
|
|
||||||
if b {
|
|
||||||
b64 = 1
|
|
||||||
}
|
|
||||||
return Object{Kind: Bool, Number: b64}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNumeric(n float64) Object { return Object{Kind: Numeric, Number: n} }
|
|
||||||
func NewName(n string) Object { return Object{Kind: Name, String: n} }
|
|
||||||
func NewString(s string) Object { return Object{Kind: String, String: s} }
|
|
||||||
|
|
||||||
func NewArray(a []Object) Object {
|
|
||||||
return Object{Kind: Array, Array: a}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDict(d map[string]Object) Object {
|
|
||||||
if d == nil {
|
|
||||||
d = make(map[string]Object)
|
|
||||||
}
|
|
||||||
return Object{Kind: Dict, Dict: d}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIndirect(o Object, n, generation uint) Object {
|
|
||||||
return Object{Kind: Indirect, N: n, Generation: generation,
|
|
||||||
Array: []Object{o}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReference(n, generation uint) Object {
|
|
||||||
return Object{Kind: Reference, N: n, Generation: generation}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newError(msg string) (Object, error) { return New(End), errors.New(msg) }
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -200,13 +165,13 @@ func (lex *Lexer) unescape(ch byte) byte {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) string() (Object, error) {
|
func (lex *Lexer) string() Object {
|
||||||
var value []byte
|
var value []byte
|
||||||
parens := 1
|
parens := 1
|
||||||
for {
|
for {
|
||||||
ch, ok := lex.read()
|
ch, ok := lex.read()
|
||||||
if !ok {
|
if !ok {
|
||||||
return newError("unexpected end of string")
|
return Object{Kind: End, String: "unexpected end of string"}
|
||||||
}
|
}
|
||||||
if lex.eatNewline(ch) {
|
if lex.eatNewline(ch) {
|
||||||
ch = '\n'
|
ch = '\n'
|
||||||
@ -218,7 +183,7 @@ func (lex *Lexer) string() (Object, error) {
|
|||||||
}
|
}
|
||||||
} else if ch == '\\' {
|
} else if ch == '\\' {
|
||||||
if ch, ok = lex.read(); !ok {
|
if ch, ok = lex.read(); !ok {
|
||||||
return newError("unexpected end of string")
|
return Object{Kind: End, String: "unexpected end of string"}
|
||||||
} else if lex.eatNewline(ch) {
|
} else if lex.eatNewline(ch) {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@ -227,19 +192,19 @@ func (lex *Lexer) string() (Object, error) {
|
|||||||
}
|
}
|
||||||
value = append(value, ch)
|
value = append(value, ch)
|
||||||
}
|
}
|
||||||
return NewString(string(value)), nil
|
return Object{Kind: String, String: string(value)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) stringHex() (Object, error) {
|
func (lex *Lexer) stringHex() Object {
|
||||||
var value, buf []byte
|
var value, buf []byte
|
||||||
for {
|
for {
|
||||||
ch, ok := lex.read()
|
ch, ok := lex.read()
|
||||||
if !ok {
|
if !ok {
|
||||||
return newError("unexpected end of hex string")
|
return Object{Kind: End, String: "unexpected end of hex string"}
|
||||||
} else if ch == '>' {
|
} else if ch == '>' {
|
||||||
break
|
break
|
||||||
} else if strings.IndexByte(hexAlphabet, ch) < 0 {
|
} else if strings.IndexByte(hexAlphabet, ch) < 0 {
|
||||||
return newError("invalid hex string")
|
return Object{Kind: End, String: "invalid hex string"}
|
||||||
} else if buf = append(buf, ch); len(buf) == 2 {
|
} else if buf = append(buf, ch); len(buf) == 2 {
|
||||||
u, _ := strconv.ParseUint(string(buf), 16, 8)
|
u, _ := strconv.ParseUint(string(buf), 16, 8)
|
||||||
value = append(value, byte(u))
|
value = append(value, byte(u))
|
||||||
@ -250,10 +215,10 @@ func (lex *Lexer) stringHex() (Object, error) {
|
|||||||
u, _ := strconv.ParseUint(string(buf)+"0", 16, 8)
|
u, _ := strconv.ParseUint(string(buf)+"0", 16, 8)
|
||||||
value = append(value, byte(u))
|
value = append(value, byte(u))
|
||||||
}
|
}
|
||||||
return NewString(string(value)), nil
|
return Object{Kind: String, String: string(value)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) name() (Object, error) {
|
func (lex *Lexer) name() Object {
|
||||||
var value []byte
|
var value []byte
|
||||||
for {
|
for {
|
||||||
ch, ok := lex.peek()
|
ch, ok := lex.peek()
|
||||||
@ -272,7 +237,7 @@ func (lex *Lexer) name() (Object, error) {
|
|||||||
lex.read()
|
lex.read()
|
||||||
}
|
}
|
||||||
if len(hexa) != 2 {
|
if len(hexa) != 2 {
|
||||||
return newError("invalid name hexa escape")
|
return Object{Kind: End, String: "invalid name hexa escape"}
|
||||||
}
|
}
|
||||||
u, _ := strconv.ParseUint(string(value), 16, 8)
|
u, _ := strconv.ParseUint(string(value), 16, 8)
|
||||||
ch = byte(u)
|
ch = byte(u)
|
||||||
@ -280,12 +245,12 @@ func (lex *Lexer) name() (Object, error) {
|
|||||||
value = append(value, ch)
|
value = append(value, ch)
|
||||||
}
|
}
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
return newError("unexpected end of name")
|
return Object{Kind: End, String: "unexpected end of name"}
|
||||||
}
|
}
|
||||||
return NewName(string(value)), nil
|
return Object{Kind: Name, String: string(value)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) comment() (Object, error) {
|
func (lex *Lexer) comment() Object {
|
||||||
var value []byte
|
var value []byte
|
||||||
for {
|
for {
|
||||||
ch, ok := lex.peek()
|
ch, ok := lex.peek()
|
||||||
@ -295,11 +260,11 @@ func (lex *Lexer) comment() (Object, error) {
|
|||||||
value = append(value, ch)
|
value = append(value, ch)
|
||||||
lex.read()
|
lex.read()
|
||||||
}
|
}
|
||||||
return NewComment(string(value)), nil
|
return Object{Kind: Comment, String: string(value)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Maybe invalid numbers should rather be interpreted as keywords.
|
// XXX: Maybe invalid numbers should rather be interpreted as keywords.
|
||||||
func (lex *Lexer) number() (Object, error) {
|
func (lex *Lexer) number() Object {
|
||||||
var value []byte
|
var value []byte
|
||||||
ch, ok := lex.peek()
|
ch, ok := lex.peek()
|
||||||
if ch == '-' {
|
if ch == '-' {
|
||||||
@ -322,16 +287,16 @@ func (lex *Lexer) number() (Object, error) {
|
|||||||
lex.read()
|
lex.read()
|
||||||
}
|
}
|
||||||
if !digits {
|
if !digits {
|
||||||
return newError("invalid number")
|
return Object{Kind: End, String: "invalid number"}
|
||||||
}
|
}
|
||||||
f, _ := strconv.ParseFloat(string(value), 64)
|
f, _ := strconv.ParseFloat(string(value), 64)
|
||||||
return NewNumeric(f), nil
|
return Object{Kind: Numeric, Number: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) Next() (Object, error) {
|
func (lex *Lexer) Next() Object {
|
||||||
ch, ok := lex.peek()
|
ch, ok := lex.peek()
|
||||||
if !ok {
|
if !ok {
|
||||||
return New(End), nil
|
return Object{Kind: End}
|
||||||
}
|
}
|
||||||
if strings.IndexByte("-0123456789.", ch) >= 0 {
|
if strings.IndexByte("-0123456789.", ch) >= 0 {
|
||||||
return lex.number()
|
return lex.number()
|
||||||
@ -350,13 +315,13 @@ func (lex *Lexer) Next() (Object, error) {
|
|||||||
switch v := string(value); v {
|
switch v := string(value); v {
|
||||||
case "":
|
case "":
|
||||||
case "null":
|
case "null":
|
||||||
return New(Nil), nil
|
return Object{Kind: Nil}
|
||||||
case "true":
|
case "true":
|
||||||
return NewBool(true), nil
|
return Object{Kind: Bool, Number: 1}
|
||||||
case "false":
|
case "false":
|
||||||
return NewBool(false), nil
|
return Object{Kind: Bool, Number: 0}
|
||||||
default:
|
default:
|
||||||
return NewKeyword(v), nil
|
return Object{Kind: Keyword, String: v}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ch, _ := lex.read(); ch {
|
switch ch, _ := lex.read(); ch {
|
||||||
@ -367,29 +332,29 @@ func (lex *Lexer) Next() (Object, error) {
|
|||||||
case '(':
|
case '(':
|
||||||
return lex.string()
|
return lex.string()
|
||||||
case '[':
|
case '[':
|
||||||
return New(BArray), nil
|
return Object{Kind: BArray}
|
||||||
case ']':
|
case ']':
|
||||||
return New(EArray), nil
|
return Object{Kind: EArray}
|
||||||
case '<':
|
case '<':
|
||||||
if ch, _ := lex.peek(); ch == '<' {
|
if ch, _ := lex.peek(); ch == '<' {
|
||||||
lex.read()
|
lex.read()
|
||||||
return New(BDict), nil
|
return Object{Kind: BDict}
|
||||||
}
|
}
|
||||||
return lex.stringHex()
|
return lex.stringHex()
|
||||||
case '>':
|
case '>':
|
||||||
if ch, _ := lex.peek(); ch == '>' {
|
if ch, _ := lex.peek(); ch == '>' {
|
||||||
lex.read()
|
lex.read()
|
||||||
return New(EDict), nil
|
return Object{Kind: EDict}
|
||||||
}
|
}
|
||||||
return newError("unexpected '>'")
|
return Object{Kind: End, String: "unexpected '>'"}
|
||||||
default:
|
default:
|
||||||
if lex.eatNewline(ch) {
|
if lex.eatNewline(ch) {
|
||||||
return New(NL), nil
|
return Object{Kind: NL}
|
||||||
}
|
}
|
||||||
if strings.IndexByte(whitespace, ch) >= 0 {
|
if strings.IndexByte(whitespace, ch) >= 0 {
|
||||||
return lex.Next()
|
return lex.Next()
|
||||||
}
|
}
|
||||||
return newError("unexpected input")
|
return Object{Kind: End, String: "unexpected input"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,10 +461,10 @@ type Updater struct {
|
|||||||
Trailer map[string]Object
|
Trailer map[string]Object
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) parseIndirect(lex *Lexer, stack *[]Object) (Object, error) {
|
func (u *Updater) parseIndirect(lex *Lexer, stack *[]Object) Object {
|
||||||
lenStack := len(*stack)
|
lenStack := len(*stack)
|
||||||
if lenStack < 2 {
|
if lenStack < 2 {
|
||||||
return newError("missing object ID pair")
|
return Object{Kind: End, String: "missing object ID pair"}
|
||||||
}
|
}
|
||||||
|
|
||||||
n := (*stack)[lenStack-2]
|
n := (*stack)[lenStack-2]
|
||||||
@ -507,30 +472,28 @@ func (u *Updater) parseIndirect(lex *Lexer, stack *[]Object) (Object, error) {
|
|||||||
*stack = (*stack)[:lenStack-2]
|
*stack = (*stack)[:lenStack-2]
|
||||||
|
|
||||||
if !g.IsUint() || !n.IsUint() {
|
if !g.IsUint() || !n.IsUint() {
|
||||||
return newError("invalid object ID pair")
|
return Object{Kind: End, String: "invalid object ID pair"}
|
||||||
}
|
}
|
||||||
|
|
||||||
var inner []Object
|
obj := Object{
|
||||||
|
Kind: Indirect, N: uint(n.Number), Generation: uint(g.Number)}
|
||||||
for {
|
for {
|
||||||
object, _ := u.parse(lex, &inner)
|
object := u.parse(lex, &obj.Array)
|
||||||
if object.Kind == End {
|
if object.Kind == End {
|
||||||
return newError("object doesn't end")
|
return Object{Kind: End, String: "object doesn't end"}
|
||||||
}
|
}
|
||||||
if object.Kind == Keyword && object.String == "endobj" {
|
if object.Kind == Keyword && object.String == "endobj" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
inner = append(inner, object)
|
obj.Array = append(obj.Array, object)
|
||||||
}
|
}
|
||||||
if len(inner) != 1 {
|
return obj
|
||||||
return newError("indirect objects must contain exactly one object")
|
|
||||||
}
|
|
||||||
return NewIndirect(inner[0], uint(n.Number), uint(g.Number)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) parseR(stack *[]Object) (Object, error) {
|
func (u *Updater) parseR(stack *[]Object) Object {
|
||||||
lenStack := len(*stack)
|
lenStack := len(*stack)
|
||||||
if lenStack < 2 {
|
if lenStack < 2 {
|
||||||
return newError("missing reference ID pair")
|
return Object{Kind: End, String: "missing reference ID pair"}
|
||||||
}
|
}
|
||||||
|
|
||||||
n := (*stack)[lenStack-2]
|
n := (*stack)[lenStack-2]
|
||||||
@ -538,16 +501,15 @@ func (u *Updater) parseR(stack *[]Object) (Object, error) {
|
|||||||
*stack = (*stack)[:lenStack-2]
|
*stack = (*stack)[:lenStack-2]
|
||||||
|
|
||||||
if !g.IsUint() || !n.IsUint() {
|
if !g.IsUint() || !n.IsUint() {
|
||||||
return newError("invalid reference ID pair")
|
return Object{Kind: End, String: "invalid reference ID pair"}
|
||||||
}
|
}
|
||||||
return NewReference(uint(n.Number), uint(g.Number)), nil
|
return Object{
|
||||||
|
Kind: Reference, N: uint(n.Number), Generation: uint(g.Number)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse reads an object at the lexer's position. Not a strict parser.
|
/// parse reads an object at the lexer's position. Not a strict parser.
|
||||||
//
|
func (u *Updater) parse(lex *Lexer, stack *[]Object) Object {
|
||||||
// TODO(p): We should fix all uses of this not to eat the error.
|
switch token := lex.Next(); token.Kind {
|
||||||
func (u *Updater) parse(lex *Lexer, stack *[]Object) (Object, error) {
|
|
||||||
switch token, err := lex.Next(); token.Kind {
|
|
||||||
case NL, Comment:
|
case NL, Comment:
|
||||||
// These are not important to parsing,
|
// These are not important to parsing,
|
||||||
// not even for this procedure's needs.
|
// not even for this procedure's needs.
|
||||||
@ -555,22 +517,22 @@ func (u *Updater) parse(lex *Lexer, stack *[]Object) (Object, error) {
|
|||||||
case BArray:
|
case BArray:
|
||||||
var array []Object
|
var array []Object
|
||||||
for {
|
for {
|
||||||
object, _ := u.parse(lex, &array)
|
object := u.parse(lex, &array)
|
||||||
if object.Kind == End {
|
if object.Kind == End {
|
||||||
return newError("array doesn't end")
|
return Object{Kind: End, String: "array doesn't end"}
|
||||||
}
|
}
|
||||||
if object.Kind == EArray {
|
if object.Kind == EArray {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
array = append(array, object)
|
array = append(array, object)
|
||||||
}
|
}
|
||||||
return NewArray(array), nil
|
return Object{Kind: Array, Array: array}
|
||||||
case BDict:
|
case BDict:
|
||||||
var array []Object
|
var array []Object
|
||||||
for {
|
for {
|
||||||
object, _ := u.parse(lex, &array)
|
object := u.parse(lex, &array)
|
||||||
if object.Kind == End {
|
if object.Kind == End {
|
||||||
return newError("dictionary doesn't end")
|
return Object{Kind: End, String: "dictionary doesn't end"}
|
||||||
}
|
}
|
||||||
if object.Kind == EDict {
|
if object.Kind == EDict {
|
||||||
break
|
break
|
||||||
@ -578,16 +540,17 @@ func (u *Updater) parse(lex *Lexer, stack *[]Object) (Object, error) {
|
|||||||
array = append(array, object)
|
array = append(array, object)
|
||||||
}
|
}
|
||||||
if len(array)%2 != 0 {
|
if len(array)%2 != 0 {
|
||||||
return newError("unbalanced dictionary")
|
return Object{Kind: End, String: "unbalanced dictionary"}
|
||||||
}
|
}
|
||||||
dict := make(map[string]Object)
|
dict := make(map[string]Object)
|
||||||
for i := 0; i < len(array); i += 2 {
|
for i := 0; i < len(array); i += 2 {
|
||||||
if array[i].Kind != Name {
|
if array[i].Kind != Name {
|
||||||
return newError("invalid dictionary key type")
|
return Object{
|
||||||
|
Kind: End, String: "invalid dictionary key type"}
|
||||||
}
|
}
|
||||||
dict[array[i].String] = array[i+1]
|
dict[array[i].String] = array[i+1]
|
||||||
}
|
}
|
||||||
return NewDict(dict), nil
|
return Object{Kind: Dict, Dict: dict}
|
||||||
case Keyword:
|
case Keyword:
|
||||||
// Appears in the document body, typically needs
|
// Appears in the document body, typically needs
|
||||||
// to access the cross-reference table.
|
// to access the cross-reference table.
|
||||||
@ -597,7 +560,7 @@ func (u *Updater) parse(lex *Lexer, stack *[]Object) (Object, error) {
|
|||||||
// streams can use the Object.String member.
|
// streams can use the Object.String member.
|
||||||
switch token.String {
|
switch token.String {
|
||||||
case "stream":
|
case "stream":
|
||||||
return newError("streams are not supported yet")
|
return Object{Kind: End, String: "streams are not supported yet"}
|
||||||
case "obj":
|
case "obj":
|
||||||
return u.parseIndirect(lex, stack)
|
return u.parseIndirect(lex, stack)
|
||||||
case "R":
|
case "R":
|
||||||
@ -605,18 +568,18 @@ func (u *Updater) parse(lex *Lexer, stack *[]Object) (Object, error) {
|
|||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
return token, err
|
return token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) loadXref(lex *Lexer, loadedEntries map[uint]struct{}) error {
|
func (u *Updater) loadXref(lex *Lexer, loadedEntries map[uint]struct{}) error {
|
||||||
var throwawayStack []Object
|
var throwawayStack []Object
|
||||||
if keyword, _ := u.parse(lex,
|
if keyword := u.parse(lex,
|
||||||
&throwawayStack); keyword.Kind != Keyword || keyword.String != "xref" {
|
&throwawayStack); keyword.Kind != Keyword || keyword.String != "xref" {
|
||||||
return errors.New("invalid xref table")
|
return errors.New("invalid xref table")
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
object, _ := u.parse(lex, &throwawayStack)
|
object := u.parse(lex, &throwawayStack)
|
||||||
if object.Kind == End {
|
if object.Kind == End {
|
||||||
return errors.New("unexpected EOF while looking for the trailer")
|
return errors.New("unexpected EOF while looking for the trailer")
|
||||||
}
|
}
|
||||||
@ -624,16 +587,16 @@ func (u *Updater) loadXref(lex *Lexer, loadedEntries map[uint]struct{}) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
second, _ := u.parse(lex, &throwawayStack)
|
second := u.parse(lex, &throwawayStack)
|
||||||
if !object.IsUint() || !second.IsUint() {
|
if !object.IsUint() || !second.IsUint() {
|
||||||
return errors.New("invalid xref section header")
|
return errors.New("invalid xref section header")
|
||||||
}
|
}
|
||||||
|
|
||||||
start, count := uint(object.Number), uint(second.Number)
|
start, count := uint(object.Number), uint(second.Number)
|
||||||
for i := uint(0); i < count; i++ {
|
for i := uint(0); i < count; i++ {
|
||||||
off, _ := u.parse(lex, &throwawayStack)
|
off := u.parse(lex, &throwawayStack)
|
||||||
gen, _ := u.parse(lex, &throwawayStack)
|
gen := u.parse(lex, &throwawayStack)
|
||||||
key, _ := u.parse(lex, &throwawayStack)
|
key := u.parse(lex, &throwawayStack)
|
||||||
if !off.IsInteger() || off.Number < 0 ||
|
if !off.IsInteger() || off.Number < 0 ||
|
||||||
off.Number > float64(len(u.Document)) ||
|
off.Number > float64(len(u.Document)) ||
|
||||||
!gen.IsInteger() || gen.Number < 0 || gen.Number > 65535 ||
|
!gen.IsInteger() || gen.Number < 0 || gen.Number > 65535 ||
|
||||||
@ -707,7 +670,7 @@ func (u *Updater) Initialize() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
trailer, _ := u.parse(&lex, &throwawayStack)
|
trailer := u.parse(&lex, &throwawayStack)
|
||||||
if trailer.Kind != Dict {
|
if trailer.Kind != Dict {
|
||||||
return errors.New("invalid trailer dictionary")
|
return errors.New("invalid trailer dictionary")
|
||||||
}
|
}
|
||||||
@ -727,7 +690,8 @@ func (u *Updater) Initialize() error {
|
|||||||
xrefOffset = int64(prevOffset.Number)
|
xrefOffset = int64(prevOffset.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Trailer["Prev"] = NewNumeric(float64(lastXrefOffset))
|
u.Trailer["Prev"] = Object{
|
||||||
|
Kind: Numeric, Number: float64(lastXrefOffset)}
|
||||||
|
|
||||||
lastSize, ok := u.Trailer["Size"]
|
lastSize, ok := u.Trailer["Size"]
|
||||||
if !ok || !lastSize.IsInteger() || lastSize.Number <= 0 {
|
if !ok || !lastSize.IsInteger() || lastSize.Number <= 0 {
|
||||||
@ -739,32 +703,30 @@ func (u *Updater) Initialize() error {
|
|||||||
|
|
||||||
// Get retrieves an object by its number and generation--may return
|
// Get retrieves an object by its number and generation--may return
|
||||||
// Nil or End with an error.
|
// Nil or End with an error.
|
||||||
//
|
func (u *Updater) Get(n, generation uint) Object {
|
||||||
// TODO(p): We should fix all uses of this not to eat the error.
|
|
||||||
func (u *Updater) Get(n, generation uint) (Object, error) {
|
|
||||||
if n >= u.xrefSize {
|
if n >= u.xrefSize {
|
||||||
return New(Nil), nil
|
return Object{Kind: Nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := u.xref[n]
|
ref := u.xref[n]
|
||||||
if !ref.nonfree || ref.generation != generation ||
|
if !ref.nonfree || ref.generation != generation ||
|
||||||
ref.offset >= int64(len(u.Document)) {
|
ref.offset >= int64(len(u.Document)) {
|
||||||
return New(Nil), nil
|
return Object{Kind: Nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
lex := Lexer{u.Document[ref.offset:]}
|
lex := Lexer{u.Document[ref.offset:]}
|
||||||
var stack []Object
|
var stack []Object
|
||||||
for {
|
for {
|
||||||
object, err := u.parse(&lex, &stack)
|
object := u.parse(&lex, &stack)
|
||||||
if object.Kind == End {
|
if object.Kind == End {
|
||||||
return object, err
|
return object
|
||||||
}
|
}
|
||||||
if object.Kind != Indirect {
|
if object.Kind != Indirect {
|
||||||
stack = append(stack, object)
|
stack = append(stack, object)
|
||||||
} else if object.N != n || object.Generation != generation {
|
} else if object.N != n || object.Generation != generation {
|
||||||
return newError("object mismatch")
|
return Object{Kind: End, String: "object mismatch"}
|
||||||
} else {
|
} else {
|
||||||
return object.Array[0], nil
|
return object.Array[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -857,8 +819,8 @@ func (u *Updater) FlushUpdates() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Trailer["Size"] = NewNumeric(float64(u.xrefSize))
|
u.Trailer["Size"] = Object{Kind: Numeric, Number: float64(u.xrefSize)}
|
||||||
trailer := NewDict(u.Trailer)
|
trailer := Object{Kind: Dict, Dict: u.Trailer}
|
||||||
|
|
||||||
fmt.Fprintf(buf, "trailer\n%s\nstartxref\n%d\n%%%%EOF\n",
|
fmt.Fprintf(buf, "trailer\n%s\nstartxref\n%d\n%%%%EOF\n",
|
||||||
trailer.Serialize(), startXref)
|
trailer.Serialize(), startXref)
|
||||||
@ -867,8 +829,8 @@ func (u *Updater) FlushUpdates() {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// NewDate makes a PDF object representing the given point in time.
|
// PdfDate makes a PDF object representing the given point in time.
|
||||||
func NewDate(ts time.Time) Object {
|
func PdfDate(ts time.Time) Object {
|
||||||
buf := ts.AppendFormat(nil, "D:20060102150405")
|
buf := ts.AppendFormat(nil, "D:20060102150405")
|
||||||
// "Z07'00'" doesn't work, we need to do some of it manually.
|
// "Z07'00'" doesn't work, we need to do some of it manually.
|
||||||
if _, offset := ts.Zone(); offset != 0 {
|
if _, offset := ts.Zone(); offset != 0 {
|
||||||
@ -877,14 +839,14 @@ func NewDate(ts time.Time) Object {
|
|||||||
} else {
|
} else {
|
||||||
buf = append(buf, 'Z')
|
buf = append(buf, 'Z')
|
||||||
}
|
}
|
||||||
return NewString(string(buf))
|
return Object{Kind: String, String: string(buf)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFirstPage retrieves the first page of the document or a Nil object.
|
// PdfGetFirstPage retrieves the first page of the document or a Nil object.
|
||||||
func (u *Updater) GetFirstPage(nodeN, nodeGeneration uint) Object {
|
func PdfGetFirstPage(pdf *Updater, nodeN, nodeGeneration uint) Object {
|
||||||
obj, _ := u.Get(nodeN, nodeGeneration)
|
obj := pdf.Get(nodeN, nodeGeneration)
|
||||||
if obj.Kind != Dict {
|
if obj.Kind != Dict {
|
||||||
return New(Nil)
|
return Object{Kind: Nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Out of convenience; these aren't filled normally.
|
// Out of convenience; these aren't filled normally.
|
||||||
@ -892,11 +854,11 @@ func (u *Updater) GetFirstPage(nodeN, nodeGeneration uint) Object {
|
|||||||
obj.Generation = nodeGeneration
|
obj.Generation = nodeGeneration
|
||||||
|
|
||||||
if typ, ok := obj.Dict["Type"]; !ok || typ.Kind != Name {
|
if typ, ok := obj.Dict["Type"]; !ok || typ.Kind != Name {
|
||||||
return New(Nil)
|
return Object{Kind: Nil}
|
||||||
} else if typ.String == "Page" {
|
} else if typ.String == "Page" {
|
||||||
return obj
|
return obj
|
||||||
} else if typ.String != "Pages" {
|
} else if typ.String != "Pages" {
|
||||||
return New(Nil)
|
return Object{Kind: Nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Technically speaking, this may be an indirect reference.
|
// XXX: Technically speaking, this may be an indirect reference.
|
||||||
@ -905,11 +867,11 @@ func (u *Updater) GetFirstPage(nodeN, nodeGeneration uint) Object {
|
|||||||
kids, ok := obj.Dict["Kids"]
|
kids, ok := obj.Dict["Kids"]
|
||||||
if !ok || kids.Kind != Array || len(kids.Array) == 0 ||
|
if !ok || kids.Kind != Array || len(kids.Array) == 0 ||
|
||||||
kids.Array[0].Kind != Reference {
|
kids.Array[0].Kind != Reference {
|
||||||
return New(Nil)
|
return Object{Kind: Nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Nothing prevents us from recursing in an evil circular graph.
|
// XXX: Nothing prevents us from recursing in an evil circular graph.
|
||||||
return u.GetFirstPage(kids.Array[0].N, kids.Array[0].Generation)
|
return PdfGetFirstPage(pdf, kids.Array[0].N, kids.Array[0].Generation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -1006,9 +968,8 @@ func PKCS12Parse(p12 []byte, password string) (
|
|||||||
|
|
||||||
// FillInSignature signs PDF contents and writes the signature into the given
|
// FillInSignature signs PDF contents and writes the signature into the given
|
||||||
// window that has been reserved for this specific purpose.
|
// window that has been reserved for this specific purpose.
|
||||||
// This is a very low-level function.
|
|
||||||
func FillInSignature(document []byte, signOff, signLen int,
|
func FillInSignature(document []byte, signOff, signLen int,
|
||||||
key crypto.PrivateKey, certs []*x509.Certificate) error {
|
key crypto.PublicKey, certs []*x509.Certificate) error {
|
||||||
if signOff < 0 || signOff > len(document) ||
|
if signOff < 0 || signOff > len(document) ||
|
||||||
signLen < 2 || signOff+signLen > len(document) {
|
signLen < 2 || signOff+signLen > len(document) {
|
||||||
return errors.New("invalid signing window")
|
return errors.New("invalid signing window")
|
||||||
@ -1078,13 +1039,7 @@ func FillInSignature(document []byte, signOff, signLen int,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf
|
|
||||||
// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
|
|
||||||
// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf
|
|
||||||
|
|
||||||
// Sign signs the given document, growing and returning the passed-in slice.
|
// Sign signs the given document, growing and returning the passed-in slice.
|
||||||
// There must be at least one certificate, matching the private key.
|
|
||||||
// The certificates must form a chain.
|
|
||||||
//
|
//
|
||||||
// The presumption here is that the document is valid and that it doesn't
|
// The presumption here is that the document is valid and that it doesn't
|
||||||
// employ cross-reference streams from PDF 1.5, or at least constitutes
|
// employ cross-reference streams from PDF 1.5, or at least constitutes
|
||||||
@ -1093,8 +1048,12 @@ func FillInSignature(document []byte, signOff, signLen int,
|
|||||||
//
|
//
|
||||||
// Carelessly assumes that the version of the original document is at most
|
// Carelessly assumes that the version of the original document is at most
|
||||||
// PDF 1.6.
|
// PDF 1.6.
|
||||||
func Sign(document []byte,
|
//
|
||||||
key crypto.PrivateKey, certs []*x509.Certificate) ([]byte, error) {
|
// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf
|
||||||
|
// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
|
||||||
|
// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf
|
||||||
|
func Sign(document []byte, key crypto.PublicKey, certs []*x509.Certificate) (
|
||||||
|
[]byte, error) {
|
||||||
pdf := &Updater{Document: document}
|
pdf := &Updater{Document: document}
|
||||||
if err := pdf.Initialize(); err != nil {
|
if err := pdf.Initialize(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1104,7 +1063,7 @@ func Sign(document []byte,
|
|||||||
if !ok || rootRef.Kind != Reference {
|
if !ok || rootRef.Kind != Reference {
|
||||||
return nil, errors.New("trailer does not contain a reference to Root")
|
return nil, errors.New("trailer does not contain a reference to Root")
|
||||||
}
|
}
|
||||||
root, _ := pdf.Get(rootRef.N, rootRef.Generation)
|
root := pdf.Get(rootRef.N, rootRef.Generation)
|
||||||
if root.Kind != Dict {
|
if root.Kind != Dict {
|
||||||
return nil, errors.New("invalid Root dictionary reference")
|
return nil, errors.New("invalid Root dictionary reference")
|
||||||
}
|
}
|
||||||
@ -1115,7 +1074,7 @@ func Sign(document []byte,
|
|||||||
pdf.Update(sigdictN, func(buf BytesWriter) {
|
pdf.Update(sigdictN, func(buf BytesWriter) {
|
||||||
// The timestamp is important for Adobe Acrobat Reader DC.
|
// The timestamp is important for Adobe Acrobat Reader DC.
|
||||||
// The ideal would be to use RFC 3161.
|
// The ideal would be to use RFC 3161.
|
||||||
now := NewDate(time.Now())
|
now := PdfDate(time.Now())
|
||||||
buf.WriteString("<< /Type/Sig /Filter/Adobe.PPKLite" +
|
buf.WriteString("<< /Type/Sig /Filter/Adobe.PPKLite" +
|
||||||
" /SubFilter/adbe.pkcs7.detached\n" +
|
" /SubFilter/adbe.pkcs7.detached\n" +
|
||||||
" /M" + now.Serialize() + " /ByteRange ")
|
" /M" + now.Serialize() + " /ByteRange ")
|
||||||
@ -1135,19 +1094,22 @@ func Sign(document []byte,
|
|||||||
signLen += 2
|
signLen += 2
|
||||||
})
|
})
|
||||||
|
|
||||||
sigfield := NewDict(map[string]Object{
|
sigfield := Object{Kind: Dict, Dict: map[string]Object{
|
||||||
// 8.6.3 Field Types - Signature Fields
|
// 8.6.3 Field Types - Signature Fields
|
||||||
"FT": NewName("Sig"),
|
"FT": {Kind: Name, String: "Sig"},
|
||||||
"V": NewReference(sigdictN, 0),
|
"V": {Kind: Reference, N: sigdictN, Generation: 0},
|
||||||
// 8.4.5 Annotations Types - Widget Annotations
|
// 8.4.5 Annotations Types - Widget Annotations
|
||||||
// We can merge the Signature Annotation and omit Kids here.
|
// We can merge the Signature Annotation and omit Kids here.
|
||||||
"Subtype": NewName("Widget"),
|
"Subtype": {Kind: Name, String: "Widget"},
|
||||||
"F": NewNumeric(2 /* Hidden */),
|
"F": {Kind: Numeric, Number: 2 /* Hidden */},
|
||||||
"T": NewString("Signature1"),
|
"T": {Kind: String, String: "Signature1"},
|
||||||
"Rect": NewArray([]Object{
|
"Rect": {Kind: Array, Array: []Object{
|
||||||
NewNumeric(0), NewNumeric(0), NewNumeric(0), NewNumeric(0),
|
{Kind: Numeric, Number: 0},
|
||||||
}),
|
{Kind: Numeric, Number: 0},
|
||||||
})
|
{Kind: Numeric, Number: 0},
|
||||||
|
{Kind: Numeric, Number: 0},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
sigfieldN := pdf.Allocate()
|
sigfieldN := pdf.Allocate()
|
||||||
pdf.Update(sigfieldN, func(buf BytesWriter) {
|
pdf.Update(sigfieldN, func(buf BytesWriter) {
|
||||||
@ -1158,7 +1120,7 @@ func Sign(document []byte,
|
|||||||
if !ok || pagesRef.Kind != Reference {
|
if !ok || pagesRef.Kind != Reference {
|
||||||
return nil, errors.New("invalid Pages reference")
|
return nil, errors.New("invalid Pages reference")
|
||||||
}
|
}
|
||||||
page := pdf.GetFirstPage(pagesRef.N, pagesRef.Generation)
|
page := PdfGetFirstPage(pdf, pagesRef.N, pagesRef.Generation)
|
||||||
if page.Kind != Dict {
|
if page.Kind != Dict {
|
||||||
return nil, errors.New("invalid or unsupported page tree")
|
return nil, errors.New("invalid or unsupported page tree")
|
||||||
}
|
}
|
||||||
@ -1166,9 +1128,10 @@ func Sign(document []byte,
|
|||||||
// XXX: Assuming this won't be an indirectly referenced array.
|
// XXX: Assuming this won't be an indirectly referenced array.
|
||||||
annots := page.Dict["Annots"]
|
annots := page.Dict["Annots"]
|
||||||
if annots.Kind != Array {
|
if annots.Kind != Array {
|
||||||
annots = NewArray(nil)
|
annots = Object{Kind: Array}
|
||||||
}
|
}
|
||||||
annots.Array = append(annots.Array, NewReference(sigfieldN, 0))
|
annots.Array = append(annots.Array, Object{
|
||||||
|
Kind: Reference, N: sigfieldN, Generation: 0})
|
||||||
|
|
||||||
page.Dict["Annots"] = annots
|
page.Dict["Annots"] = annots
|
||||||
pdf.Update(page.N, func(buf BytesWriter) {
|
pdf.Update(page.N, func(buf BytesWriter) {
|
||||||
@ -1177,16 +1140,19 @@ func Sign(document []byte,
|
|||||||
|
|
||||||
// 8.6.1 Interactive Form Dictionary
|
// 8.6.1 Interactive Form Dictionary
|
||||||
// XXX: Assuming there are no forms already, overwriting everything.
|
// XXX: Assuming there are no forms already, overwriting everything.
|
||||||
root.Dict["AcroForm"] = NewDict(map[string]Object{
|
root.Dict["AcroForm"] = Object{Kind: Dict, Dict: map[string]Object{
|
||||||
"Fields": NewArray([]Object{NewReference(sigfieldN, 0)}),
|
"Fields": {Kind: Array, Array: []Object{
|
||||||
"SigFlags": NewNumeric(3 /* SignaturesExist | AppendOnly */),
|
{Kind: Reference, N: sigfieldN, Generation: 0},
|
||||||
})
|
}},
|
||||||
|
"SigFlags": {Kind: Numeric,
|
||||||
|
Number: 3 /* SignaturesExist | AppendOnly */},
|
||||||
|
}}
|
||||||
|
|
||||||
// Upgrade the document version for SHA-256 etc.
|
// Upgrade the document version for SHA-256 etc.
|
||||||
// XXX: Assuming that it's not newer than 1.6 already--while Cairo can't
|
// XXX: Assuming that it's not newer than 1.6 already--while Cairo can't
|
||||||
// currently use a newer version that 1.5, it's not a bad idea to use
|
// currently use a newer version that 1.5, it's not a bad idea to use
|
||||||
// cairo_pdf_surface_restrict_to_version().
|
// cairo_pdf_surface_restrict_to_version().
|
||||||
root.Dict["Version"] = NewName("1.6")
|
root.Dict["Version"] = Object{Kind: Name, String: "1.6"}
|
||||||
pdf.Update(rootRef.N, func(buf BytesWriter) {
|
pdf.Update(rootRef.N, func(buf BytesWriter) {
|
||||||
buf.WriteString(root.Serialize())
|
buf.WriteString(root.Serialize())
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user