
在處理 PDF 文檔時(shí)我們經(jīng)常需要附帶一些補(bǔ)充文件。例如項(xiàng)目報(bào)告中需要包含生成圖表的原始 Excel 表格發(fā)票中需要附帶行程憑證或者技術(shù)文檔中需要提供 JSON 配置文件和日志。如果將這些文件單獨(dú)分發(fā)不僅管理起來(lái)比較繁瑣接收方也容易遺漏。將補(bǔ)充文件直接嵌入 PDF是解決這一問(wèn)題的一種常用方式。在 PDF 中添加附件主要有兩種形式文檔級(jí)附件附件注釋本文將演示如何使用 Python 實(shí)現(xiàn)這兩種附件嵌入方式。兩種附件形式的區(qū)別與適用場(chǎng)景在開(kāi)始編寫(xiě)代碼之前可以先根據(jù)附件與正文之間的關(guān)系判斷應(yīng)該使用哪一種方式文檔級(jí)附件Document-Level Attachment屬于整個(gè) PDF 文檔不會(huì)改變頁(yè)面原有的版面。用戶(hù)使用 PDF 閱讀器打開(kāi)文檔時(shí)可以通過(guò)“附件”面板查看和導(dǎo)出這些文件。這種方式適用于與整份文檔相關(guān)的補(bǔ)充材料如數(shù)據(jù)表、附錄、環(huán)境配置文件等。附件注釋Attachment Annotation與頁(yè)面上的特定位置關(guān)聯(lián)并以可視、可點(diǎn)擊的附件圖標(biāo)顯示。這種方式適用于與具體正文內(nèi)容直接相關(guān)的材料。例如在分析圖表旁放置對(duì)應(yīng)的數(shù)據(jù)源或者在某段內(nèi)容旁提供相關(guān)的原始文件。選擇原則不需要在頁(yè)面中體現(xiàn)位置關(guān)系的補(bǔ)充文件可以使用文檔級(jí)附件需要與特定段落、圖片或圖表建立上下文對(duì)應(yīng)關(guān)系時(shí)則更適合使用附件注釋。環(huán)境設(shè)置要運(yùn)行下面的代碼示例需要先安裝 PDF 處理所需的 Python 模塊pip install Spire.PDF然后導(dǎo)入示例中需要使用的類(lèi)from spire.pdf import * from spire.pdf.common import *方法一添加文檔級(jí)附件將文件添加為文檔級(jí)附件時(shí)文件會(huì)被加入 PDF 的文檔附件集合中不會(huì)修改任何頁(yè)面內(nèi)容。1. 添加單個(gè)附件下例演示如何將 Excel 數(shù)據(jù)文件嵌入一份 PDF 報(bào)告中from spire.pdf import * from spire.pdf.common import * # 創(chuàng)建 PdfDocument 對(duì)象并加載源文檔 pdf PdfDocument() pdf.LoadFromFile(report.pdf) # 創(chuàng)建附件對(duì)象 attachment PdfAttachment(source-data.xlsx) # 將附件添加到文檔附件集合中 pdf.Attachments.Add(attachment) # 保存更新后的 PDF 文檔 pdf.SaveToFile(report-with-attachment.pdf)PdfAttachment用于根據(jù)外部文件創(chuàng)建附件對(duì)象然后通過(guò)Attachments.Add()將其加入 PDF 的附件集合。保存后的 PDF 頁(yè)面不會(huì)發(fā)生變化。使用支持 PDF 附件的閱讀器打開(kāi)文件后可以從附件面板中查看并打開(kāi)source-data.xlsx。2. 添加多個(gè)附件如果需要將多個(gè)相關(guān)文件隨 PDF 一起發(fā)布可以分別創(chuàng)建PdfAttachment對(duì)象并添加到文檔中# 創(chuàng)建多個(gè)附件 attachment1 PdfAttachment(source-data.xlsx) attachment2 PdfAttachment(meeting-notes.docx) attachment3 PdfAttachment(architecture.png) # 添加到文檔附件集合 pdf.Attachments.Add(attachment1) pdf.Attachments.Add(attachment2) pdf.Attachments.Add(attachment3)如果附件數(shù)量較多也可以通過(guò)列表和循環(huán)處理attachment_files [ source-data.xlsx, meeting-notes.docx, architecture.png ] for file in attachment_files: pdf.Attachments.Add(PdfAttachment(file))這樣可以把 PDF 和相關(guān)資料統(tǒng)一保存在同一個(gè)文件中同時(shí)不會(huì)改變正文頁(yè)面原有的布局。需要注意的是附件文件會(huì)實(shí)際寫(xiě)入 PDF因此附件越大、數(shù)量越多最終生成的 PDF 文件體積也會(huì)相應(yīng)增加。方法二在指定頁(yè)面添加附件注釋當(dāng)附件需要與頁(yè)面中的某個(gè)具體元素例如表格、圖片、圖表或段落對(duì)應(yīng)時(shí)可以使用附件注釋。1. 基本實(shí)現(xiàn)代碼創(chuàng)建附件注釋時(shí)需要指定它在頁(yè)面中的位置區(qū)域RectangleF并傳入需要嵌入的文件數(shù)據(jù)流Streamfrom spire.pdf import * from spire.pdf.common import * pdf PdfDocument() pdf.LoadFromFile(report.pdf) # 獲取目標(biāo)頁(yè)面這里為第 1 頁(yè)索引從 0 開(kāi)始 page pdf.Pages.get_Item(0) # 讀取附件文件 data_stream Stream(source-data.xlsx) # 設(shè)置圖標(biāo)在頁(yè)面上的位置和尺寸 # 參數(shù)依次為X 坐標(biāo)、Y 坐標(biāo)、寬度、高度 bounds RectangleF(50.0, 100.0, 16.0, 16.0) # 創(chuàng)建附件注釋 annotation PdfAttachmentAnnotation( bounds, source-data.xlsx, data_stream ) # 設(shè)置外觀和提示文字 annotation.Color PdfRGBColor(Color.get_Blue()) annotation.Flags PdfAnnotationFlags.Default annotation.Icon PdfAttachmentIcon.Graph annotation.Text 雙擊打開(kāi)原始數(shù)據(jù)表格 # 將附件注釋添加到頁(yè)面 page.AnnotationsWidget.Add(annotation) pdf.SaveToFile(report-with-page-attachment.pdf)這里最關(guān)鍵的是bounds RectangleF(50.0, 100.0, 16.0, 16.0)四個(gè)參數(shù)分別表示附件圖標(biāo)的 X 坐標(biāo)、Y 坐標(biāo)、寬度和高度。另外page pdf.Pages.get_Item(0)獲取的是 PDF 的第一頁(yè)因?yàn)轫?yè)面索引從0開(kāi)始。如果要把附件添加到其他頁(yè)面需要根據(jù)實(shí)際頁(yè)碼調(diào)整索引值。2. 常見(jiàn)的附件圖標(biāo)樣式PdfAttachmentIcon提供了多種附件注釋的內(nèi)置圖標(biāo)樣式。這些枚舉值只影響附件圖標(biāo)的顯示形式并不會(huì)限制附件實(shí)際可以使用的文件類(lèi)型。枚舉值圖標(biāo)樣式PdfAttachmentIcon.Graph圖表樣式PdfAttachmentIcon.Paperclip回形針樣式PdfAttachmentIcon.PushPin圖釘樣式PdfAttachmentIcon.Tag標(biāo)簽樣式可以根據(jù)頁(yè)面內(nèi)容和附件用途選擇合適的圖標(biāo)。例如annotation.Icon PdfAttachmentIcon.Paperclip進(jìn)階運(yùn)用為附件圖標(biāo)配上文字說(shuō)明孤零零放一個(gè) 16x16 像素的附件小圖標(biāo)讀者可能不知道干嘛用的。我們可以在畫(huà)布上直接畫(huà)一行引導(dǎo)文字再根據(jù)文字寬度將附件圖標(biāo)放置在文字后面# 1. 在頁(yè)面 Canvas 上繪制提示文字 label_text 附件生成該圖表對(duì)應(yīng)的原始數(shù)據(jù) font PdfTrueTypeFont(Arial, 10.0, PdfFontStyle.Regular, True) x_pos 50.0 y_pos 150.0 page.Canvas.DrawString( label_text, font, PdfBrushes.get_Black(), x_pos, y_pos ) # 2. 獲取文字實(shí)際寬度 text_width font.MeasureString(label_text).Width # 3. 將附件圖標(biāo)放在文字右側(cè) 5 點(diǎn)的位置 icon_bounds RectangleF( x_pos text_width 5.0, y_pos, 16.0, 16.0 ) # 4. 創(chuàng)建并添加附件注釋 annotation PdfAttachmentAnnotation( icon_bounds, source-data.xlsx, data_stream ) annotation.Icon PdfAttachmentIcon.Paperclip page.AnnotationsWidget.Add(annotation)最終頁(yè)面上的效果可以理解為附件生成該圖表對(duì)應(yīng)的原始數(shù)據(jù)這里沒(méi)有直接寫(xiě)死附件圖標(biāo)的 X 坐標(biāo)而是先通過(guò)font.MeasureString(label_text).Width獲取文字實(shí)際占用的寬度再計(jì)算圖標(biāo)的位置。如果提示文字發(fā)生變化圖標(biāo)仍然可以自動(dòng)保持在文字后方。這種方式比固定坐標(biāo)更適合動(dòng)態(tài)生成的 PDF 文檔。補(bǔ)充操作提取 PDF 中的文檔級(jí)附件在自動(dòng)化處理流程中除了向 PDF 中寫(xiě)入附件有時(shí)還需要讀取已經(jīng)存在的文檔級(jí)附件并將它們保存到本地。from spire.pdf import * from spire.pdf.common import * pdf PdfDocument() pdf.LoadFromFile(report-with-attachment.pdf) # 遍歷文檔級(jí)附件 for i in range(pdf.Attachments.Count): attachment pdf.Attachments.get_Item(i) # 將附件保存到本地 with open(attachment.FileName, wb) as f: f.write(attachment.Data) pdf.Close()這里訪問(wèn)的pdf.Attachments是 PDF 的文檔級(jí)附件集合。需要注意的是通過(guò)附件注釋嵌入的文件屬于頁(yè)面注釋并不包含在這個(gè)集合中。如果需要讀取頁(yè)面上的附件注釋則需要進(jìn)一步遍歷相應(yīng)頁(yè)面的注釋集合。閱讀器兼容性說(shuō)明附件成功寫(xiě)入 PDF 后實(shí)際的查看和交互體驗(yàn)還會(huì)受到 PDF 閱讀器的影響。桌面端 PDF 閱讀器如 Adobe Acrobat Reader、Foxit PDF Reader 等一般能夠較完整地顯示文檔級(jí)附件面板并支持頁(yè)面上的附件注釋。瀏覽器內(nèi)置 PDF 查看器如 Chrome、Edge、Firefox 等主要用于顯示 PDF 頁(yè)面內(nèi)容對(duì)附件面板和附件注釋等交互功能的支持可能有所不同。有些情況下用戶(hù)可能無(wú)法像在桌面閱讀器中一樣直接訪問(wèn)附件。因此如果附件屬于業(yè)務(wù)流程中的關(guān)鍵文件例如財(cái)務(wù)憑證、合同附件、審計(jì)資料或項(xiàng)目交付文件最好在正式交付前使用接收方實(shí)際可能使用的 PDF 閱讀器進(jìn)行測(cè)試??偨Y(jié)使用 Python 給 PDF 添加附件時(shí)主要需要根據(jù)附件與正文內(nèi)容之間的關(guān)系選擇合適的方式如果文件與整份 PDF 相關(guān)并且不需要在頁(yè)面中體現(xiàn)具體位置可以使用文檔級(jí)附件如果文件與正文中的某個(gè)圖表、圖片或段落直接相關(guān)可以使用附件注釋并將附件圖標(biāo)放在對(duì)應(yīng)內(nèi)容附近。兩種方式都可以將外部文件直接嵌入 PDF但在文檔結(jié)構(gòu)和閱讀方式上各有側(cè)重。實(shí)際使用時(shí)只需要判斷附件屬于整份文檔還是與某個(gè)具體頁(yè)面內(nèi)容相關(guān)再選擇相應(yīng)的實(shí)現(xiàn)方式即可。