/
write.go
110 lines (98 loc) · 2.43 KB
/
write.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package gdf
import (
"bytes"
"crypto/md5"
"encoding/hex"
"io"
"slices"
)
func writeHeader(p *PDF, w io.Writer) error {
if p.info == nil && len(p.catalog.Acroform.acrofields) == 0 {
n, err := w.Write([]byte("%PDF-2.0\n\x81\x81\x81\x81\n"))
p.n += n
return err
}
n, err := w.Write([]byte("%PDF-1.7\n\x81\x81\x81\x81\n"))
p.n += n
return err
}
func writeObjects(p *PDF, w io.Writer) error {
for _, obj := range p.objects {
p.xref = append(p.xref, p.n)
t, err := w.Write([]byte(itoa(obj.id()) + "\x200\x20obj\n"))
p.n += t
if err != nil {
return err
}
t, err = obj.encode(w)
p.n += t
if err != nil {
return err
}
t, err = w.Write([]byte("endobj\n"))
p.n += t
if err != nil {
return err
}
}
return nil
}
func writeXref(p *PDF, w io.Writer) error {
p.xref = append(p.xref, p.n) // adding the offset even though it won't be included yet
t, err := w.Write([]byte("xref\n" +
"0\x20" + itoa(len(p.xref)) + "\n" +
"0000000000\x2065536\x20f\n\r"))
p.n += t
if err != nil {
return err
}
b := []byte("0000000000\x2000000\x20n\n\r")
for i := 0; i < len(p.xref)-1; i++ {
pad10(p.xref[i], b)
t, err := w.Write(b)
p.n += t
if err != nil {
return err
}
}
return nil
}
func writeTrailer(p *PDF, w io.Writer) error {
t, err := w.Write([]byte("trailer\n"))
if err != nil {
return err
}
p.n += t
// 14.5: The ID string should be based on the current time, the PDF's file location, or the size of the file in bytes.
// Since the first two options are not WASM compatible, we'll go with the third, but it will be annoying.
// The md5 hash produces a 16 byte output, which is 32 bytes when hex-encoded. idx will serve as a placeholder.
idx := make([]byte, 32)
fields := []field{
{"/Size", len(p.xref)},
{"/ID", slices.Concat([]byte("[<"), idx, []byte("> <"), idx, []byte(">]"))},
{"/Root", "1\x200\x20R"},
}
if p.info != nil {
fields = append(fields, field{"/Info", iref(p.info)})
}
buf := dict(512, fields)
buf = append(buf, "startxref\n"...)
buf = itobuf(p.xref[len(p.xref)-1], buf)
buf = append(buf, "\n%%EOF\n"...)
// now that everything has been appended to buf, we know the final file length.
h := md5.New()
h.Write(itob(p.n + len(buf)))
idx = hex.AppendEncode(nil, h.Sum(nil))
// Overwrite the 0 bytes.
i := bytes.IndexByte(buf, 0)
if i > -1 {
copy(buf[i:], idx)
}
i = bytes.IndexByte(buf, 0)
if i > -1 {
copy(buf[i:], idx)
}
t, err = w.Write(buf)
p.n += t
return err
}