Swift: Add file attachments (not only images) into PDF - swift

How can I add other file attachments than images into a PDF natively in Swift or with "free" libraries? I know it is i.e. possible with the commercial library "quick pdf library" from debenu.com.
Today my working Swift3 code looks like this but does not include the above mentioned file attachment functionality:
// function PDF creation
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>"
// new hmtl version
let frmt = UIMarkupTextPrintFormatter(markupText: HtmlForPDF)
// set print format
let render = UIPrintPageRenderer()
render.addPrintFormatter(frmt, startingAtPageAt: 0)
// create Paper Size for print
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")
// create PDF context and draw
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();
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
filedata.write(toFile: "\(documentsPath)/blabla.pdf", atomically: true)
}
Any hint is welcome.

Related

Draw text on all pages of PDF using PDFKit

I'm using the following code to draw text on a PDF Document.This seems to draw the text only on a single page.I'm trying to iterate through each page,draw string on it and finally display the PDF document from the MutableData. How do I draw the string on all pages?
var pdffile=PDFDocument(url: input)
let data = NSMutableData()
let consumer = CGDataConsumer(data: data as CFMutableData)!
for y in stride(from: 0, to: pdffile!.pageCount, by: 1)
{
let page: PDFPage = pdffile!.page(at: y)!
let outputBounds = page.bounds(for: PDFDisplayBox.mediaBox)
var mediaBox = CGRect(x: 0, y: 0, width: outputBounds.size.width, height: outputBounds.size.height)
let context = CGContext(consumer: consumer, mediaBox: &mediaBox, nil)!
NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false)
context.beginPDFPage(nil)
page.draw(with: .mediaBox, to: context)
text.draw(in:drawrect,withAttributes:textFontAttributes);
context.endPDFPage()
context.closePDF()
}
let anotherDocument = PDFDocument(data:data as Data)
pdfview.document=anotherDocument
The main issue here is that context recreated, for multiple pages we should write into the same context (it manages pages by beginPDFPage/endPDFPage pair).
Here is fixed code. Tested with Xcode 13.4 / macOS 12.4
let pdffile = PDFDocument(url: input)
let data = NSMutableData()
let consumer = CGDataConsumer(data: data as CFMutableData)!
// create common context with no mediaBox, we will add it later
// per-page (because, actually they might be different)
let context = CGContext(consumer: consumer, mediaBox: nil, nil)!
for y in stride(from: 0, to: pdffile!.pageCount, by: 1)
{
let page: PDFPage = pdffile!.page(at: y)!
// re-use media box of original document as-is w/o changes !!
var mediaBox = page.bounds(for: PDFDisplayBox.mediaBox)
NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false)
// prepare mediaBox data for page setup
let rectData = NSData(bytes: &mediaBox, length: MemoryLayout.size(ofValue: mediaBox))
context.beginPDFPage([kCGPDFContextMediaBox as String: rectData] as CFDictionary) // << here !!
page.draw(with: .mediaBox, to: context) // << original !!
text.draw(in:drawrect,withAttributes:textFontAttributes) // << over !!
context.endPDFPage()
}
context.closePDF() // close entire document
let anotherDocument = PDFDocument(data:data as Data)
// ... as used before

Swift - Overlay Text on current PDF Document

I have been trying to simply overlay text onto a current PDF document that is essentially a timecard. I copy the file to the downloads folder and that works fine, but then when I try to use a CGContext to add text, it exports a white PDF document. Can anyone see where I'm going wrong?
do {
try fileManager.copyItem(at: pdfURL!, to: destinationURL)
} catch let error as NSError {
print("Copy failed :( with error: \(error)")
}
if let pdf: CGPDFDocument = CGPDFDocument(destinationURL as CFURL) { // Create a PDF Document
if pdf.numberOfPages == 1 {
let pdfPage: CGPDFPage = pdf.page(at: 1)!
let pageRect = pdfPage.getBoxRect(CGPDFBox.mediaBox)
//print(pageRect)
let context = CGContext.init(destinationURL as CFURL, mediaBox: nil, nil)
let font = NSFont(name: "Helvetica Bold", size: 20.0)
let textRect = CGRect(x: 250, y: 250, width: 500, height: 40)
let paragraphStyle: NSParagraphStyle = NSParagraphStyle.default
let textColor = NSColor.black
let textFontAttributes = [
NSAttributedStringKey.font: font!,
NSAttributedStringKey.foregroundColor: textColor,
NSAttributedStringKey.paragraphStyle: paragraphStyle
]
let text: NSString = "Hello world"
text.draw(in: textRect, withAttributes: textFontAttributes)
context?.addRect(textRect)
context?.closePDF()
}
}
The following code is what I used to overlay text on macOS. I've been trying to find a link to the source answer I got this from. If I find it I'll edit this answer with a link.
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = CGRect(x: 0, y: 0, width: 792, height: 612)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
let graphicsContext = NSGraphicsContext(cgContext: context!, flipped: false)
NSGraphicsContext.current = graphicsContext
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
// Parse and Draw Text on the context
drawText()
context!.saveGState()
context!.restoreGState()
context!.endPDFPage()
NSGraphicsContext.current = nil
context?.closePDF()
}

