Swift中如何原生或借助免费库向PDF添加非图片类附件?
First off, your existing Swift 3 code for generating PDFs is solid—let's extend it to support file attachments without relying on commercial libraries like Quick PDF Library. Below are two reliable, free approaches using Apple's native frameworks, plus a free open-source option for older OS versions.
1. Native Implementation with Core Graphics (iOS 11+/macOS 10.13+)
Core Graphics provides a direct way to add document-level attachments while you're generating the PDF. You can use the CGPDFContextAddDocumentAttachment function right after initializing your PDF context. Here's how to modify your existing createPDFcontent function:
func createPDFcontent(_ msgIDfunc: String) { self.doctitle = msgIDfunc self.author = "blabla" self.creator = "blabla" self.subject = msgIDfunc var infoDict = [String: AnyObject]() infoDict[kCGPDFContextTitle as String] = self.doctitle as NSString? infoDict[kCGPDFContextAuthor as String] = self.author as NSString? infoDict[kCGPDFContextCreator as String] = self.creator as NSString? infoDict[kCGPDFContextSubject as String] = self.subject as NSString? let HtmlForPDF = "<b><i><font color='red'>+++ Hello World +++</i></b></font>" let frmt = UIMarkupTextPrintFormatter(markupText: HtmlForPDF) let render = UIPrintPageRenderer() render.addPrintFormatter(frmt, startingAtPageAt: 0) let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) let printable = page.insetBy(dx: 0, dy: 0) render.setValue(NSValue(cgRect: page), forKey: "paperRect") render.setValue(NSValue(cgRect: printable), forKey: "printableRect") let filedata = NSMutableData() UIGraphicsBeginPDFContextToData(filedata, CGRect.zero, infoDict) // --- Add this section to attach a file --- guard let pdfContext = UIGraphicsGetCurrentContext(), // Replace this with data from your actual file (e.g., Data(contentsOf: fileURL)) let attachmentData = "This is sample attachment content.".data(using: .utf8) else { UIGraphicsEndPDFContext() return } let filename = "sample_attachment.txt" let mimeType = "text/plain" // Embed the attachment directly in the PDF (not just a link) CGPDFContextAddDocumentAttachment( pdfContext, filename as CFString, attachmentData as CFData, mimeType as CFString, kCGPDFContextAttachmentTypeEmbedded ) // --- End attachment section --- for i in 1...render.numberOfPages { UIGraphicsBeginPDFPage(); let bounds = UIGraphicsGetPDFContextBounds() render.drawPage(at: i - 1, in: bounds) } UIGraphicsEndPDFContext(); let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] filedata.write(toFile: "\(documentsPath)/blabla_with_attachment.pdf", atomically: true) }
Key notes:
kCGPDFContextAttachmentTypeEmbeddedensures the file is stored directly inside the PDF, not just linked externally.- Swap the sample
attachmentDatawith data from any file type you need (e.g., CSV, JSON, or plain text) usingData(contentsOf: URL).
2. Easier Native Implementation with PDFKit (iOS 11+/macOS 10.13+)
If you prefer a more high-level, intuitive API, PDFKit (Apple's built-in framework) simplifies adding attachments after generating the PDF. This works great for both new and existing PDFs:
func createPDFcontentWithPDFKit(_ msgIDfunc: String) { self.doctitle = msgIDfunc self.author = "blabla" self.creator = "blabla" self.subject = msgIDfunc var infoDict = [String: AnyObject]() infoDict[kCGPDFContextTitle as String] = self.doctitle as NSString? infoDict[kCGPDFContextAuthor as String] = self.author as NSString? infoDict[kCGPDFContextCreator as String] = self.creator as NSString? infoDict[kCGPDFContextSubject as String] = self.subject as NSString? let HtmlForPDF = "<b><i><font color='red'>+++ Hello World +++</i></b></font>" let frmt = UIMarkupTextPrintFormatter(markupText: HtmlForPDF) let render = UIPrintPageRenderer() render.addPrintFormatter(frmt, startingAtPageAt: 0) let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) let printable = page.insetBy(dx: 0, dy: 0) render.setValue(NSValue(cgRect: page), forKey: "paperRect") render.setValue(NSValue(cgRect: printable), forKey: "printableRect") let filedata = NSMutableData() UIGraphicsBeginPDFContextToData(filedata, CGRect.zero, infoDict) for i in 1...render.numberOfPages { UIGraphicsBeginPDFPage(); let bounds = UIGraphicsGetPDFContextBounds() render.drawPage(at: i - 1, in: bounds) } UIGraphicsEndPDFContext(); // --- Use PDFKit to add attachment --- guard let pdfDocument = PDFDocument(data: filedata as Data), let attachmentData = "Sample attachment content".data(using: .utf8) else { return } let attachment = PDFAttachment(data: attachmentData, mimeType: "text/plain") attachment.filename = "my_attachment.txt" attachment.displayName = "My Text Attachment" // Optional: user-friendly display name pdfDocument.addAttachment(attachment) // Save the updated PDF let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] if let updatedData = pdfDocument.dataRepresentation() { updatedData.write(toFile: "\(documentsPath)/blabla_with_pdfkit_attachment.pdf", atomically: true) } }
PDFKit handles all the low-level PDF structure work for you, making this approach cleaner and easier to maintain.
3. Free Open-Source Option for Older OS Versions: libharu
If you need support for iOS/macOS versions before 11, you can use libharu—a free, cross-platform PDF generation library. You'll need to either wrap it in Swift directly or use a CocoaPods wrapper like HaruPDF.
Libharu supports adding attachments via its HPDF_AddAttachment function. While setup is a bit more involved, it's a reliable free alternative for legacy projects.
All these approaches avoid commercial tools and work with native or free open-source resources. Let me know if you need help adapting any of this to your specific file types or use case!
内容的提问来源于stack exchange,提问作者Romeosan

