forked from jung-kurt/gofpdf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
242 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package gofpdf | ||
|
||
import ( | ||
"crypto/md5" | ||
"encoding/hex" | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Attachment defines a content to be included in the pdf, in one | ||
// of the following ways : | ||
// - associated with the document as a whole : see SetAttachments() | ||
// - accessible via a link localized on a page : see AddAttachmentAnnotation() | ||
type Attachment struct { | ||
Content []byte | ||
|
||
// Filename is the displayed name of the attachment | ||
Filename string | ||
|
||
// Description is only displayed when using AddAttachmentAnnotation(), | ||
// and might be modified by the pdf reader. | ||
Description string | ||
|
||
objectNumber int // filled when content is included | ||
} | ||
|
||
// return the hex encoded checksum of `data` | ||
func checksum(data []byte) string { | ||
tmp := md5.Sum(data) | ||
sl := make([]byte, len(tmp)) | ||
for i, v := range tmp { | ||
sl[i] = v | ||
} | ||
return hex.EncodeToString(sl) | ||
} | ||
|
||
// Writes a compressed file like object as ``/EmbeddedFile``. Compressing is | ||
// done with deflate. Includes length, compressed length and MD5 checksum. | ||
func (f *Fpdf) writeCompressedFileObject(content []byte) { | ||
lenUncompressed := len(content) | ||
sum := checksum(content) | ||
compressed := sliceCompress(content) | ||
lenCompressed := len(compressed) | ||
f.newobj() | ||
f.outf("<< /Type /EmbeddedFile /Length %d /Filter /FlateDecode /Params << /CheckSum <%s> /Size %d >> >>\n", | ||
lenCompressed, sum, lenUncompressed) | ||
f.putstream(compressed) | ||
f.out("endobj") | ||
} | ||
|
||
// Embed includes the content of `a`, and update its internal reference. | ||
func (f *Fpdf) embed(a *Attachment) { | ||
if a.objectNumber != 0 { // already embedded (objectNumber start at 2) | ||
return | ||
} | ||
oldState := f.state | ||
f.state = 1 // we write file content in the main buffer | ||
f.writeCompressedFileObject(a.Content) | ||
streamID := f.n | ||
f.newobj() | ||
f.outf("<< /Type /Filespec /F () /UF %s /EF << /F %d 0 R >> /Desc %s\n>>", | ||
f.textstring(utf8toutf16(a.Filename)), | ||
streamID, | ||
f.textstring(utf8toutf16(a.Description))) | ||
f.out("endobj") | ||
a.objectNumber = f.n | ||
f.state = oldState | ||
} | ||
|
||
// SetAttachments writes attachments as embedded files (document attachment). | ||
// These attachments are global, see AddAttachmentAnnotation() for a link | ||
// anchored in a page. Note that only the last call of SetAttachments is | ||
// useful, previous calls are discarded. Be aware that not all PDF readers | ||
// support document attachments. See the SetAttachment example for a | ||
// demonstration of this method. | ||
func (f *Fpdf) SetAttachments(as []Attachment) { | ||
f.attachments = as | ||
} | ||
|
||
// embed current attachments. store object numbers | ||
// for later use by getEmbeddedFiles() | ||
func (f *Fpdf) putAttachments() { | ||
for i, a := range f.attachments { | ||
f.embed(&a) | ||
f.attachments[i] = a | ||
} | ||
} | ||
|
||
// return /EmbeddedFiles tree name catalog entry. | ||
func (f Fpdf) getEmbeddedFiles() string { | ||
names := make([]string, len(f.attachments)) | ||
for i, as := range f.attachments { | ||
names[i] = fmt.Sprintf("(Attachement%d) %d 0 R ", i+1, as.objectNumber) | ||
} | ||
nameTree := fmt.Sprintf("<< /Names [\n %s \n] >>", strings.Join(names, "\n")) | ||
return nameTree | ||
} | ||
|
||
// ---------------------------------- Annotations ---------------------------------- | ||
|
||
type annotationAttach struct { | ||
*Attachment | ||
|
||
x, y, w, h float64 // fpdf coordinates (y diff and scaling done) | ||
} | ||
|
||
// AddAttachmentAnnotation puts a link on the current page, on the rectangle | ||
// defined by `x`, `y`, `w`, `h`. This link points towards the content defined | ||
// in `a`, which is embedded in the document. Note than no drawing is done by | ||
// this method : a method like `Cell()` or `Rect()` should be called to | ||
// indicate to the reader that there is a link here. Requiring a pointer to an | ||
// Attachment avoids useless copies in the resulting pdf: attachment pointing | ||
// to the same data will have their content only be included once, and be | ||
// shared amongst all links. Be aware that not all PDF readers support | ||
// annotated attachments. See the AddAttachmentAnnotation example for a | ||
// demonstration of this method. | ||
func (f *Fpdf) AddAttachmentAnnotation(a *Attachment, x, y, w, h float64) { | ||
if a == nil { | ||
return | ||
} | ||
f.pageAttachments[f.page] = append(f.pageAttachments[f.page], annotationAttach{ | ||
Attachment: a, | ||
x: x * f.k, y: f.hPt - y*f.k, w: w * f.k, h: h * f.k, | ||
}) | ||
} | ||
|
||
// embed current annotations attachments. store object numbers | ||
// for later use by putAttachmentAnnotationLinks(), which is | ||
// called for each page. | ||
func (f *Fpdf) putAnnotationsAttachments() { | ||
// avoid duplication | ||
m := map[*Attachment]bool{} | ||
for _, l := range f.pageAttachments { | ||
for _, an := range l { | ||
if m[an.Attachment] { // already embedded | ||
continue | ||
} | ||
f.embed(an.Attachment) | ||
} | ||
} | ||
} | ||
|
||
func (f *Fpdf) putAttachmentAnnotationLinks(out *fmtBuffer, page int) { | ||
for _, an := range f.pageAttachments[page] { | ||
x1, y1, x2, y2 := an.x, an.y, an.x+an.w, an.y-an.h | ||
as := fmt.Sprintf("<< /Type /XObject /Subtype /Form /BBox [%.2f %.2f %.2f %.2f] /Length 0 >>", | ||
x1, y1, x2, y2) | ||
as += "\nstream\nendstream" | ||
|
||
out.printf("<< /Type /Annot /Subtype /FileAttachment /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0]\n", | ||
x1, y1, x2, y2) | ||
out.printf("/Contents %s ", f.textstring(utf8toutf16(an.Description))) | ||
out.printf("/T %s ", f.textstring(utf8toutf16(an.Filename))) | ||
out.printf("/AP << /N %s>>", as) | ||
out.printf("/FS %d 0 R >>\n", an.objectNumber) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters