1 package gofpdf
2
3 import (
4 "crypto/md5"
5 "encoding/hex"
6 "fmt"
7 "strings"
8 )
9
10
11
12
13
14 type Attachment struct {
15 Content []byte
16
17
18 Filename string
19
20
21
22 Description string
23
24 objectNumber int
25 }
26
27
28 func checksum(data []byte) string {
29 tmp := md5.Sum(data)
30 sl := make([]byte, len(tmp))
31 for i, v := range tmp {
32 sl[i] = v
33 }
34 return hex.EncodeToString(sl)
35 }
36
37
38
39 func (f *Fpdf) writeCompressedFileObject(content []byte) {
40 lenUncompressed := len(content)
41 sum := checksum(content)
42 compressed := sliceCompress(content)
43 lenCompressed := len(compressed)
44 f.newobj()
45 f.outf("<< /Type /EmbeddedFile /Length %d /Filter /FlateDecode /Params << /CheckSum <%s> /Size %d >> >>\n",
46 lenCompressed, sum, lenUncompressed)
47 f.putstream(compressed)
48 f.out("endobj")
49 }
50
51
52 func (f *Fpdf) embed(a *Attachment) {
53 if a.objectNumber != 0 {
54 return
55 }
56 oldState := f.state
57 f.state = 1
58 f.writeCompressedFileObject(a.Content)
59 streamID := f.n
60 f.newobj()
61 f.outf("<< /Type /Filespec /F () /UF %s /EF << /F %d 0 R >> /Desc %s\n>>",
62 f.textstring(utf8toutf16(a.Filename)),
63 streamID,
64 f.textstring(utf8toutf16(a.Description)))
65 f.out("endobj")
66 a.objectNumber = f.n
67 f.state = oldState
68 }
69
70
71
72
73
74
75
76 func (f *Fpdf) SetAttachments(as []Attachment) {
77 f.attachments = as
78 }
79
80
81
82 func (f *Fpdf) putAttachments() {
83 for i, a := range f.attachments {
84 f.embed(&a)
85 f.attachments[i] = a
86 }
87 }
88
89
90 func (f Fpdf) getEmbeddedFiles() string {
91 names := make([]string, len(f.attachments))
92 for i, as := range f.attachments {
93 names[i] = fmt.Sprintf("(Attachement%d) %d 0 R ", i+1, as.objectNumber)
94 }
95 nameTree := fmt.Sprintf("<< /Names [\n %s \n] >>", strings.Join(names, "\n"))
96 return nameTree
97 }
98
99
100
101 type annotationAttach struct {
102 *Attachment
103
104 x, y, w, h float64
105 }
106
107
108
109
110
111
112
113
114
115
116
117 func (f *Fpdf) AddAttachmentAnnotation(a *Attachment, x, y, w, h float64) {
118 if a == nil {
119 return
120 }
121 f.pageAttachments[f.page] = append(f.pageAttachments[f.page], annotationAttach{
122 Attachment: a,
123 x: x * f.k, y: f.hPt - y*f.k, w: w * f.k, h: h * f.k,
124 })
125 }
126
127
128
129
130 func (f *Fpdf) putAnnotationsAttachments() {
131
132 m := map[*Attachment]bool{}
133 for _, l := range f.pageAttachments {
134 for _, an := range l {
135 if m[an.Attachment] {
136 continue
137 }
138 f.embed(an.Attachment)
139 }
140 }
141 }
142
143 func (f *Fpdf) putAttachmentAnnotationLinks(out *fmtBuffer, page int) {
144 for _, an := range f.pageAttachments[page] {
145 x1, y1, x2, y2 := an.x, an.y, an.x+an.w, an.y-an.h
146 as := fmt.Sprintf("<< /Type /XObject /Subtype /Form /BBox [%.2f %.2f %.2f %.2f] /Length 0 >>",
147 x1, y1, x2, y2)
148 as += "\nstream\nendstream"
149
150 out.printf("<< /Type /Annot /Subtype /FileAttachment /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0]\n",
151 x1, y1, x2, y2)
152 out.printf("/Contents %s ", f.textstring(utf8toutf16(an.Description)))
153 out.printf("/T %s ", f.textstring(utf8toutf16(an.Filename)))
154 out.printf("/AP << /N %s>>", as)
155 out.printf("/FS %d 0 R >>\n", an.objectNumber)
156 }
157 }
158
View as plain text