Create paginated PDF-Mac OS X

I am creating a Mac application (in Swift 3 using Xcode 8, Beta 5) with which the user can take a long note and export it as a PDF.

To create this PDF, I use the Cocoa dataWithPDF: method with the following code:

 do { // define bounds of PDF as the note text view let rect: NSRect = self.noteTextView.bounds // create the file path for the PDF if let dir = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true).first { // add the note title to path let path = NSURL(fileURLWithPath: dir).appendingPathComponent("ExportedNote.pdf") // Create a PDF of the noteTextView and write it to the created filepath try self.noteTextView.dataWithPDF(inside: rect).write(to: path!) } else { print("Path format incorrect.") // never happens to me } } catch _ { print("something went wrong.") // never happens to me } 

This fully works, but there is one problem: PDF goes on only one page, which means that the page becomes very long when there is a lot of text in the note. How to make PDF files format on as many pages with letter size as needed, either when my application exports a PDF file, or immediately after?

+6
source share
1 answer

After several weeks of frustration, I came up with the final way to create paginated PDFs in the Swift application. And it's not as complicated as it seems (based on Swift 2 ):


Note Before you read more, you should know that I made a simple tool for Github, so you can do it in a few steps (and in Swift 3, which no longer needs Objective-C). Check it out here .


Step 1: Configure Sandboxing app correctly. Each submitted Mac application requires the Sandboxing app correctly, so it’s best to have 30 seconds to set it up right now. If it is not already enabled, go to the Target settings and then the Capabilities header. Turn on the Sandboxing app at the top. You should see a lot of helper options, enable any of these applications, and make sure User Selected File set to Read/Write .

Step 2: Add this function to your code that will create an invisible web view and add text to it.

 func createPDF(fromHTMLString: String) { self.webView.mainFrame.loadHTMLString(htmlString, baseURL: nil) self.delay(1) { MyCreatePDFFile(self.webView) } } 

Step 3: Create a delay() function so that Xcode can wait a second for the HTML to be loaded into the web view.

  func delay(delay:Double, closure:()->()) { dispatch_after( dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue(), closure) } 

(Note: for the version of this Swift 3 feature, go here .)

Step 4: Add our WebView declaration over the function added in step 2.

 var webView = WebView() 

Step 5: You may notice that we have not yet created the MyCreatePDFFile() function. It turns out there is no way to convert this WebView to PDF using Swift, so we have to turn to Objective-C (groan). Yes, you need to run several Objective-C in your Swift application.

Step 6: Create the .m file by going to File β†’ New β†’ File (or by pressing CMD + N ) by pressing Objective-C File . Name it CreatePDF.m and leave it as an empty file.

Step 7: When adding your .m file, you should be prompted to add the bridge header:

enter image description here

Click Yes .

If you did not see the invitation or accidentally deleted the bridge header, add a new .h file to the project and name it <#YourProjectName#>-Bridging-Header.h

In some situations, especially when working with ObjC infrastructures, you do not explicitly add the Objective-C class, and Xcode cannot find the linker. In this case, create your .h header title, named as above, then make sure you link its path in the settings of the target project as follows:

enter image description here

Step 8: Create the .h file by going to File β†’ New β†’ File (or by pressing CMD + N ) by pressing Header File . Name it CreatePDF.h .

Step 9: In your CreatePDF.h file CreatePDF.h add the following code that imports Cocoa and WebKit and sets up your PDF creation function:

 #ifndef CreatePDF_h #define CreatePDF_h #import <Cocoa/Cocoa.h> #import <WebKit/WebKit.h> @interface Thing : NSObject void MyCreatePDFFile(WebView *thewebview); @end #endif /* CreatePDF_h */ 

Step 10: Installation time for the .m file for the PDF creation function. Start by adding this to an empty .m file:

 #import "CreatePDF.h" #import <Cocoa/Cocoa.h> #import <WebKit/WebKit.h> @implementation Thing void MyCreatePDFFile(WebView *thewebview) { } @end 

Step 11: Now you can add the code to the MyCreatePDFFile(..) function. Here the function itself (this goes into the current empty void function):

 NSDictionary *printOpts = @{ NSPrintJobDisposition: NSPrintSaveJob // sets the print job to save the PDF instead of print it. }; NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printOpts]; [printInfo setPaperSize:NSMakeSize(595.22, 841.85)]; // sets the paper size to a nice size that works, you can mess around with it [printInfo setTopMargin:10.0]; [printInfo setLeftMargin:10.0]; [printInfo setRightMargin:10.0]; [printInfo setBottomMargin:10.0]; NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:[[[thewebview mainFrame] frameView] documentView] printInfo:printInfo]; // sets the print operation to printing the WebView as a document with the print info set above printOp.showsPrintPanel = NO; // skip the print question and go straight to saving pdf printOp.showsProgressPanel = NO; [printOp runOperation]; 

Step 12: Now add this to your bridge header so that your Swift file can see what Objective-C function you just created.

 #import "CreatePDF.h" 

Step 13: There should not be any errors from this code, but if there are comments about them below.

Step 14: To call the createPDF function from anywhere in your Swift file, follow these steps:

 createPDF("<h1>Title!</h1><p>\(textview.text!)</p>") 

The line you see inside createPDF is HTML and can be edited for any HTML. If you don't know HTML, you can see some of the very reasons here .

It should be! Now you can create paginated PDF files directly from Swift, in Cocoa / Mac applications.


Loans

Note. This answer has been tested to work with Xcode 7, Swift 2, and OS X El Captian. If you are having problems using Xcode 8 / Swift 3 / macOS Sierra, let me know.

+4
source

All Articles