Swift converted to PDF but not stops

I want to generate pdf from textview after user enters the data. The textview contains paragraphs and attributed texts.
I have Followed
this procedure
and my code is:
func createPdf() {
createPDFNamed("test")
}
func getPDFPath(_ name: String) -> String {
let newPDFName = "\(name).pdf"
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let pdfPath: String = (documentsDirectory as String).appending(newPDFName);
print(pdfPath)
return pdfPath
}
func createPDFNamed(_ name: String) {
let text = myTextView.text
let currentText: CFAttributedString = CFAttributedStringCreate(nil, (text as CFString?), nil)
if currentText != nil {
let framesetter: CTFramesetter = CTFramesetterCreateWithAttributedString(currentText)
if framesetter != nil {
let pdfFileName: String = getPDFPath(name)
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRect.zero, nil)
var currentRange: CFRange! = CFRangeMake(0, 0)
var currentPage: Int = 0
var done = false
repeat {
UIGraphicsBeginPDFPageWithInfo(CGRect(x: 0, y: 0, width: 612, height: 792), nil)
currentPage += 1
drawPageNbr(currentPage)
currentRange = renderPagewithTextRange(currentRange, andFramesetter: framesetter)
if currentRange.location == CFAttributedStringGetLength((currentText as? CFAttributedString)) {
done = true
}
} while !done
UIGraphicsEndPDFContext()
}
else {
print("Could not create the framesetter needed to lay out the atrributed string.")
}
}
else {
print("Could not create the attributed string for the framesetter")
}
}
func renderPagewithTextRange(_ currentRange: CFRange, andFramesetter framesetter: CTFramesetter) -> CFRange {
var currentRange: CFRange! = CFRangeMake(0, 0)
let currentContext: CGContext? = UIGraphicsGetCurrentContext()
currentContext?.textMatrix = .identity
let frameRect = CGRect(x: 72, y: 72, width: 468, height: 648)
let framePath: CGMutablePath = CGMutablePath()
framePath.addRect(frameRect, transform: .identity)
let frameRef: CTFrame = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil)
currentContext?.translateBy(x: 0, y: 792)
currentContext?.scaleBy(x: 1.0, y: -1.0)
CTFrameDraw(frameRef, currentContext!)
currentRange = CTFrameGetVisibleStringRange(frameRef)
currentRange.location += currentRange.length
currentRange.length = 0 as? CFIndex ?? CFIndex()
return currentRange
}
func drawPageNumber(_ pageNum: Int) {
let pageString = "Page \(Int(pageNum))"
let theFont = UIFont.systemFont(ofSize: 12)
let pageStringSize: CGSize = pageString.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17.0)])
let stringRect = CGRect(x: ((612.0 - pageStringSize.width) / 2.0), y: 720.0 + ((72.0 - pageStringSize.height) / 2.0), width: pageStringSize.width, height: pageStringSize.height)
let paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle
paragraphStyle?.lineBreakMode = .byTruncatingTail
paragraphStyle?.alignment = .right
let attributes = [NSAttributedStringKey.font: theFont, .paragraphStyle: paragraphStyle as Any] as [NSAttributedStringKey : Any]
pageString.draw(in: stringRect, withAttributes: attributes)
}
The pdf generates and never ends even it went upto 2Gb file size. But not opening. Could you please advice where I am doing mistake to finish the pdf generation? Also after pdf creation, would like to send by mail
Thanks in advance
EDIT:
If I change the code to while done it stops but prints one page only. If I change to while !done it goes on generating pdf. Past two days searching and not end up with any solution. Also the pdf prints black only. I want to reflect the attributes to pdf. Any suggestions.
if (currentRange.location == CFAttributedStringGetLength(currentText)){
done = true
}
} while done
UIGraphicsEndPDFContext()
}
else {
print("Could not create the framesetter needed to lay out the atrributed string.")
}
}
else {
print("Could not create the attributed string for the framesetter")
}
}

How to create a PDF in Swift with Cocoa (Mac)

Xcode 7.3.2, Swift 2, Cocoa (Mac).
My app involves the user entering in some text, which can be exported to a PDF.
In the iOS version of my app, I can create the PDF relatively easily with the CoreText framework:
let html = "<font face=\'Futura\' color=\"SlateGray\"><h2>\(title)</h2></font><font face=\"Avenir\" color=\"SlateGray\"><h4>\(string)</h4></font>"
let fmt = UIMarkupTextPrintFormatter(markupText: html)
// 2. Assign print formatter to UIPrintPageRenderer
let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)
// 3. Assign paperRect and printableRect
let page = CGRect(x: 10, y: 10, width: 595.2, height: 841.8) // A4, 72 dpi, margin of 10 from top and left.
let printable = page.insetBy(dx: 0, dy: 0)
render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
// 4. Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}
UIGraphicsEndPDFContext();
// 5. Save PDF file
path = "\(NSTemporaryDirectory())\(title).pdf"
pdfData.write(toFile: path, atomically: true)
However, UIMarkupTextPrintFormatter, UIPrintPageRenderer, UIGraphicsBeginPDFContextToData, and UIGraphicsEndPDFContext all do not exist on OS X. How can I do the exact same thing as I am doing with this iOS code (create a basic PDF from some HTML and write it to a certain file path as a paginated PDF) with Mac and Cocoa?
EDIT: The answer to this question is here: Create a paginated PDF—Mac OS X.
Here is a function that will generate a PDF from pure HTML.
func makePDF(markup: String) {
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let printOpts: [NSPrintInfo.AttributeKey: Any] = [NSPrintInfo.AttributeKey.jobDisposition: NSPrintInfo.JobDisposition.save, NSPrintInfo.AttributeKey.jobSavingURL: directoryURL]
let printInfo = NSPrintInfo(dictionary: printOpts)
printInfo.horizontalPagination = NSPrintingPaginationMode.AutoPagination
printInfo.verticalPagination = NSPrintingPaginationMode.AutoPagination
printInfo.topMargin = 20.0
printInfo.leftMargin = 20.0
printInfo.rightMargin = 20.0
printInfo.bottomMargin = 20.0
let view = NSView(frame: NSRect(x: 0, y: 0, width: 570, height: 740))
if let htmlData = markup.dataUsingEncoding(NSUTF8StringEncoding) {
if let attrStr = NSAttributedString(HTML: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) {
let frameRect = NSRect(x: 0, y: 0, width: 570, height: 740)
let textField = NSTextField(frame: frameRect)
textField.attributedStringValue = attrStr
view.addSubview(textField)
let printOperation = NSPrintOperation(view: view, printInfo: printInfo)
printOperation.showsPrintPanel = false
printOperation.showsProgressPanel = false
printOperation.run()
}
}
}
What is happening:
Put the HTML into a NSAttributedString.
Render the NSAttributedString to a NSTextField.
Render the NSTextField to a NSView.
Create a NSPrintOperation with that NSView.
Set the printing parameters to save as a PDF.
Run the print operation (which actually opens a dialog to save the PDF)
Everyone is happy.
This is not a perfect solution. Note the hard coded integer values.

Adding an image to a PDF file in Swift

Hi i'm currently trying to get a image in a imageview added to my PDF file.
i already have text added to the PDF im just looking to get an image added in too.
Heres what i got so far
#IBAction func CreatePDF(sender: AnyObject) {
// 1. Create a print formatter
let html = "<b>Hello <i>World!</i></b>"
let fmt = UIMarkupTextPrintFormatter(markupText: html)
// 2. Assign print formatter to UIPrintPageRenderer
let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAtIndex: 0)
// 3. Assign paperRect and printableRect
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = CGRectInset(page, 0, 0)
render.setValue(NSValue(CGRect: page), forKey: "paperRect")
render.setValue(NSValue(CGRect: printable), forKey: "printableRect")
// 4. Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil)
for i in 1...render.numberOfPages() {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPageAtIndex(i - 1, inRect: bounds)
}
UIGraphicsEndPDFContext();
// 5. Save PDF file
let path = "\(NSTemporaryDirectory())file.pdf"
pdfData.writeToFile(path, atomically: true)
print("open \(path)") // command to open the generated file
}