├── .github └── FUNDING.yml ├── .gitignore ├── .hound.yml ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── CHANGELOG.md ├── Demo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Controllers │ ├── PDFOutputViewController.swift │ ├── PDFPreviewVC.swift │ ├── SampleTableViewController.swift │ ├── ViewController.swift │ └── WebViewController.swift ├── Info.plist ├── PDFViewController.storyboard ├── PDFViewController.swift ├── Views │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ └── SampleTableViewCell.swift └── sample_images │ ├── sample_0.jpg │ ├── sample_1.jpg │ ├── sample_2.jpg │ ├── sample_3.jpg │ ├── sample_4.jpg │ └── sample_5.jpg ├── LICENSE ├── PDFGenerator.podspec ├── PDFGenerator.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── PDFGenerator.xcscheme ├── PDFGenerator ├── DPIType.swift ├── FilePathConvertible.swift ├── Info.plist ├── PDFGenerateError.swift ├── PDFGenerator.h ├── PDFGenerator.swift ├── PDFPage.swift ├── PDFPageRenderable.swift ├── PDFPagedScrollViewConfiguration.swift └── PDFPassword.swift ├── PDFGeneratorTests ├── DPITests.swift ├── FilePathConvertibleTests.swift ├── Images │ └── test_image1.png ├── Info.plist ├── PDFGeneratorTests.swift └── PDFPasswordTests.swift ├── README.md └── codecov.yml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: sgr-ksmt 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | *.DS_Store 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | # Pods/ 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/screenshots 65 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | swift: 2 | config_file: .swiftlint.yml 3 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | opt_in_rules: 2 | - trailing_newline 3 | 4 | disabled_rules: 5 | - trailing_whitespace 6 | - type_name 7 | - force_cast 8 | - type_body_length 9 | - file_length 10 | - cyclomatic_complexity 11 | - valid_docs 12 | - missing_docs 13 | - opening_brace 14 | - line_length 15 | - force_try 16 | - identifier_name 17 | 18 | force_try: warning 19 | 20 | line_length: 150 21 | 22 | excluded: 23 | - Pods 24 | - Carthage 25 | - PDFGeneratorTests 26 | 27 | function_body_length: 28 | - 100 # warning 29 | - 300 # error 30 | 31 | function_parameter_count: 32 | - 5 #warning 33 | - 10 #error 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode10 3 | language: objective-c 4 | install: 5 | - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" 6 | before_script: 7 | - pod lib lint --allow-warnings 8 | - swiftlint --strict 9 | env: 10 | global: 11 | - DESTINATION="platform=iOS Simulator,name=iPhone 8" 12 | - FRAMEWORK_NAME=PDFGenerator 13 | script: 14 | - xcodebuild test -project PDFGenerator.xcodeproj -scheme PDFGenerator -destination "${DESTINATION}" 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #Change Log 2 | 3 | ## [v3.1.0] 4 | ### Updated 5 | - WKWebView + Xcode 11.2.1 Updates (#117) 6 | 7 | Special thanks!! **@rlester** 8 | 9 | ## [v3.0.0] 10 | 11 | . 12 | 13 | ## [v2.1](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/2.1) (2017/09/21) 14 | ### Updated 15 | - Update project to xcode9 and swift4 (#79) 16 | 17 | ### Fixed 18 | - Cleanup (#77) 19 | 20 | Special thanks!! **@wesbillman** , **@russellbstephens** 21 | 22 | 23 | ## [v2.0.1](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/2.0.1) (2016/09/19) 24 | Minor bug fix 25 | ### Fixed 26 | - Fix generate as Data issue using `PDFGenerator.generated(by:)` #54 27 | 28 | 29 | ## [v2.0.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/2.0.0) (2016/09/15) 30 | Major update :point_up::point_up: 31 | ### Improvement 32 | - Support Swift3.0 33 | 34 | 35 | ## [v1.4.2](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.4.0) (2016/09/08) 36 | ### Fixed 37 | - Fix minor bugs #40 38 | 39 | ## [v1.4.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.4.0) (2016/07/23) 40 | ### Added 41 | - FilePathConvertible : `outputPath` is allowed both `String` and `NSURL`. #38 42 | - CHANGELOG.md 43 | - codecov #37 44 | 45 | ### Updated 46 | - Add more UnitTest : codecov percentage increase to 92%. #39 47 | 48 | ## Swift3.0 support (beta) 49 | - compatible to Swift 3.0 #29 #31 #32 50 | 51 | ## [v1.3.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.3.0) (2016/07/12) 52 | ### Implemented 53 | - Password Protection #34 54 | 55 | 56 | ## [v1.2.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.2.0) (2016/06/22) 57 | ### Implemented 58 | - DPI suppoert #27 59 | 60 | ## [v1.1.4](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.1.4) (2016/06/22) 61 | ### Updated 62 | - Update for Xcode7.3(swift2.2) #21 63 | 64 | ## 1.1.3~1.1.1 65 | ### Fixed 66 | - Fix minor bugs #18, #13 67 | 68 | ## [v1.1.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.1.0) (2016/02/20) 69 | ### Added 70 | - support Binary,ImageRef render. #11 71 | 72 | ## [v1.0.0](https://github.com/sgr-ksmt/PDFGenerator/releases/tag/1.0.0) (2016/02/11) 73 | ### Stable Version Release!! 74 | - Support multiple pages. 75 | - Also generate PDF from imagePath that can load image with UIImage(contentsOfFile:) 76 | - Type safe. 77 | - Good memory management. 78 | - Generate PDF from mixed-pages. 79 | - If view is UIScrollView , drawn whole content. 80 | - Outputs as NSData or writes to Disk(in given file path) directly. 81 | - Corresponding to Error-Handling. Strange PDF has never been generated!! 82 | 83 | ## 0.2.0 ~ 0.1.0 84 | - beta release -------------------------------------------------------------------------------- /Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/04. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Demo/Controllers/PDFOutputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFOutputViewController.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 9/7/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import PDFGenerator 11 | 12 | class PDFOutputViewController: UIViewController { 13 | 14 | @IBOutlet fileprivate weak var scrollView: UIScrollView! 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | // Do any additional setup after loading the view. 19 | } 20 | 21 | override func didReceiveMemoryWarning() { 22 | super.didReceiveMemoryWarning() 23 | // Dispose of any resources that can be recreated. 24 | } 25 | 26 | @IBAction func render(_: UIButton) { 27 | let dst = NSHomeDirectory() + "/test.pdf" 28 | try! PDFGenerator.generate(self.scrollView, to: dst) 29 | openPDFViewer(dst) 30 | } 31 | 32 | fileprivate func openPDFViewer(_ pdfPath: String) { 33 | self.performSegue(withIdentifier: "PreviewVC", sender: pdfPath) 34 | } 35 | 36 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 37 | if let pdfPreviewVC = segue.destination as? PDFPreviewVC, let pdfPath = sender as? String { 38 | let url = URL(fileURLWithPath: pdfPath) 39 | pdfPreviewVC.setupWithURL(url) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Demo/Controllers/PDFPreviewVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPreviewVC.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/06. 6 | // 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | class PDFPreviewVC: UIViewController { 13 | 14 | @IBOutlet fileprivate weak var webView: WKWebView! 15 | var url: URL! 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | let req = NSMutableURLRequest(url: url) 19 | req.timeoutInterval = 60.0 20 | req.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData 21 | 22 | // webView.scalesPageToFit = true 23 | webView.load(req as URLRequest) 24 | } 25 | 26 | override func didReceiveMemoryWarning() { 27 | super.didReceiveMemoryWarning() 28 | } 29 | 30 | @IBAction fileprivate func close(_ sender: AnyObject!) { 31 | dismiss(animated: true, completion: nil) 32 | } 33 | 34 | func setupWithURL(_ url: URL) { 35 | self.url = url 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Demo/Controllers/SampleTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleTableViewController.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/27. 6 | // 7 | // 8 | 9 | import UIKit 10 | import PDFGenerator 11 | 12 | class SampleTableViewController: UITableViewController { 13 | @objc fileprivate func generatePDF() { 14 | do { 15 | let dst = NSHomeDirectory() + "/sample_tblview.pdf" 16 | try PDFGenerator.generate(self.tableView, to: dst) 17 | openPDFViewer(dst) 18 | } catch let error { 19 | print(error) 20 | } 21 | 22 | } 23 | 24 | fileprivate func openPDFViewer(_ pdfPath: String) { 25 | self.performSegue(withIdentifier: "PreviewVC", sender: pdfPath) 26 | } 27 | 28 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 29 | if let pdfPreviewVC = segue.destination as? PDFPreviewVC, let pdfPath = sender as? String { 30 | let url = URL(fileURLWithPath: pdfPath) 31 | pdfPreviewVC.setupWithURL(url) 32 | } 33 | } 34 | 35 | // MARK: - Table view data source 36 | 37 | override func numberOfSections(in tableView: UITableView) -> Int { 38 | return 3 39 | } 40 | 41 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 42 | return 10 43 | } 44 | 45 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 46 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SampleTableViewCell 47 | cell.leftLabel.text = "\((indexPath as NSIndexPath).section)-\((indexPath as NSIndexPath).row)cell" 48 | cell.rightLabel.text = "sample" 49 | return cell 50 | } 51 | 52 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 53 | return "section\(section)" 54 | } 55 | 56 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 57 | generatePDF() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Demo/Controllers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Demo 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/04. 6 | // 7 | // 8 | 9 | import UIKit 10 | import PDFGenerator 11 | 12 | class ViewController: UIViewController { 13 | 14 | fileprivate var outputAsData: Bool = false 15 | 16 | fileprivate func getImagePath(_ number: Int) -> String { 17 | return Bundle.main.path(forResource: "sample_\(number)", ofType: "jpg")! 18 | } 19 | 20 | fileprivate func getDestinationPath(_ number: Int) -> String { 21 | return NSHomeDirectory() + "/sample\(number).pdf" 22 | } 23 | 24 | @IBAction fileprivate func generateSamplePDFFromViews(_ sender: AnyObject?) { 25 | let v1 = UIScrollView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 26 | let v2 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200)) 27 | let v3 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200)) 28 | v1.backgroundColor = UIColor.red 29 | v1.contentSize = CGSize(width: 100, height: 100) 30 | v2.backgroundColor = UIColor.green 31 | v3.backgroundColor = UIColor.blue 32 | 33 | do { 34 | let dst = getDestinationPath(1) 35 | if outputAsData { 36 | let data = try PDFGenerator.generated(by: [v1, v2, v3]) 37 | try data.write(to: URL(fileURLWithPath: dst)) 38 | } else { 39 | try PDFGenerator.generate([v1, v2, v3], to: dst) 40 | } 41 | openPDFViewer(dst) 42 | } catch let e { 43 | print(e) 44 | } 45 | } 46 | 47 | @IBAction fileprivate func generateSamplePDFFromImages(_ sender: AnyObject?) { 48 | let dst = getDestinationPath(2) 49 | autoreleasepool { 50 | do { 51 | var images = [UIImage]() 52 | (0..<3).forEach { 53 | images.append(UIImage(contentsOfFile: getImagePath($0))!) 54 | } 55 | if outputAsData { 56 | let data = try PDFGenerator.generated(by: images) 57 | try data.write(to: URL(fileURLWithPath: dst)) 58 | } else { 59 | try PDFGenerator.generate(images, to: dst, dpi: .custom(144), password: "123456") 60 | } 61 | openPDFViewer(dst) 62 | } catch let e { 63 | print(e) 64 | } 65 | } 66 | } 67 | 68 | @IBAction fileprivate func generateSamplePDFFromImagePaths(_ sender: AnyObject?) { 69 | do { 70 | let dst = getDestinationPath(3) 71 | var imagePaths = [String]() 72 | (3..<6).forEach { 73 | imagePaths.append(getImagePath($0)) 74 | } 75 | if outputAsData { 76 | let data = try PDFGenerator.generated(by: imagePaths) 77 | try data.write(to: URL(fileURLWithPath: dst)) 78 | } else { 79 | try PDFGenerator.generate(imagePaths, to: dst) 80 | } 81 | openPDFViewer(dst) 82 | } catch let e { 83 | print(e) 84 | } 85 | } 86 | 87 | @IBAction fileprivate func generateSamplePDFFromPages(_ sender: AnyObject?) { 88 | let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 89 | v1.backgroundColor = UIColor.red 90 | let v2 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200)) 91 | v2.backgroundColor = UIColor.green 92 | 93 | let page1 = PDFPage.view(v1) 94 | let page2 = PDFPage.view(v2) 95 | let page3 = PDFPage.whitePage(CGSize(width: 200, height: 100)) 96 | let page4 = PDFPage.image(UIImage(contentsOfFile: getImagePath(1))!) 97 | let page5 = PDFPage.imagePath(getImagePath(2)) 98 | let pages = [page1, page2, page3, page4, page5] 99 | do { 100 | let dst = getDestinationPath(3) 101 | if outputAsData { 102 | let data = try PDFGenerator.generated(by: pages) 103 | try data.write(to: URL(fileURLWithPath: dst)) 104 | } else { 105 | try PDFGenerator.generate(pages, to: dst) 106 | } 107 | openPDFViewer(dst) 108 | } catch let e { 109 | print(e) 110 | } 111 | } 112 | 113 | fileprivate func openPDFViewer(_ pdfPath: String) { 114 | self.performSegue(withIdentifier: "PreviewVC", sender: pdfPath) 115 | } 116 | 117 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 118 | if let pdfPreviewVC = segue.destination as? PDFPreviewVC, let pdfPath = sender as? String { 119 | let url = URL(fileURLWithPath: pdfPath) 120 | pdfPreviewVC.setupWithURL(url) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Demo/Controllers/WebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewController.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/03/23. 6 | // 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import PDFGenerator 12 | 13 | class WebViewController: UIViewController { 14 | 15 | @IBOutlet fileprivate weak var webView: WKWebView! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | let req = NSMutableURLRequest(url: URL(string: "http://www.yahoo.co.jp")!, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 60) 20 | webView.load(req as URLRequest) 21 | } 22 | 23 | @IBAction func generatePDF() { 24 | do { 25 | let dst = NSHomeDirectory() + "/sample_tblview.pdf" 26 | try PDFGenerator.generate(webView, to: dst) 27 | openPDFViewer(dst) 28 | } catch let error { 29 | print(error) 30 | } 31 | 32 | } 33 | 34 | fileprivate func openPDFViewer(_ pdfPath: String) { 35 | self.performSegue(withIdentifier: "PreviewVC", sender: pdfPath) 36 | } 37 | 38 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 39 | if let pdfPreviewVC = segue.destination as? PDFPreviewVC, let pdfPath = sender as? String { 40 | let url = URL(fileURLWithPath: pdfPath) 41 | pdfPreviewVC.setupWithURL(url) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | NSAppTransportSecurity 40 | 41 | NSAllowsArbitraryLoads 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo/PDFViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Demo/PDFViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFViewController.swift 3 | // PDFMaker 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/05. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class PDFViewController: UIViewController { 12 | 13 | @IBOutlet private weak var webView: UIWebView! 14 | @IBOutlet private weak var closeButton: UIButton! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | } 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | } 23 | 24 | func setupWithURL(url: NSURL) { 25 | let req = NSURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 60) 26 | webView.loadRequest(req) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Demo/Views/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Demo/Views/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 48 | 55 | 62 | 69 | 76 | 83 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 213 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | -------------------------------------------------------------------------------- /Demo/Views/SampleTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleTableViewCell.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/27. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class SampleTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var leftLabel: UILabel! 14 | @IBOutlet weak var rightLabel: UILabel! 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | } 18 | 19 | override func setSelected(_ selected: Bool, animated: Bool) { 20 | super.setSelected(selected, animated: animated) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Demo/sample_images/sample_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_0.jpg -------------------------------------------------------------------------------- /Demo/sample_images/sample_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_1.jpg -------------------------------------------------------------------------------- /Demo/sample_images/sample_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_2.jpg -------------------------------------------------------------------------------- /Demo/sample_images/sample_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_3.jpg -------------------------------------------------------------------------------- /Demo/sample_images/sample_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_4.jpg -------------------------------------------------------------------------------- /Demo/sample_images/sample_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/Demo/sample_images/sample_5.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Suguru Kishimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PDFGenerator.podspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Pod::Spec.new do |s| 4 | s.name = 'PDFGenerator' 5 | s.version = '3.1.0' 6 | s.summary = 'A simple PDF generator.' 7 | s.homepage = 'https://github.com/sgr-ksmt/PDFGenerator' 8 | # s.screenshots = "" 9 | s.license = 'MIT' 10 | s.author = { 'Suguru Kishimoto' => 'melodydance.k.s@gmail.com' } 11 | s.source = { git: 'https://github.com/sgr-ksmt/PDFGenerator.git', tag: s.version.to_s } 12 | s.platform = :ios, '8.0' 13 | s.swift_version = '5.1' 14 | s.source_files = 'PDFGenerator/**/*.{swift,h}' 15 | s.frameworks = 'WebKit' 16 | end 17 | -------------------------------------------------------------------------------- /PDFGenerator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4D2EB59B1C6A47CD007A87E7 /* PDFPageRenderable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D2EB59A1C6A47CD007A87E7 /* PDFPageRenderable.swift */; }; 11 | 4D4F97931C63882F00108BEB /* PDFGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D4F97921C63882F00108BEB /* PDFGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 4D4F979A1C63882F00108BEB /* PDFGenerator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D4F978F1C63882F00108BEB /* PDFGenerator.framework */; }; 13 | 4D4F979F1C63882F00108BEB /* PDFGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D4F979E1C63882F00108BEB /* PDFGeneratorTests.swift */; }; 14 | 4D4F97B01C6388AA00108BEB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D4F97AF1C6388AA00108BEB /* AppDelegate.swift */; }; 15 | 4D4F97B21C6388AA00108BEB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D4F97B11C6388AA00108BEB /* ViewController.swift */; }; 16 | 4D4F97B51C6388AA00108BEB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97B31C6388AA00108BEB /* Main.storyboard */; }; 17 | 4D4F97B71C6388AA00108BEB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97B61C6388AA00108BEB /* Assets.xcassets */; }; 18 | 4D4F97BA1C6388AA00108BEB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97B81C6388AA00108BEB /* LaunchScreen.storyboard */; }; 19 | 4D4F97C01C638AE000108BEB /* PDFGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D4F97BF1C638AE000108BEB /* PDFGenerator.swift */; }; 20 | 4D4F97CE1C639CCB00108BEB /* sample_0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97C81C639CCB00108BEB /* sample_0.jpg */; }; 21 | 4D4F97CF1C639CCB00108BEB /* sample_1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97C91C639CCB00108BEB /* sample_1.jpg */; }; 22 | 4D4F97D01C639CCB00108BEB /* sample_2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97CA1C639CCB00108BEB /* sample_2.jpg */; }; 23 | 4D4F97D11C639CCB00108BEB /* sample_3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97CB1C639CCB00108BEB /* sample_3.jpg */; }; 24 | 4D4F97D21C639CCB00108BEB /* sample_4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97CC1C639CCB00108BEB /* sample_4.jpg */; }; 25 | 4D4F97D31C639CCB00108BEB /* sample_5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4D4F97CD1C639CCB00108BEB /* sample_5.jpg */; }; 26 | 4D54FFEC1C65CFFD000A9036 /* PDFPreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D54FFEB1C65CFFD000A9036 /* PDFPreviewVC.swift */; }; 27 | 4D7462BD1D43AEB50044FBBB /* PDFPasswordTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D7462BC1D43AEB50044FBBB /* PDFPasswordTests.swift */; }; 28 | 4D7A9A021D805C4D00475C2E /* PDFOutputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D7A9A011D805C4D00475C2E /* PDFOutputViewController.swift */; }; 29 | 4D85127A1D191BA4009EE05C /* PDFPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D8512791D191BA4009EE05C /* PDFPage.swift */; }; 30 | 4D8A9A6C1D2FB65600007E4B /* PDFPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D8A9A6B1D2FB65600007E4B /* PDFPassword.swift */; }; 31 | 4D900B931C8174B8004034C2 /* SampleTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D900B921C8174B8004034C2 /* SampleTableViewController.swift */; }; 32 | 4D900B951C817582004034C2 /* SampleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D900B941C817582004034C2 /* SampleTableViewCell.swift */; }; 33 | 4DA8AC331D4350DE00F04F9D /* FilePathConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA8AC321D4350DE00F04F9D /* FilePathConvertible.swift */; }; 34 | 4DA8AC351D43527D00F04F9D /* FilePathConvertibleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA8AC341D43527D00F04F9D /* FilePathConvertibleTests.swift */; }; 35 | 4DC0A7AE1C6C583000E843C8 /* test_image1.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DC0A7AD1C6C583000E843C8 /* test_image1.png */; }; 36 | 4DD477E01D193ECB00D83D50 /* PDFGenerateError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD477DF1D193ECB00D83D50 /* PDFGenerateError.swift */; }; 37 | 4DD477E21D19449700D83D50 /* DPIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD477E11D19449700D83D50 /* DPIType.swift */; }; 38 | 4DDF14FE1D43A931004ADB0B /* DPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DDF14FD1D43A931004ADB0B /* DPITests.swift */; }; 39 | 4DF6DEB61CA2590900C97F30 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DF6DEB51CA2590900C97F30 /* WebViewController.swift */; }; 40 | B45907781E44CC100063E596 /* PDFGenerator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D4F978F1C63882F00108BEB /* PDFGenerator.framework */; }; 41 | D47C1DBD21145ADC00AF7BAF /* PDFPagedScrollViewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47C1DBC21145ADC00AF7BAF /* PDFPagedScrollViewConfiguration.swift */; }; 42 | /* End PBXBuildFile section */ 43 | 44 | /* Begin PBXContainerItemProxy section */ 45 | 4D4F979B1C63882F00108BEB /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 4D4F97861C63882F00108BEB /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = 4D4F978E1C63882F00108BEB; 50 | remoteInfo = PDFMaker; 51 | }; 52 | B45907791E44CC140063E596 /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 4D4F97861C63882F00108BEB /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 4D4F978E1C63882F00108BEB; 57 | remoteInfo = PDFGenerator; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 1618D283215B4CD6005A2CE9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 4D2EB59A1C6A47CD007A87E7 /* PDFPageRenderable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPageRenderable.swift; sourceTree = ""; }; 64 | 4D4F978F1C63882F00108BEB /* PDFGenerator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PDFGenerator.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 4D4F97921C63882F00108BEB /* PDFGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PDFGenerator.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 66 | 4D4F97991C63882F00108BEB /* PDFGeneratorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PDFGeneratorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 4D4F979E1C63882F00108BEB /* PDFGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PDFGeneratorTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 68 | 4D4F97A01C63882F00108BEB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 4D4F97AD1C6388AA00108BEB /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 4D4F97AF1C6388AA00108BEB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 71 | 4D4F97B11C6388AA00108BEB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 72 | 4D4F97B41C6388AA00108BEB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 73 | 4D4F97B61C6388AA00108BEB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 74 | 4D4F97B91C6388AA00108BEB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 75 | 4D4F97BB1C6388AA00108BEB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | 4D4F97BF1C638AE000108BEB /* PDFGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PDFGenerator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 77 | 4D4F97C81C639CCB00108BEB /* sample_0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_0.jpg; sourceTree = ""; }; 78 | 4D4F97C91C639CCB00108BEB /* sample_1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_1.jpg; sourceTree = ""; }; 79 | 4D4F97CA1C639CCB00108BEB /* sample_2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_2.jpg; sourceTree = ""; }; 80 | 4D4F97CB1C639CCB00108BEB /* sample_3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_3.jpg; sourceTree = ""; }; 81 | 4D4F97CC1C639CCB00108BEB /* sample_4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_4.jpg; sourceTree = ""; }; 82 | 4D4F97CD1C639CCB00108BEB /* sample_5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample_5.jpg; sourceTree = ""; }; 83 | 4D54FFEB1C65CFFD000A9036 /* PDFPreviewVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PDFPreviewVC.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 84 | 4D7462BC1D43AEB50044FBBB /* PDFPasswordTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPasswordTests.swift; sourceTree = ""; }; 85 | 4D7A9A011D805C4D00475C2E /* PDFOutputViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFOutputViewController.swift; sourceTree = ""; }; 86 | 4D8512791D191BA4009EE05C /* PDFPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPage.swift; sourceTree = ""; }; 87 | 4D8A9A6B1D2FB65600007E4B /* PDFPassword.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPassword.swift; sourceTree = ""; }; 88 | 4D900B921C8174B8004034C2 /* SampleTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleTableViewController.swift; sourceTree = ""; }; 89 | 4D900B941C817582004034C2 /* SampleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleTableViewCell.swift; sourceTree = ""; }; 90 | 4DA8AC321D4350DE00F04F9D /* FilePathConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilePathConvertible.swift; sourceTree = ""; }; 91 | 4DA8AC341D43527D00F04F9D /* FilePathConvertibleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilePathConvertibleTests.swift; sourceTree = ""; }; 92 | 4DC0A7AD1C6C583000E843C8 /* test_image1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image1.png; sourceTree = ""; }; 93 | 4DD477DF1D193ECB00D83D50 /* PDFGenerateError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFGenerateError.swift; sourceTree = ""; }; 94 | 4DD477E11D19449700D83D50 /* DPIType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPIType.swift; sourceTree = ""; }; 95 | 4DDF14FD1D43A931004ADB0B /* DPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPITests.swift; sourceTree = ""; }; 96 | 4DF6DEB51CA2590900C97F30 /* WebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = ""; }; 97 | D47C1DBC21145ADC00AF7BAF /* PDFPagedScrollViewConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPagedScrollViewConfiguration.swift; sourceTree = ""; }; 98 | /* End PBXFileReference section */ 99 | 100 | /* Begin PBXFrameworksBuildPhase section */ 101 | 4D4F978B1C63882F00108BEB /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | 4D4F97961C63882F00108BEB /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | 4D4F979A1C63882F00108BEB /* PDFGenerator.framework in Frameworks */, 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | 4D4F97AA1C6388AA00108BEB /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | B45907781E44CC100063E596 /* PDFGenerator.framework in Frameworks */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXFrameworksBuildPhase section */ 125 | 126 | /* Begin PBXGroup section */ 127 | 148C591F237A202B001E239F /* Controllers */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 4D4F97B11C6388AA00108BEB /* ViewController.swift */, 131 | 4D54FFEB1C65CFFD000A9036 /* PDFPreviewVC.swift */, 132 | 4D900B921C8174B8004034C2 /* SampleTableViewController.swift */, 133 | 4DF6DEB51CA2590900C97F30 /* WebViewController.swift */, 134 | 4D7A9A011D805C4D00475C2E /* PDFOutputViewController.swift */, 135 | ); 136 | path = Controllers; 137 | sourceTree = ""; 138 | }; 139 | 148C5920237A2030001E239F /* Views */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 4D900B941C817582004034C2 /* SampleTableViewCell.swift */, 143 | 4D4F97B81C6388AA00108BEB /* LaunchScreen.storyboard */, 144 | 4D4F97B31C6388AA00108BEB /* Main.storyboard */, 145 | ); 146 | path = Views; 147 | sourceTree = ""; 148 | }; 149 | 4D4F97851C63882F00108BEB = { 150 | isa = PBXGroup; 151 | children = ( 152 | 4D4F97911C63882F00108BEB /* PDFGenerator */, 153 | 4D4F979D1C63882F00108BEB /* PDFGeneratorTests */, 154 | 4D4F97AE1C6388AA00108BEB /* Demo */, 155 | 4D4F97901C63882F00108BEB /* Products */, 156 | ); 157 | sourceTree = ""; 158 | }; 159 | 4D4F97901C63882F00108BEB /* Products */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 4D4F978F1C63882F00108BEB /* PDFGenerator.framework */, 163 | 4D4F97991C63882F00108BEB /* PDFGeneratorTests.xctest */, 164 | 4D4F97AD1C6388AA00108BEB /* Demo.app */, 165 | ); 166 | name = Products; 167 | sourceTree = ""; 168 | }; 169 | 4D4F97911C63882F00108BEB /* PDFGenerator */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 4DD477E11D19449700D83D50 /* DPIType.swift */, 173 | 4DA8AC321D4350DE00F04F9D /* FilePathConvertible.swift */, 174 | 1618D283215B4CD6005A2CE9 /* Info.plist */, 175 | 4DD477DF1D193ECB00D83D50 /* PDFGenerateError.swift */, 176 | 4D4F97921C63882F00108BEB /* PDFGenerator.h */, 177 | 4D4F97BF1C638AE000108BEB /* PDFGenerator.swift */, 178 | 4D8512791D191BA4009EE05C /* PDFPage.swift */, 179 | D47C1DBC21145ADC00AF7BAF /* PDFPagedScrollViewConfiguration.swift */, 180 | 4D2EB59A1C6A47CD007A87E7 /* PDFPageRenderable.swift */, 181 | 4D8A9A6B1D2FB65600007E4B /* PDFPassword.swift */, 182 | ); 183 | path = PDFGenerator; 184 | sourceTree = ""; 185 | }; 186 | 4D4F979D1C63882F00108BEB /* PDFGeneratorTests */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 4DC0A7AC1C6C583000E843C8 /* Images */, 190 | 4D4F979E1C63882F00108BEB /* PDFGeneratorTests.swift */, 191 | 4D4F97A01C63882F00108BEB /* Info.plist */, 192 | 4DA8AC341D43527D00F04F9D /* FilePathConvertibleTests.swift */, 193 | 4DDF14FD1D43A931004ADB0B /* DPITests.swift */, 194 | 4D7462BC1D43AEB50044FBBB /* PDFPasswordTests.swift */, 195 | ); 196 | path = PDFGeneratorTests; 197 | sourceTree = ""; 198 | }; 199 | 4D4F97AE1C6388AA00108BEB /* Demo */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 148C5920237A2030001E239F /* Views */, 203 | 148C591F237A202B001E239F /* Controllers */, 204 | 4D4F97C11C63966D00108BEB /* sample_images */, 205 | 4D4F97AF1C6388AA00108BEB /* AppDelegate.swift */, 206 | 4D4F97B61C6388AA00108BEB /* Assets.xcassets */, 207 | 4D4F97BB1C6388AA00108BEB /* Info.plist */, 208 | ); 209 | path = Demo; 210 | sourceTree = ""; 211 | }; 212 | 4D4F97C11C63966D00108BEB /* sample_images */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 4D4F97C81C639CCB00108BEB /* sample_0.jpg */, 216 | 4D4F97C91C639CCB00108BEB /* sample_1.jpg */, 217 | 4D4F97CA1C639CCB00108BEB /* sample_2.jpg */, 218 | 4D4F97CB1C639CCB00108BEB /* sample_3.jpg */, 219 | 4D4F97CC1C639CCB00108BEB /* sample_4.jpg */, 220 | 4D4F97CD1C639CCB00108BEB /* sample_5.jpg */, 221 | ); 222 | path = sample_images; 223 | sourceTree = ""; 224 | }; 225 | 4DC0A7AC1C6C583000E843C8 /* Images */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | 4DC0A7AD1C6C583000E843C8 /* test_image1.png */, 229 | ); 230 | path = Images; 231 | sourceTree = ""; 232 | }; 233 | /* End PBXGroup section */ 234 | 235 | /* Begin PBXHeadersBuildPhase section */ 236 | 4D4F978C1C63882F00108BEB /* Headers */ = { 237 | isa = PBXHeadersBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 4D4F97931C63882F00108BEB /* PDFGenerator.h in Headers */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXHeadersBuildPhase section */ 245 | 246 | /* Begin PBXNativeTarget section */ 247 | 4D4F978E1C63882F00108BEB /* PDFGenerator */ = { 248 | isa = PBXNativeTarget; 249 | buildConfigurationList = 4D4F97A31C63882F00108BEB /* Build configuration list for PBXNativeTarget "PDFGenerator" */; 250 | buildPhases = ( 251 | 4DD477E31D1A354F00D83D50 /* swiftlint */, 252 | 4D4F978A1C63882F00108BEB /* Sources */, 253 | 4D4F978B1C63882F00108BEB /* Frameworks */, 254 | 4D4F978C1C63882F00108BEB /* Headers */, 255 | 4D4F978D1C63882F00108BEB /* Resources */, 256 | ); 257 | buildRules = ( 258 | ); 259 | dependencies = ( 260 | ); 261 | name = PDFGenerator; 262 | productName = PDFMaker; 263 | productReference = 4D4F978F1C63882F00108BEB /* PDFGenerator.framework */; 264 | productType = "com.apple.product-type.framework"; 265 | }; 266 | 4D4F97981C63882F00108BEB /* PDFGeneratorTests */ = { 267 | isa = PBXNativeTarget; 268 | buildConfigurationList = 4D4F97A61C63882F00108BEB /* Build configuration list for PBXNativeTarget "PDFGeneratorTests" */; 269 | buildPhases = ( 270 | 4D4F97951C63882F00108BEB /* Sources */, 271 | 4D4F97961C63882F00108BEB /* Frameworks */, 272 | 4D4F97971C63882F00108BEB /* Resources */, 273 | ); 274 | buildRules = ( 275 | ); 276 | dependencies = ( 277 | 4D4F979C1C63882F00108BEB /* PBXTargetDependency */, 278 | ); 279 | name = PDFGeneratorTests; 280 | productName = PDFMakerTests; 281 | productReference = 4D4F97991C63882F00108BEB /* PDFGeneratorTests.xctest */; 282 | productType = "com.apple.product-type.bundle.unit-test"; 283 | }; 284 | 4D4F97AC1C6388AA00108BEB /* Demo */ = { 285 | isa = PBXNativeTarget; 286 | buildConfigurationList = 4D4F97BE1C6388AA00108BEB /* Build configuration list for PBXNativeTarget "Demo" */; 287 | buildPhases = ( 288 | 4D4F97A91C6388AA00108BEB /* Sources */, 289 | 4D4F97AA1C6388AA00108BEB /* Frameworks */, 290 | 4D4F97AB1C6388AA00108BEB /* Resources */, 291 | ); 292 | buildRules = ( 293 | ); 294 | dependencies = ( 295 | B459077A1E44CC140063E596 /* PBXTargetDependency */, 296 | ); 297 | name = Demo; 298 | productName = Demo; 299 | productReference = 4D4F97AD1C6388AA00108BEB /* Demo.app */; 300 | productType = "com.apple.product-type.application"; 301 | }; 302 | /* End PBXNativeTarget section */ 303 | 304 | /* Begin PBXProject section */ 305 | 4D4F97861C63882F00108BEB /* Project object */ = { 306 | isa = PBXProject; 307 | attributes = { 308 | LastSwiftUpdateCheck = 0720; 309 | LastUpgradeCheck = 1000; 310 | TargetAttributes = { 311 | 4D4F978E1C63882F00108BEB = { 312 | CreatedOnToolsVersion = 7.2; 313 | LastSwiftMigration = 0900; 314 | }; 315 | 4D4F97981C63882F00108BEB = { 316 | CreatedOnToolsVersion = 7.2; 317 | LastSwiftMigration = 1000; 318 | }; 319 | 4D4F97AC1C6388AA00108BEB = { 320 | CreatedOnToolsVersion = 7.2; 321 | LastSwiftMigration = 0800; 322 | }; 323 | }; 324 | }; 325 | buildConfigurationList = 4D4F97891C63882F00108BEB /* Build configuration list for PBXProject "PDFGenerator" */; 326 | compatibilityVersion = "Xcode 3.2"; 327 | developmentRegion = en; 328 | hasScannedForEncodings = 0; 329 | knownRegions = ( 330 | en, 331 | Base, 332 | ); 333 | mainGroup = 4D4F97851C63882F00108BEB; 334 | productRefGroup = 4D4F97901C63882F00108BEB /* Products */; 335 | projectDirPath = ""; 336 | projectRoot = ""; 337 | targets = ( 338 | 4D4F978E1C63882F00108BEB /* PDFGenerator */, 339 | 4D4F97981C63882F00108BEB /* PDFGeneratorTests */, 340 | 4D4F97AC1C6388AA00108BEB /* Demo */, 341 | ); 342 | }; 343 | /* End PBXProject section */ 344 | 345 | /* Begin PBXResourcesBuildPhase section */ 346 | 4D4F978D1C63882F00108BEB /* Resources */ = { 347 | isa = PBXResourcesBuildPhase; 348 | buildActionMask = 2147483647; 349 | files = ( 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | 4D4F97971C63882F00108BEB /* Resources */ = { 354 | isa = PBXResourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | 4DC0A7AE1C6C583000E843C8 /* test_image1.png in Resources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | 4D4F97AB1C6388AA00108BEB /* Resources */ = { 362 | isa = PBXResourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 4D4F97D31C639CCB00108BEB /* sample_5.jpg in Resources */, 366 | 4D4F97BA1C6388AA00108BEB /* LaunchScreen.storyboard in Resources */, 367 | 4D4F97CE1C639CCB00108BEB /* sample_0.jpg in Resources */, 368 | 4D4F97B71C6388AA00108BEB /* Assets.xcassets in Resources */, 369 | 4D4F97CF1C639CCB00108BEB /* sample_1.jpg in Resources */, 370 | 4D4F97B51C6388AA00108BEB /* Main.storyboard in Resources */, 371 | 4D4F97D01C639CCB00108BEB /* sample_2.jpg in Resources */, 372 | 4D4F97D21C639CCB00108BEB /* sample_4.jpg in Resources */, 373 | 4D4F97D11C639CCB00108BEB /* sample_3.jpg in Resources */, 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | /* End PBXResourcesBuildPhase section */ 378 | 379 | /* Begin PBXShellScriptBuildPhase section */ 380 | 4DD477E31D1A354F00D83D50 /* swiftlint */ = { 381 | isa = PBXShellScriptBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | ); 385 | inputPaths = ( 386 | ); 387 | name = swiftlint; 388 | outputPaths = ( 389 | ); 390 | runOnlyForDeploymentPostprocessing = 0; 391 | shellPath = /bin/sh; 392 | shellScript = "if which swiftlint >/dev/null && [ -z ${CI+x} ]; then\n swiftlint\nfi"; 393 | }; 394 | /* End PBXShellScriptBuildPhase section */ 395 | 396 | /* Begin PBXSourcesBuildPhase section */ 397 | 4D4F978A1C63882F00108BEB /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | 4DD477E01D193ECB00D83D50 /* PDFGenerateError.swift in Sources */, 402 | 4D4F97C01C638AE000108BEB /* PDFGenerator.swift in Sources */, 403 | 4D2EB59B1C6A47CD007A87E7 /* PDFPageRenderable.swift in Sources */, 404 | 4D85127A1D191BA4009EE05C /* PDFPage.swift in Sources */, 405 | 4D8A9A6C1D2FB65600007E4B /* PDFPassword.swift in Sources */, 406 | D47C1DBD21145ADC00AF7BAF /* PDFPagedScrollViewConfiguration.swift in Sources */, 407 | 4DA8AC331D4350DE00F04F9D /* FilePathConvertible.swift in Sources */, 408 | 4DD477E21D19449700D83D50 /* DPIType.swift in Sources */, 409 | ); 410 | runOnlyForDeploymentPostprocessing = 0; 411 | }; 412 | 4D4F97951C63882F00108BEB /* Sources */ = { 413 | isa = PBXSourcesBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | 4DA8AC351D43527D00F04F9D /* FilePathConvertibleTests.swift in Sources */, 417 | 4D7462BD1D43AEB50044FBBB /* PDFPasswordTests.swift in Sources */, 418 | 4DDF14FE1D43A931004ADB0B /* DPITests.swift in Sources */, 419 | 4D4F979F1C63882F00108BEB /* PDFGeneratorTests.swift in Sources */, 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | }; 423 | 4D4F97A91C6388AA00108BEB /* Sources */ = { 424 | isa = PBXSourcesBuildPhase; 425 | buildActionMask = 2147483647; 426 | files = ( 427 | 4D7A9A021D805C4D00475C2E /* PDFOutputViewController.swift in Sources */, 428 | 4D4F97B21C6388AA00108BEB /* ViewController.swift in Sources */, 429 | 4DF6DEB61CA2590900C97F30 /* WebViewController.swift in Sources */, 430 | 4D54FFEC1C65CFFD000A9036 /* PDFPreviewVC.swift in Sources */, 431 | 4D900B951C817582004034C2 /* SampleTableViewCell.swift in Sources */, 432 | 4D4F97B01C6388AA00108BEB /* AppDelegate.swift in Sources */, 433 | 4D900B931C8174B8004034C2 /* SampleTableViewController.swift in Sources */, 434 | ); 435 | runOnlyForDeploymentPostprocessing = 0; 436 | }; 437 | /* End PBXSourcesBuildPhase section */ 438 | 439 | /* Begin PBXTargetDependency section */ 440 | 4D4F979C1C63882F00108BEB /* PBXTargetDependency */ = { 441 | isa = PBXTargetDependency; 442 | target = 4D4F978E1C63882F00108BEB /* PDFGenerator */; 443 | targetProxy = 4D4F979B1C63882F00108BEB /* PBXContainerItemProxy */; 444 | }; 445 | B459077A1E44CC140063E596 /* PBXTargetDependency */ = { 446 | isa = PBXTargetDependency; 447 | target = 4D4F978E1C63882F00108BEB /* PDFGenerator */; 448 | targetProxy = B45907791E44CC140063E596 /* PBXContainerItemProxy */; 449 | }; 450 | /* End PBXTargetDependency section */ 451 | 452 | /* Begin PBXVariantGroup section */ 453 | 4D4F97B31C6388AA00108BEB /* Main.storyboard */ = { 454 | isa = PBXVariantGroup; 455 | children = ( 456 | 4D4F97B41C6388AA00108BEB /* Base */, 457 | ); 458 | name = Main.storyboard; 459 | sourceTree = ""; 460 | }; 461 | 4D4F97B81C6388AA00108BEB /* LaunchScreen.storyboard */ = { 462 | isa = PBXVariantGroup; 463 | children = ( 464 | 4D4F97B91C6388AA00108BEB /* Base */, 465 | ); 466 | name = LaunchScreen.storyboard; 467 | sourceTree = ""; 468 | }; 469 | /* End PBXVariantGroup section */ 470 | 471 | /* Begin XCBuildConfiguration section */ 472 | 4D4F97A11C63882F00108BEB /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ALWAYS_SEARCH_USER_PATHS = NO; 476 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 477 | CLANG_CXX_LIBRARY = "libc++"; 478 | CLANG_ENABLE_MODULES = YES; 479 | CLANG_ENABLE_OBJC_ARC = YES; 480 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 481 | CLANG_WARN_BOOL_CONVERSION = YES; 482 | CLANG_WARN_COMMA = YES; 483 | CLANG_WARN_CONSTANT_CONVERSION = YES; 484 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 485 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 486 | CLANG_WARN_EMPTY_BODY = YES; 487 | CLANG_WARN_ENUM_CONVERSION = YES; 488 | CLANG_WARN_INFINITE_RECURSION = YES; 489 | CLANG_WARN_INT_CONVERSION = YES; 490 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 491 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 492 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 493 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 494 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 495 | CLANG_WARN_STRICT_PROTOTYPES = YES; 496 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 497 | CLANG_WARN_UNREACHABLE_CODE = YES; 498 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 499 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 500 | COPY_PHASE_STRIP = NO; 501 | CURRENT_PROJECT_VERSION = 1; 502 | DEBUG_INFORMATION_FORMAT = dwarf; 503 | ENABLE_STRICT_OBJC_MSGSEND = YES; 504 | ENABLE_TESTABILITY = YES; 505 | GCC_C_LANGUAGE_STANDARD = gnu99; 506 | GCC_DYNAMIC_NO_PIC = NO; 507 | GCC_NO_COMMON_BLOCKS = YES; 508 | GCC_OPTIMIZATION_LEVEL = 0; 509 | GCC_PREPROCESSOR_DEFINITIONS = ( 510 | "DEBUG=1", 511 | "$(inherited)", 512 | ); 513 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 514 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 515 | GCC_WARN_UNDECLARED_SELECTOR = YES; 516 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 517 | GCC_WARN_UNUSED_FUNCTION = YES; 518 | GCC_WARN_UNUSED_VARIABLE = YES; 519 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 520 | MTL_ENABLE_DEBUG_INFO = YES; 521 | ONLY_ACTIVE_ARCH = YES; 522 | SDKROOT = iphoneos; 523 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 524 | SWIFT_VERSION = 3.0; 525 | TARGETED_DEVICE_FAMILY = "1,2"; 526 | VERSIONING_SYSTEM = "apple-generic"; 527 | VERSION_INFO_PREFIX = ""; 528 | }; 529 | name = Debug; 530 | }; 531 | 4D4F97A21C63882F00108BEB /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | ALWAYS_SEARCH_USER_PATHS = NO; 535 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 536 | CLANG_CXX_LIBRARY = "libc++"; 537 | CLANG_ENABLE_MODULES = YES; 538 | CLANG_ENABLE_OBJC_ARC = YES; 539 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 540 | CLANG_WARN_BOOL_CONVERSION = YES; 541 | CLANG_WARN_COMMA = YES; 542 | CLANG_WARN_CONSTANT_CONVERSION = YES; 543 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 544 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 545 | CLANG_WARN_EMPTY_BODY = YES; 546 | CLANG_WARN_ENUM_CONVERSION = YES; 547 | CLANG_WARN_INFINITE_RECURSION = YES; 548 | CLANG_WARN_INT_CONVERSION = YES; 549 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 550 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 551 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 552 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 553 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 554 | CLANG_WARN_STRICT_PROTOTYPES = YES; 555 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 556 | CLANG_WARN_UNREACHABLE_CODE = YES; 557 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 558 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 559 | COPY_PHASE_STRIP = NO; 560 | CURRENT_PROJECT_VERSION = 1; 561 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 562 | ENABLE_NS_ASSERTIONS = NO; 563 | ENABLE_STRICT_OBJC_MSGSEND = YES; 564 | GCC_C_LANGUAGE_STANDARD = gnu99; 565 | GCC_NO_COMMON_BLOCKS = YES; 566 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 567 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 568 | GCC_WARN_UNDECLARED_SELECTOR = YES; 569 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 570 | GCC_WARN_UNUSED_FUNCTION = YES; 571 | GCC_WARN_UNUSED_VARIABLE = YES; 572 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 573 | MTL_ENABLE_DEBUG_INFO = NO; 574 | SDKROOT = iphoneos; 575 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 576 | SWIFT_VERSION = 3.0; 577 | TARGETED_DEVICE_FAMILY = "1,2"; 578 | VALIDATE_PRODUCT = YES; 579 | VERSIONING_SYSTEM = "apple-generic"; 580 | VERSION_INFO_PREFIX = ""; 581 | }; 582 | name = Release; 583 | }; 584 | 4D4F97A41C63882F00108BEB /* Debug */ = { 585 | isa = XCBuildConfiguration; 586 | buildSettings = { 587 | CLANG_ENABLE_MODULES = YES; 588 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 589 | DEFINES_MODULE = YES; 590 | DYLIB_COMPATIBILITY_VERSION = 1; 591 | DYLIB_CURRENT_VERSION = 1; 592 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 593 | INFOPLIST_FILE = PDFGenerator/Info.plist; 594 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 595 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 596 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.PDFGenerator"; 597 | PRODUCT_NAME = "$(TARGET_NAME)"; 598 | SKIP_INSTALL = YES; 599 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 600 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 601 | SWIFT_VERSION = 5.0; 602 | }; 603 | name = Debug; 604 | }; 605 | 4D4F97A51C63882F00108BEB /* Release */ = { 606 | isa = XCBuildConfiguration; 607 | buildSettings = { 608 | CLANG_ENABLE_MODULES = YES; 609 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 610 | DEFINES_MODULE = YES; 611 | DYLIB_COMPATIBILITY_VERSION = 1; 612 | DYLIB_CURRENT_VERSION = 1; 613 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 614 | INFOPLIST_FILE = PDFGenerator/Info.plist; 615 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 616 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 617 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.PDFGenerator"; 618 | PRODUCT_NAME = "$(TARGET_NAME)"; 619 | SKIP_INSTALL = YES; 620 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 621 | SWIFT_VERSION = 5.0; 622 | }; 623 | name = Release; 624 | }; 625 | 4D4F97A71C63882F00108BEB /* Debug */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | INFOPLIST_FILE = PDFGeneratorTests/Info.plist; 629 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 630 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.PDFGeneratorTests"; 631 | PRODUCT_NAME = "$(TARGET_NAME)"; 632 | SWIFT_VERSION = 5.0; 633 | }; 634 | name = Debug; 635 | }; 636 | 4D4F97A81C63882F00108BEB /* Release */ = { 637 | isa = XCBuildConfiguration; 638 | buildSettings = { 639 | INFOPLIST_FILE = PDFGeneratorTests/Info.plist; 640 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 641 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.PDFGeneratorTests"; 642 | PRODUCT_NAME = "$(TARGET_NAME)"; 643 | SWIFT_VERSION = 5.0; 644 | }; 645 | name = Release; 646 | }; 647 | 4D4F97BC1C6388AA00108BEB /* Debug */ = { 648 | isa = XCBuildConfiguration; 649 | buildSettings = { 650 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 651 | INFOPLIST_FILE = Demo/Info.plist; 652 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 653 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.Demo"; 654 | PRODUCT_NAME = "$(TARGET_NAME)"; 655 | SWIFT_VERSION = 5.0; 656 | }; 657 | name = Debug; 658 | }; 659 | 4D4F97BD1C6388AA00108BEB /* Release */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 663 | INFOPLIST_FILE = Demo/Info.plist; 664 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 665 | PRODUCT_BUNDLE_IDENTIFIER = "com.github.sgr-ksmt.Demo"; 666 | PRODUCT_NAME = "$(TARGET_NAME)"; 667 | SWIFT_VERSION = 5.0; 668 | }; 669 | name = Release; 670 | }; 671 | /* End XCBuildConfiguration section */ 672 | 673 | /* Begin XCConfigurationList section */ 674 | 4D4F97891C63882F00108BEB /* Build configuration list for PBXProject "PDFGenerator" */ = { 675 | isa = XCConfigurationList; 676 | buildConfigurations = ( 677 | 4D4F97A11C63882F00108BEB /* Debug */, 678 | 4D4F97A21C63882F00108BEB /* Release */, 679 | ); 680 | defaultConfigurationIsVisible = 0; 681 | defaultConfigurationName = Release; 682 | }; 683 | 4D4F97A31C63882F00108BEB /* Build configuration list for PBXNativeTarget "PDFGenerator" */ = { 684 | isa = XCConfigurationList; 685 | buildConfigurations = ( 686 | 4D4F97A41C63882F00108BEB /* Debug */, 687 | 4D4F97A51C63882F00108BEB /* Release */, 688 | ); 689 | defaultConfigurationIsVisible = 0; 690 | defaultConfigurationName = Release; 691 | }; 692 | 4D4F97A61C63882F00108BEB /* Build configuration list for PBXNativeTarget "PDFGeneratorTests" */ = { 693 | isa = XCConfigurationList; 694 | buildConfigurations = ( 695 | 4D4F97A71C63882F00108BEB /* Debug */, 696 | 4D4F97A81C63882F00108BEB /* Release */, 697 | ); 698 | defaultConfigurationIsVisible = 0; 699 | defaultConfigurationName = Release; 700 | }; 701 | 4D4F97BE1C6388AA00108BEB /* Build configuration list for PBXNativeTarget "Demo" */ = { 702 | isa = XCConfigurationList; 703 | buildConfigurations = ( 704 | 4D4F97BC1C6388AA00108BEB /* Debug */, 705 | 4D4F97BD1C6388AA00108BEB /* Release */, 706 | ); 707 | defaultConfigurationIsVisible = 0; 708 | defaultConfigurationName = Release; 709 | }; 710 | /* End XCConfigurationList section */ 711 | }; 712 | rootObject = 4D4F97861C63882F00108BEB /* Project object */; 713 | } 714 | -------------------------------------------------------------------------------- /PDFGenerator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PDFGenerator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PDFGenerator.xcodeproj/xcshareddata/xcschemes/PDFGenerator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /PDFGenerator/DPIType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DPIType.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/06/21. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public enum DPIType { 13 | fileprivate static let defaultDpi: CGFloat = 72.0 14 | case `default` 15 | case dpi_300 16 | case custom(CGFloat) 17 | 18 | public var value: CGFloat { 19 | switch self { 20 | case .default: 21 | return type(of: self).defaultDpi 22 | case .dpi_300: 23 | return 300.0 24 | case .custom(let value) where value > 1.0: 25 | return value 26 | default: 27 | return DPIType.default.value 28 | } 29 | } 30 | 31 | public var scaleFactor: CGFloat { 32 | return self.value / DPIType.default.value 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PDFGenerator/FilePathConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilePathConvertible.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 7/23/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol FilePathConvertible { 12 | var url: URL { get } 13 | var path: String { get } 14 | } 15 | 16 | extension FilePathConvertible { 17 | var isEmptyPath: Bool { 18 | return path.isEmpty 19 | } 20 | } 21 | 22 | extension String: FilePathConvertible { 23 | public var url: URL { 24 | return URL(fileURLWithPath: self) 25 | } 26 | 27 | public var path: String { 28 | return self 29 | } 30 | } 31 | 32 | extension URL: FilePathConvertible { 33 | public var url: URL { 34 | return self 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /PDFGenerator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /PDFGenerator/PDFGenerateError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFGenerateError.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/06/21. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | PDFGenerateError 13 | 14 | - ZeroSizeView: View's size is (0, 0) 15 | - ImageLoadFailed: Image has not been loaded from image path. 16 | - EmptyOutputPath: Output path is empty. 17 | - EmptyPage: Create PDF from no pages. 18 | - InvalidContext: If UIGraphicsGetCurrentContext returns nil. 19 | - InvalidPassword: If password cannot covert ASCII text. 20 | - TooLongPassword: If password too long 21 | */ 22 | public enum PDFGenerateError: Error { 23 | /// View's size is (0, 0) 24 | case zeroSizeView(UIView) 25 | /// Image has not been loaded from image path. 26 | case imageLoadFailed(Any) 27 | /// Output path is empty 28 | case emptyOutputPath 29 | /// Attempt to create empty PDF. (no pages.) 30 | case emptyPage 31 | /// If UIGraphicsGetCurrentContext returns nil. 32 | case invalidContext 33 | /// If rendering scale factor is zero. 34 | case invalidScaleFactor 35 | /// If password cannot covert ASCII text. 36 | case invalidPassword(String) 37 | /// If password too long 38 | case tooLongPassword(Int) 39 | } 40 | -------------------------------------------------------------------------------- /PDFGenerator/PDFGenerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // PDFGenerator.h 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/04. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PDFGenerator. 12 | FOUNDATION_EXPORT double PDFGeneratorVersionNumber; 13 | 14 | //! Project version string for PDFGenerator. 15 | FOUNDATION_EXPORT const unsigned char PDFGeneratorVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /PDFGenerator/PDFGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFGenerator.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /// PDFGenerator 13 | public final class PDFGenerator { 14 | fileprivate typealias Process = () throws -> Void 15 | 16 | /// Avoid creating instance. 17 | fileprivate init() { } 18 | 19 | /** 20 | Generate from page object. 21 | 22 | - parameter page: A `PDFPage`'s object. 23 | - parameter outputPath: An outputPath to save PDF. 24 | 25 | - throws: A `PDFGenerateError` thrown if some error occurred. 26 | */ 27 | public class func generate(_ page: PDFPage, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 28 | try generate([page], to: path, dpi: dpi, password: password) 29 | } 30 | 31 | /** 32 | Generate from page objects. 33 | 34 | - parameter pages: Array of `PDFPage`'s objects. 35 | - parameter outputPath: An outputPath to save PDF. 36 | 37 | - throws: A `PDFGenerateError` thrown if some error occurred. 38 | */ 39 | public class func generate(_ pages: [PDFPage], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 40 | guard !pages.isEmpty else { 41 | throw PDFGenerateError.emptyPage 42 | } 43 | guard !path.isEmptyPath else { 44 | throw PDFGenerateError.emptyOutputPath 45 | } 46 | do { 47 | try render(to: path, password: password) { 48 | try render(pages, dpi: dpi) 49 | } 50 | } catch let error { 51 | _ = try? FileManager.default.removeItem(at: path.url) 52 | throw error 53 | } 54 | } 55 | 56 | /** 57 | Generate from view. 58 | 59 | - parameter view: A view 60 | - parameter outputPath: An outputPath to save PDF. 61 | 62 | - throws: A `PDFGenerateError` thrown if some error occurred. 63 | */ 64 | public class func generate(_ view: UIView, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 65 | try generate([view], to: path, dpi: dpi, password: password) 66 | } 67 | 68 | /** 69 | Generate from views. 70 | 71 | - parameter views: Array of views. 72 | - parameter outputPath: An outputPath to save PDF. 73 | 74 | - throws: A `PDFGenerateError` thrown if some error occurred. 75 | */ 76 | public class func generate(_ views: [UIView], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 77 | try generate(PDFPage.pages(views), to: path, dpi: dpi, password: password) 78 | } 79 | 80 | /** 81 | Generate from image. 82 | 83 | - parameter image: An image. 84 | - parameter outputPath: An outputPath to save PDF. 85 | 86 | - throws: A `PDFGenerateError` thrown if some error occurred. 87 | */ 88 | public class func generate(_ image: UIImage, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 89 | try generate([image], to: path, dpi: dpi, password: password) 90 | } 91 | 92 | /** 93 | Generate from images. 94 | 95 | - parameter images: Array of images. 96 | - parameter outputPath: An outputPath to save PDF. 97 | 98 | - throws: A `PDFGenerateError` thrown if some error occurred. 99 | */ 100 | public class func generate(_ images: [UIImage], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 101 | try generate(PDFPage.pages(images), to: path, dpi: dpi, password: password) 102 | } 103 | 104 | /** 105 | Generate from image path. 106 | 107 | - parameter imagePath: An image path. 108 | - parameter outputPath: An outputPath to save PDF. 109 | 110 | - throws: A `PDFGenerateError` thrown if some error occurred. 111 | */ 112 | public class func generate(_ imagePath: String, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 113 | try generate([imagePath], to: path, dpi: dpi, password: password) 114 | } 115 | 116 | /** 117 | Generate from image paths. 118 | 119 | - parameter imagePaths: Arrat of image paths. 120 | - parameter outputPath: An outputPath to save PDF. 121 | 122 | - throws: A `PDFGenerateError` thrown if some error occurred. 123 | */ 124 | public class func generate(_ imagePaths: [String], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws { 125 | try generate(PDFPage.pages(imagePaths), to: path, dpi: dpi, password: password) 126 | } 127 | 128 | /** 129 | Generate from page object. 130 | 131 | - parameter page: A `PDFPage`'s object. 132 | 133 | - throws: A `PDFGenerateError` thrown if some error occurred. 134 | 135 | - returns: PDF's binary data (NSData) 136 | */ 137 | 138 | public class func generated(by page: PDFPage, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 139 | return try generated(by: [page], dpi: dpi, password: password) 140 | } 141 | 142 | /** 143 | Generate from page objects. 144 | 145 | - parameter pages: Array of `PDFPage`'s objects. 146 | 147 | - throws: A `PDFGenerateError` thrown if some error occurred. 148 | 149 | - returns: PDF's binary data (NSData) 150 | */ 151 | 152 | public class func generated(by pages: [PDFPage], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 153 | guard !pages.isEmpty else { 154 | throw PDFGenerateError.emptyPage 155 | } 156 | return try rendered(with: password) { try render(pages, dpi: dpi) } 157 | } 158 | 159 | /** 160 | Generate from view. 161 | 162 | - parameter view: A view 163 | 164 | - throws: A `PDFGenerateError` thrown if some error occurred. 165 | 166 | - returns: PDF's binary data (NSData) 167 | */ 168 | 169 | public class func generated(by view: UIView, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 170 | return try generated(by: [view], dpi: dpi, password: password) 171 | } 172 | 173 | /** 174 | Generate from views. 175 | 176 | - parameter views: Array of views. 177 | 178 | - throws: A `PDFGenerateError` thrown if some error occurred. 179 | 180 | - returns: PDF's binary data (NSData) 181 | */ 182 | 183 | public class func generated(by views: [UIView], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 184 | return try generated(by: PDFPage.pages(views), dpi: dpi, password: password) 185 | } 186 | 187 | /** 188 | Generate from image. 189 | 190 | - parameter image: An image. 191 | 192 | - throws: A `PDFGenerateError` thrown if some error occurred. 193 | 194 | - returns: PDF's binary data (NSData) 195 | */ 196 | 197 | public class func generated(by image: UIImage, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 198 | return try generated(by: [image], dpi: dpi, password: password) 199 | } 200 | 201 | /** 202 | Generate from images. 203 | 204 | - parameter images: Array of images. 205 | 206 | - throws: A `PDFGenerateError` thrown if some error occurred. 207 | 208 | - returns: PDF's binary data (NSData) 209 | */ 210 | 211 | public class func generated(by images: [UIImage], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 212 | return try generated(by: PDFPage.pages(images), dpi: dpi, password: password) 213 | } 214 | 215 | /** 216 | Generate from image path. 217 | 218 | - parameter imagePath: An image path. 219 | 220 | - throws: A `PDFGenerateError` thrown if some error occurred. 221 | 222 | - returns: PDF's binary data (NSData) 223 | */ 224 | 225 | public class func generated(by imagePath: String, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 226 | return try generated(by: [imagePath], dpi: dpi, password: password) 227 | } 228 | 229 | /** 230 | Generate from image paths. 231 | 232 | - parameter imagePaths: Arrat of image paths. 233 | 234 | - throws: A `PDFGenerateError` thrown if some error occurred. 235 | 236 | - returns: PDF's binary data (NSData) 237 | */ 238 | 239 | public class func generated(by imagePaths: [String], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data { 240 | return try generated(by: PDFPage.pages(imagePaths), dpi: dpi, password: password) 241 | } 242 | } 243 | 244 | // MARK: Private Extension 245 | 246 | /// PDFGenerator private extensions (render processes) 247 | private extension PDFGenerator { 248 | class func render(_ page: PDFPage, dpi: DPIType) throws { 249 | let scaleFactor = dpi.scaleFactor 250 | 251 | try autoreleasepool { 252 | switch page { 253 | case .whitePage(let size): 254 | let view = UIView(frame: CGRect(origin: .zero, size: size)) 255 | view.backgroundColor = .white 256 | try view.renderPDFPage(scaleFactor: scaleFactor) 257 | case .view(let view): 258 | try view.renderPDFPage(scaleFactor: scaleFactor) 259 | case .viewArea(let view, let area): 260 | try view.renderPDFPage(scaleFactor: scaleFactor, area: area) 261 | case .image(let image): 262 | try image.asUIImage().renderPDFPage(scaleFactor: scaleFactor) 263 | case .imagePath(let ip): 264 | try ip.asUIImage().renderPDFPage(scaleFactor: scaleFactor) 265 | case .binary(let data): 266 | try data.asUIImage().renderPDFPage(scaleFactor: scaleFactor) 267 | case .imageRef(let cgImage): 268 | try cgImage.asUIImage().renderPDFPage(scaleFactor: scaleFactor) 269 | } 270 | } 271 | } 272 | 273 | class func render(_ pages: [PDFPage], dpi: DPIType) throws { 274 | try pages.forEach { try render($0, dpi: dpi) } 275 | } 276 | 277 | class func render(to path: FilePathConvertible, password: PDFPassword, process: Process) rethrows { 278 | try { try password.verify() }() 279 | UIGraphicsBeginPDFContextToFile(path.path, .zero, password.toDocumentInfo()) 280 | try process() 281 | UIGraphicsEndPDFContext() 282 | } 283 | 284 | class func rendered(with password: PDFPassword, process: Process) rethrows -> Data { 285 | try { try password.verify() }() 286 | let data = NSMutableData() 287 | UIGraphicsBeginPDFContextToData(data, .zero, password.toDocumentInfo()) 288 | try process() 289 | UIGraphicsEndPDFContext() 290 | return data as Data 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /PDFGenerator/PDFPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPage.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/06/21. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /** 13 | PDF page model. 14 | 15 | - WhitePage: A white view (CGSize) 16 | - View: A view. (UIView) 17 | - Image: An image (UIImage) 18 | - ImagePath: ImagePath: An image path (String) 19 | - Binary: Binary data (NSData) 20 | - ImageRef: Image ref (CGImage) 21 | */ 22 | public enum PDFPage { 23 | /// A white view (CGSize) 24 | case whitePage(CGSize) 25 | /// A view. (UIView) 26 | case view(UIView) 27 | /// An area of a view. (UIView) 28 | case viewArea(UIView, area: CGRect) 29 | /// An image (UIImage) 30 | case image(UIImage) 31 | /// ImagePath: An image path (String) 32 | case imagePath(String) 33 | /// Binary data (NSData) 34 | case binary(Data) 35 | /// Image ref (CGImage) 36 | case imageRef(CGImage) 37 | 38 | /** 39 | Convert views to PDFPage models. 40 | 41 | - parameter views: Array of `UIVIew` 42 | 43 | - returns: Array of `PDFPage` 44 | */ 45 | static func pages(_ views: [UIView]) -> [PDFPage] { 46 | return views.map { .view($0) } 47 | } 48 | 49 | /** 50 | Convert images to PDFPage models. 51 | 52 | - parameter views: Array of `UIImage` 53 | 54 | - returns: Array of `PDFPage` 55 | */ 56 | static func pages(_ images: [UIImage]) -> [PDFPage] { 57 | return images.map { .image($0) } 58 | } 59 | 60 | /** 61 | Convert image path to PDFPage models. 62 | 63 | - parameter views: Array of `String`(image path) 64 | 65 | - returns: Array of `PDFPage` 66 | */ 67 | static func pages(_ imagePaths: [String]) -> [PDFPage] { 68 | return imagePaths.map { .imagePath($0) } 69 | } 70 | 71 | /** 72 | Convert a scrollview into different pages with a given configuration 73 | 74 | - parameter scrollview: the `UIScrollView` that should be rendered 75 | 76 | - parameter configuration: a `PDFPagedScrollViewConfiguration` including the overlapPercentage and the ratio (format) of the Page 77 | 78 | - returns: Array of `PDFPage` 79 | */ 80 | public static func pages(_ scrollView: UIScrollView, configuration: PDFPagedScrollViewConfiguration = PDFPagedScrollViewConfiguration(overlapPercentage: 0.10, ratio: .dinA4)) -> [PDFPage] { 81 | let contentSize = scrollView.contentSize 82 | let height = contentSize.width / configuration.ratio.rawValue 83 | let overlapPercentage = configuration.overlapPercentage > 0 && configuration.overlapPercentage < 1 ? configuration.overlapPercentage : 0 84 | 85 | var currentOffset: CGFloat = 0 86 | var areas: [CGRect] = [] 87 | 88 | while currentOffset < contentSize.height { 89 | let area = CGRect(x: 0, y: currentOffset, width: contentSize.width, height: height) 90 | areas.append(area) 91 | currentOffset += height - height * overlapPercentage 92 | } 93 | return areas.map { .viewArea(scrollView, area: $0) } 94 | } 95 | } 96 | 97 | /// PDF page size (pixel, 72dpi) 98 | public struct PDFPageSize { 99 | fileprivate init() { } 100 | /// A4 101 | public static let A4 = CGSize(width: 595.0, height: 842.0) 102 | /// A5 103 | public static let A5 = CGSize(width: 420.0, height: 595.0) 104 | ///A6 105 | public static let A6 = CGSize(width: 298.0, height: 420.0) 106 | /// B5 107 | public static let B5 = CGSize(width: 516.0, height: 729.0) 108 | } 109 | -------------------------------------------------------------------------------- /PDFGenerator/PDFPageRenderable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPageRenderable.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/10. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import WebKit 12 | 13 | protocol PDFPageRenderable { 14 | func renderPDFPage(scaleFactor: CGFloat) throws 15 | } 16 | 17 | private extension UIScrollView { 18 | typealias TempInfo = (frame: CGRect, offset: CGPoint, inset: UIEdgeInsets) 19 | 20 | var tempInfo: TempInfo { 21 | return (frame, contentOffset, contentInset) 22 | } 23 | 24 | func transformForRender() { 25 | contentOffset = .zero 26 | contentInset = UIEdgeInsets.zero 27 | frame = CGRect(origin: .zero, size: contentSize) 28 | } 29 | 30 | func restore(_ info: TempInfo) { 31 | frame = info.frame 32 | contentOffset = info.offset 33 | contentInset = info.inset 34 | } 35 | } 36 | 37 | extension UIView: PDFPageRenderable { 38 | fileprivate func _render(_ view: T, scaleFactor: CGFloat, area: CGRect? = nil, completion: (T) -> Void = { _ in }) throws { 39 | guard scaleFactor > 0.0 else { 40 | throw PDFGenerateError.invalidScaleFactor 41 | } 42 | 43 | let size: CGSize 44 | let origin: CGPoint 45 | if let area = area { 46 | origin = area.origin 47 | size = area.size 48 | } else { 49 | origin = .zero 50 | size = getPageSize() 51 | } 52 | 53 | guard size.width > 0 && size.height > 0 else { 54 | throw PDFGenerateError.zeroSizeView(self) 55 | } 56 | guard let context = UIGraphicsGetCurrentContext() else { 57 | throw PDFGenerateError.invalidContext 58 | } 59 | 60 | let renderFrame = CGRect(origin: CGPoint(x: origin.x * scaleFactor, y: origin.y * scaleFactor), 61 | size: CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)) 62 | autoreleasepool { 63 | let superView = view.superview 64 | view.removeFromSuperview() 65 | UIGraphicsBeginPDFPageWithInfo(CGRect(origin: .zero, size: renderFrame.size), nil) 66 | context.translateBy(x: -renderFrame.origin.x, y: -renderFrame.origin.y) 67 | view.layer.render(in: context) 68 | superView?.addSubview(view) 69 | superView?.layoutIfNeeded() 70 | completion(view) 71 | } 72 | } 73 | 74 | func renderPDFPage(scaleFactor: CGFloat) throws { 75 | try self.renderPDFPage(scaleFactor: scaleFactor, area: nil) 76 | } 77 | 78 | func renderPDFPage(scaleFactor: CGFloat, area: CGRect?) throws { 79 | func renderScrollView(_ scrollView: UIScrollView, area: CGRect?) throws { 80 | let tmp = scrollView.tempInfo 81 | scrollView.transformForRender() 82 | try _render(scrollView, scaleFactor: scaleFactor, area: area) { scrollView in 83 | scrollView.restore(tmp) 84 | } 85 | } 86 | 87 | if let webView = self as? WKWebView { 88 | try renderScrollView(webView.scrollView, area: area) 89 | } else if let webView = self as? WKWebView { 90 | try renderScrollView(webView.scrollView, area: area) 91 | } else if let scrollView = self as? UIScrollView { 92 | try renderScrollView(scrollView, area: area) 93 | } else { 94 | try _render(self, scaleFactor: scaleFactor, area: area) 95 | } 96 | } 97 | 98 | fileprivate func getPageSize() -> CGSize { 99 | switch self { 100 | case (let webView as WKWebView): 101 | return webView.scrollView.contentSize 102 | case (let scrollView as UIScrollView): 103 | return scrollView.contentSize 104 | default: 105 | return self.frame.size 106 | } 107 | } 108 | } 109 | 110 | extension UIImage: PDFPageRenderable { 111 | func renderPDFPage(scaleFactor: CGFloat) throws { 112 | guard scaleFactor > 0.0 else { 113 | throw PDFGenerateError.invalidScaleFactor 114 | } 115 | autoreleasepool { 116 | let bounds = CGRect( 117 | origin: .zero, 118 | size: CGSize( 119 | width: size.width * scaleFactor, 120 | height: size.height * scaleFactor 121 | ) 122 | ) 123 | UIGraphicsBeginPDFPageWithInfo(bounds, nil) 124 | draw(in: bounds) 125 | } 126 | } 127 | } 128 | 129 | protocol UIImageConvertible { 130 | func asUIImage() throws -> UIImage 131 | } 132 | 133 | extension UIImage: UIImageConvertible { 134 | func asUIImage() throws -> UIImage { 135 | return self 136 | } 137 | } 138 | 139 | extension String: UIImageConvertible { 140 | func asUIImage() throws -> UIImage { 141 | guard let image = UIImage(contentsOfFile: self) else { 142 | throw PDFGenerateError.imageLoadFailed(self) 143 | } 144 | return image 145 | } 146 | } 147 | 148 | extension Data: UIImageConvertible { 149 | func asUIImage() throws -> UIImage { 150 | guard let image = UIImage(data: self) else { 151 | throw PDFGenerateError.imageLoadFailed(self) 152 | } 153 | return image 154 | } 155 | } 156 | 157 | extension CGImage: UIImageConvertible { 158 | func asUIImage() throws -> UIImage { 159 | return UIImage(cgImage: self) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /PDFGenerator/PDFPagedScrollViewConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPagedScrollViewConfiguration.swift 3 | // ActionSheetPicker-3.0 4 | // 5 | // Created by Philip Messlehner on 03.08.18. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct PDFPagedScrollViewConfiguration { 11 | public let overlapPercentage: CGFloat 12 | public let ratio: PageRatio 13 | 14 | public init(overlapPercentage: CGFloat, ratio: PageRatio) { 15 | self.overlapPercentage = overlapPercentage 16 | self.ratio = ratio 17 | } 18 | 19 | public enum PageRatio { 20 | case dinA3, dinA4, dinA5 21 | case ansiA, ansiB, ansiC 22 | case invoice, executive, legal, letter 23 | case custom(CGFloat) 24 | 25 | public var rawValue: CGFloat { 26 | switch self { 27 | case .dinA3: return 297.0 / 420.0 28 | case .dinA4: return 210.0 / 297.0 29 | case .dinA5: return 148.0 / 210.0 30 | case .ansiA: return 216.0 / 279.0 31 | case .ansiB: return 279.0 / 432.0 32 | case .ansiC: return 432.0 / 559.0 33 | case .invoice: return 140.0 / 216.0 34 | case .executive: return 184.0 / 267.0 35 | case .legal: return 216.0 / 356.0 36 | case .letter: return PageRatio.ansiA.rawValue 37 | case .custom(let ratio): return ratio 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PDFGenerator/PDFPassword.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPassword.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 2016/07/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct PDFPassword { 13 | static let NoPassword = "" 14 | fileprivate static let PasswordLengthMax = 32 15 | let userPassword: String 16 | let ownerPassword: String 17 | 18 | public init(user userPassword: String, owner ownerPassword: String) { 19 | self.userPassword = userPassword 20 | self.ownerPassword = ownerPassword 21 | } 22 | 23 | public init(_ password: String) { 24 | self.init(user: password, owner: password) 25 | } 26 | 27 | func toDocumentInfo() -> [AnyHashable: Any] { 28 | var info: [AnyHashable: Any] = [:] 29 | if userPassword != type(of: self).NoPassword { 30 | info[String(kCGPDFContextUserPassword)] = userPassword 31 | } 32 | if ownerPassword != type(of: self).NoPassword { 33 | info[String(kCGPDFContextOwnerPassword)] = ownerPassword 34 | } 35 | return info 36 | } 37 | 38 | func verify() throws { 39 | guard userPassword.canBeConverted(to: String.Encoding.ascii) else { 40 | throw PDFGenerateError.invalidPassword(userPassword) 41 | } 42 | guard userPassword.count <= type(of: self).PasswordLengthMax else { 43 | throw PDFGenerateError.tooLongPassword(userPassword.count) 44 | } 45 | 46 | guard ownerPassword.canBeConverted(to: String.Encoding.ascii) else { 47 | throw PDFGenerateError.invalidPassword(ownerPassword) 48 | } 49 | guard ownerPassword.count <= type(of: self).PasswordLengthMax else { 50 | throw PDFGenerateError.tooLongPassword(ownerPassword.count) 51 | } 52 | } 53 | } 54 | 55 | extension PDFPassword: ExpressibleByStringLiteral { 56 | public init(unicodeScalarLiteral value: String) { 57 | self.init(value) 58 | } 59 | 60 | public init(extendedGraphemeClusterLiteral value: String) { 61 | self.init(value) 62 | } 63 | 64 | public init(stringLiteral value: String) { 65 | self.init(value) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /PDFGeneratorTests/DPITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DPITests.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 7/23/16. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import PDFGenerator 11 | 12 | class DPITests: XCTestCase { 13 | func test() { 14 | XCTAssertEqual(DPIType.default.value, 72.0) 15 | XCTAssertEqual(DPIType.dpi_300.value, 300.0) 16 | XCTAssertEqual(DPIType.custom(100.0).value, 100.0) 17 | XCTAssertEqual(DPIType.custom(-100.0).value, DPIType.default.value) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PDFGeneratorTests/FilePathConvertibleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilePathConvertibleTests.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 7/23/16. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import PDFGenerator 11 | 12 | class FilePathConvertibleTests: XCTestCase { 13 | 14 | func test() { 15 | let p1: FilePathConvertible = "" 16 | XCTAssertNotNil(p1.url) 17 | XCTAssertEqual(p1.path, "") 18 | XCTAssertEqual(p1.url, URL(fileURLWithPath: "")) 19 | 20 | let p2: FilePathConvertible = "path/to/hoge.txt" 21 | XCTAssertNotNil(p2.url) 22 | XCTAssertEqual(p2.url, URL(fileURLWithPath: "path/to/hoge.txt")) 23 | XCTAssertEqual(p2.path, "path/to/hoge.txt") 24 | 25 | let p3: FilePathConvertible = URL(fileURLWithPath: "path/to/hoge.txt") 26 | XCTAssertNotNil(p3.url) 27 | XCTAssertEqual(p3.url, URL(fileURLWithPath: "path/to/hoge.txt")) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PDFGeneratorTests/Images/test_image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgr-ksmt/PDFGenerator/4036333e83a0a2abb23837cc264b2fc8befb0460/PDFGeneratorTests/Images/test_image1.png -------------------------------------------------------------------------------- /PDFGeneratorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PDFGeneratorTests/PDFGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFGeneratorTests.swift 3 | // PDFGeneratorTests 4 | // 5 | // Created by Suguru Kishimoto on 2016/02/04. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import PDFGenerator 11 | 12 | class Mock { 13 | struct ImageName { 14 | static let testImage1 = "test_image1" 15 | } 16 | 17 | class func view(_ size: CGSize) -> UIView { 18 | return UIView(frame: CGRect(origin: CGPoint.zero, size: size)) 19 | } 20 | 21 | class func scrollView(_ size: CGSize) -> UIScrollView { 22 | return { () -> UIScrollView in 23 | let v = UIScrollView(frame: CGRect(origin: CGPoint.zero, size: size)) 24 | v.contentSize = v.frame.size 25 | return v 26 | }() 27 | } 28 | 29 | class func imagePath(_ name: String) -> String { 30 | return Bundle(for: self).path(forResource: name, ofType: "png")! 31 | } 32 | 33 | class func image(_ name: String) -> UIImage { 34 | return UIImage(contentsOfFile: imagePath(name))! 35 | } 36 | 37 | } 38 | 39 | class PDFGeneratorTests: XCTestCase { 40 | 41 | func isExistPDF(_ path: String) -> Bool { 42 | return FileManager.default.fileExists(atPath: path) 43 | } 44 | 45 | func PDFDirectoryPath() -> String { 46 | return NSHomeDirectory() + "/test/" 47 | } 48 | 49 | func PDFfilePath(_ fileName: String) -> String { 50 | return PDFDirectoryPath() + "/\(fileName)" 51 | } 52 | 53 | override func setUp() { 54 | super.setUp() 55 | try! FileManager.default.createDirectory( 56 | atPath: PDFDirectoryPath(), 57 | withIntermediateDirectories: true, 58 | attributes: nil 59 | ) 60 | } 61 | 62 | override func tearDown() { 63 | _ = try? FileManager.default.removeItem(atPath: PDFDirectoryPath()) 64 | super.tearDown() 65 | } 66 | 67 | // MARK: UIView -> PDF 68 | func testViewToPDF() { 69 | let view = Mock.view(CGSize(width: 100, height: 100)) 70 | let view2 = Mock.scrollView(CGSize(width: 100, height: 100)) 71 | 72 | let path1 = PDFfilePath("test_sample1.pdf") 73 | _ = try? PDFGenerator.generate(view, to: path1) 74 | XCTAssertTrue(isExistPDF(path1)) 75 | 76 | let path2 = PDFfilePath("hoge/test_sample2.pdf") 77 | _ = try? PDFGenerator.generate(view, to: path2) 78 | XCTAssertFalse(isExistPDF(path2)) 79 | 80 | let path3 = PDFfilePath("test_sample3.pdf") 81 | _ = try? PDFGenerator.generate(view, to: path3) 82 | XCTAssertTrue(isExistPDF(path3)) 83 | 84 | XCTAssertNotNil(try? PDFGenerator.generated(by: view)) 85 | XCTAssertNotNil(try? PDFGenerator.generated(by: [view])) 86 | XCTAssertNotNil(try? PDFGenerator.generated(by: [view, view2])) 87 | } 88 | 89 | // MARK: UIImage -> PDF 90 | func testImageToPDF() { 91 | let image1 = Mock.image("test_image1") 92 | let image2 = Mock.image("test_image1") 93 | 94 | let path1 = PDFfilePath("test_sample1.pdf") 95 | _ = try? PDFGenerator.generate(image1, to: path1) 96 | XCTAssertTrue(isExistPDF(path1)) 97 | 98 | let path2 = PDFfilePath("hoge/test_sample2.pdf") 99 | _ = try? PDFGenerator.generate(image1, to: path2) 100 | XCTAssertFalse(isExistPDF(path2)) 101 | 102 | let path3 = PDFfilePath("test_sample3.pdf") 103 | _ = try? PDFGenerator.generate([image1, image2], to: path3) 104 | XCTAssertTrue(isExistPDF(path3)) 105 | 106 | XCTAssertNotNil(try? PDFGenerator.generated(by: image1)) 107 | XCTAssertNotNil(try? PDFGenerator.generated(by: [image1])) 108 | XCTAssertNotNil(try? PDFGenerator.generated(by: [image1, image2])) 109 | } 110 | 111 | // MARK: ImagePath(String) -> PDF 112 | func testImagePathToPDF() { 113 | let image1 = Mock.imagePath("test_image1") 114 | let image2 = Mock.imagePath("test_image1") 115 | 116 | let path1 = PDFfilePath("test_sample1.pdf") 117 | _ = try? PDFGenerator.generate(image1, to: path1) 118 | XCTAssertTrue(isExistPDF(path1)) 119 | 120 | let path2 = PDFfilePath("hoge/test_sample2.pdf") 121 | _ = try? PDFGenerator.generate(image1, to: path2) 122 | XCTAssertFalse(isExistPDF(path2)) 123 | 124 | let path3 = PDFfilePath("test_sample3.pdf") 125 | _ = try? PDFGenerator.generate([image1, image2], to: path3) 126 | XCTAssertTrue(isExistPDF(path3)) 127 | 128 | XCTAssertNotNil(try? PDFGenerator.generated(by: image1)) 129 | XCTAssertNotNil(try? PDFGenerator.generated(by: [image1])) 130 | XCTAssertNotNil(try? PDFGenerator.generated(by: [image1, image2])) 131 | } 132 | 133 | // MARK: PDFPage -> PDF 134 | func testMixedPageToPDF() { 135 | let p1 = PDFPage.view(Mock.view(CGSize(width: 100, height: 100))) 136 | let p2 = PDFPage.image(Mock.image(Mock.ImageName.testImage1)) 137 | let p3 = PDFPage.imagePath(Mock.imagePath(Mock.ImageName.testImage1)) 138 | let p4 = PDFPage.whitePage(CGSize(width: 100, height: 100)) 139 | let p5 = PDFPage.imageRef(Mock.image(Mock.ImageName.testImage1).cgImage!) 140 | let p6 = PDFPage.binary(Mock.image(Mock.ImageName.testImage1).pngData()!) 141 | 142 | let path1 = PDFfilePath("test_sample1.pdf") 143 | _ = try? PDFGenerator.generate(p1, to: path1) 144 | XCTAssertTrue(isExistPDF(path1)) 145 | 146 | let path2 = PDFfilePath("hoge/test_sample2.pdf") 147 | _ = try? PDFGenerator.generate(p2, to: path2) 148 | XCTAssertFalse(isExistPDF(path2)) 149 | 150 | let path3 = PDFfilePath("test_sample3.pdf") 151 | _ = try? PDFGenerator.generate([p1, p2, p3, p4], to: path3) 152 | XCTAssertTrue(isExistPDF(path3)) 153 | 154 | XCTAssertNotNil(try? PDFGenerator.generated(by: p1)) 155 | XCTAssertNotNil(try? PDFGenerator.generated(by: [p2])) 156 | XCTAssertNotNil(try? PDFGenerator.generated(by: [p3, p4])) 157 | XCTAssertNotNil(try? PDFGenerator.generated(by: [p5, p6])) 158 | 159 | } 160 | 161 | // swiftlint:disable function_body_length 162 | func testErrors() { 163 | let view = Mock.view(CGSize(width: 100, height: 100)) 164 | let image = Mock.image(Mock.ImageName.testImage1) 165 | let imagePath = Mock.imagePath(Mock.ImageName.testImage1) 166 | let viewPage = PDFPage.view(Mock.view(CGSize(width: 100, height: 100))) 167 | let imagePage = PDFPage.image(Mock.image(Mock.ImageName.testImage1)) 168 | let imagePathPage = PDFPage.imagePath(Mock.imagePath(Mock.ImageName.testImage1)) 169 | let whitePage = PDFPage.whitePage(CGSize(width: 100, height: 100)) 170 | let views = [ 171 | Mock.view(CGSize(width: 100, height: 100)), 172 | Mock.view(CGSize(width: 100, height: 100)) 173 | ] 174 | let images = [ 175 | Mock.image(Mock.ImageName.testImage1), 176 | Mock.image(Mock.ImageName.testImage1) 177 | ] 178 | let imagePaths = [ 179 | Mock.imagePath(Mock.ImageName.testImage1), 180 | Mock.imagePath(Mock.ImageName.testImage1) 181 | ] 182 | 183 | let pages = [ 184 | PDFPage.view(Mock.view(CGSize(width: 100, height: 100))), 185 | PDFPage.image(Mock.image(Mock.ImageName.testImage1)), 186 | PDFPage.imagePath(Mock.imagePath(Mock.ImageName.testImage1)), 187 | PDFPage.whitePage(CGSize(width: 100, height: 100)) 188 | ] 189 | 190 | let mocks: [Any] = [ 191 | view, 192 | image, 193 | imagePath, 194 | viewPage, 195 | imagePage, 196 | imagePathPage, 197 | whitePage, 198 | views, 199 | images, 200 | imagePaths, 201 | pages 202 | ] 203 | 204 | let emptyMocks: [Any] = [ 205 | [UIView](), 206 | [UIImage](), 207 | [String](), 208 | [PDFPage]() 209 | ] 210 | 211 | // MARK: check EmptyOutputPath 212 | mocks.forEach { 213 | do { 214 | if let page = $0 as? UIView { 215 | try PDFGenerator.generate(page, to: "") 216 | } else if let page = $0 as? UIImage { 217 | try PDFGenerator.generate(page, to: "") 218 | } else if let page = $0 as? String { 219 | try PDFGenerator.generate(page, to: "") 220 | } else if let page = $0 as? PDFPage { 221 | try PDFGenerator.generate(page, to: "") 222 | } else if let pages = $0 as? [UIView] { 223 | try PDFGenerator.generate(pages, to: "") 224 | } else if let pages = $0 as? [UIImage] { 225 | try PDFGenerator.generate(pages, to: "") 226 | } else if let pages = $0 as? [String] { 227 | try PDFGenerator.generate(pages, to: "") 228 | } else if let pages = $0 as? [PDFPage] { 229 | try PDFGenerator.generate(pages, to: "") 230 | } else { 231 | XCTFail("invalid page(s) type found.") 232 | } 233 | XCTFail("[\($0)] No create PDF from empty name image path.") 234 | } catch PDFGenerateError.emptyOutputPath { 235 | // Right Error 236 | } catch (let e) { 237 | XCTFail("[\($0)] Unknown or wrong error occurred.\(e)") 238 | } 239 | } 240 | 241 | // MARK: check EmptyPage 242 | emptyMocks.forEach { 243 | do { 244 | let path = PDFfilePath("test_sample1.pdf") 245 | if let pages = $0 as? [UIView] { 246 | try PDFGenerator.generate(pages, to: path) 247 | } else if let pages = $0 as? [UIImage] { 248 | try PDFGenerator.generate(pages, to: path) 249 | } else if let pages = $0 as? [String] { 250 | try PDFGenerator.generate(pages, to: path) 251 | } else if let pages = $0 as? [PDFPage] { 252 | try PDFGenerator.generate(pages, to: path) 253 | } else { 254 | XCTFail("invalid pages type found.") 255 | } 256 | XCTFail("[\($0)] No create PDF from empty name image path.") 257 | } catch PDFGenerateError.emptyPage { 258 | // Right Error 259 | } catch (let e) { 260 | XCTFail("[\($0)] Unknown or wrong error occurred.\(e)") 261 | } 262 | } 263 | 264 | // MARK: check EmptyPage 265 | emptyMocks.forEach { 266 | do { 267 | if let pages = $0 as? [UIView] { 268 | _ = try PDFGenerator.generated(by: pages) 269 | } else if let pages = $0 as? [UIImage] { 270 | _ = try PDFGenerator.generated(by: pages) 271 | } else if let pages = $0 as? [String] { 272 | _ = try PDFGenerator.generated(by: pages) 273 | } else if let pages = $0 as? [PDFPage] { 274 | _ = try PDFGenerator.generated(by: pages) 275 | } else { 276 | XCTFail("invalid pages type found.") 277 | } 278 | XCTFail("[\($0)] No create PDF from empty name image path.") 279 | } catch PDFGenerateError.emptyPage { 280 | // Right Error 281 | } catch (let e) { 282 | XCTFail("[\($0)] Unknown or wrong error occurred.\(e)") 283 | } 284 | } 285 | 286 | // MARK: check ZeroSizeView 287 | let emptyView = Mock.view(CGSize.zero) 288 | do { 289 | let path = PDFfilePath("test_sample2.pdf") 290 | try PDFGenerator.generate(emptyView, to: path) 291 | } catch PDFGenerateError.zeroSizeView(let v) { 292 | XCTAssertEqual(emptyView, v) 293 | } catch (let e) { 294 | XCTFail("Unknown or wrong error occurred.\(e)") 295 | } 296 | do { 297 | _ = try PDFGenerator.generated(by: emptyView) 298 | } catch PDFGenerateError.zeroSizeView(let v) { 299 | XCTAssertEqual(emptyView, v) 300 | } catch (let e) { 301 | XCTFail("Unknown or wrong error occurred.\(e)") 302 | } 303 | do { 304 | _ = try PDFGenerator.generated(by: [emptyView]) 305 | } catch PDFGenerateError.zeroSizeView(let v) { 306 | XCTAssertEqual(emptyView, v) 307 | } catch (let e) { 308 | XCTFail("Unknown or wrong error occurred.\(e)") 309 | } 310 | 311 | let emptyViewPage = PDFPage.view(emptyView) 312 | do { 313 | let path = PDFfilePath("test_sample3.pdf") 314 | try PDFGenerator.generate(emptyViewPage, to: path) 315 | } catch PDFGenerateError.zeroSizeView(let v) { 316 | XCTAssertEqual(emptyView, v) 317 | } catch (let e) { 318 | XCTFail("Unknown or wrong error occurred.\(e)") 319 | } 320 | do { 321 | _ = try PDFGenerator.generated(by: emptyViewPage) 322 | } catch PDFGenerateError.zeroSizeView(let v) { 323 | XCTAssertEqual(emptyView, v) 324 | } catch (let e) { 325 | XCTFail("Unknown or wrong error occurred.\(e)") 326 | } 327 | do { 328 | _ = try PDFGenerator.generated(by: [emptyViewPage]) 329 | } catch PDFGenerateError.zeroSizeView(let v) { 330 | XCTAssertEqual(emptyView, v) 331 | } catch (let e) { 332 | XCTFail("Unknown or wrong error occurred.\(e)") 333 | } 334 | 335 | // MARK: check ImageLoadFailed 336 | let wrongImagePath = "wrong/image.png" 337 | do { 338 | let path = PDFfilePath("test_sample4.pdf") 339 | try PDFGenerator.generate(wrongImagePath, to: path) 340 | } catch PDFGenerateError.imageLoadFailed(let ip) { 341 | XCTAssertEqual(wrongImagePath, ip as? String) 342 | } catch (let e) { 343 | XCTFail("Unknown or wrong error occurred.\(e)") 344 | } 345 | do { 346 | _ = try PDFGenerator.generated(by: wrongImagePath) 347 | } catch PDFGenerateError.imageLoadFailed(let ip) { 348 | XCTAssertEqual(wrongImagePath, ip as? String) 349 | } catch (let e) { 350 | XCTFail("Unknown or wrong error occurred.\(e)") 351 | } 352 | do { 353 | _ = try PDFGenerator.generated(by: [wrongImagePath]) 354 | } catch PDFGenerateError.imageLoadFailed(let ip) { 355 | XCTAssertEqual(wrongImagePath, ip as? String) 356 | } catch (let e) { 357 | XCTFail("Unknown or wrong error occurred.\(e)") 358 | } 359 | 360 | let wrongImagePathPage = PDFPage.imagePath(wrongImagePath) 361 | do { 362 | let path = PDFfilePath("test_sample5.pdf") 363 | try PDFGenerator.generate(wrongImagePathPage, to: path) 364 | } catch PDFGenerateError.imageLoadFailed(let ip) { 365 | XCTAssertEqual(wrongImagePath, ip as? String) 366 | } catch (let e) { 367 | XCTFail("Unknown or wrong error occurred.\(e)") 368 | } 369 | do { 370 | _ = try PDFGenerator.generated(by: wrongImagePathPage) 371 | } catch PDFGenerateError.imageLoadFailed(let ip) { 372 | XCTAssertEqual(wrongImagePath, ip as? String) 373 | } catch (let e) { 374 | XCTFail("Unknown or wrong error occurred.\(e)") 375 | } 376 | do { 377 | _ = try PDFGenerator.generated(by: [wrongImagePathPage]) 378 | } catch PDFGenerateError.imageLoadFailed(let ip) { 379 | XCTAssertEqual(wrongImagePath, ip as? String) 380 | } catch (let e) { 381 | XCTFail("Unknown or wrong error occurred.\(e)") 382 | } 383 | 384 | let wrongData = Data() 385 | 386 | do { 387 | _ = try PDFGenerator.generated(by: PDFPage.binary(wrongData)) 388 | } catch PDFGenerateError.imageLoadFailed(let data) { 389 | XCTAssertEqual(wrongData, data as? Data) 390 | } catch (let e) { 391 | XCTFail("Unknown or wrong error occurred.\(e)") 392 | } 393 | 394 | } 395 | // swiftlint:enable function_body_length 396 | 397 | func testPDFPassword() { 398 | let view = Mock.view(CGSize(width: 100, height: 100)) 399 | let view2 = Mock.view(CGSize(width: 100, height: 100)) 400 | 401 | let path1 = PDFfilePath("test_sample1.pdf") 402 | _ = try? PDFGenerator.generate(view, to: path1, password: "abcdef") 403 | XCTAssertTrue(isExistPDF(path1)) 404 | 405 | let path2 = PDFfilePath("test_sample2.pdf") 406 | _ = try? PDFGenerator.generate(view, to: path2, password: "⌘123456") 407 | XCTAssertFalse(isExistPDF(path2)) 408 | 409 | let path3 = PDFfilePath("test_sample3.pdf") 410 | do { 411 | try PDFGenerator.generate([view, view2], to: path3, password: "123456") 412 | } catch { 413 | XCTFail() 414 | } 415 | 416 | let path4 = PDFfilePath("test_sample4.pdf") 417 | do { 418 | try PDFGenerator.generate([view, view2], to: path4, password: "⌘123456") 419 | XCTFail() 420 | } catch PDFGenerateError.invalidPassword(let password) { 421 | XCTAssertEqual(password, "⌘123456") 422 | } catch { 423 | XCTFail() 424 | } 425 | 426 | let path5 = PDFfilePath("test_sample5.pdf") 427 | do { 428 | try PDFGenerator.generate([view, view2], to: path5, password: "0123456789abcdef0123456789abcdefA") 429 | XCTFail() 430 | } catch PDFGenerateError.tooLongPassword(let length) { 431 | XCTAssertEqual(length, 33) 432 | } catch { 433 | XCTFail() 434 | } 435 | 436 | XCTAssertNotNil(try? PDFGenerator.generated(by: view, password: "abcdef")) 437 | XCTAssertNil(try? PDFGenerator.generated(by: [view], password: "⌘123456")) 438 | 439 | do { 440 | _ = try PDFGenerator.generated(by: [view], password: "123456") 441 | } catch { 442 | XCTFail() 443 | } 444 | 445 | do { 446 | _ = try PDFGenerator.generated(by: [view], password: "⌘123456") 447 | } catch PDFGenerateError.invalidPassword(let password) { 448 | XCTAssertEqual(password, "⌘123456") 449 | } catch { 450 | XCTFail() 451 | } 452 | 453 | do { 454 | _ = try PDFGenerator.generated(by: [view], password: "0123456789abcdef0123456789abcdefA") 455 | XCTFail() 456 | } catch PDFGenerateError.tooLongPassword(let length) { 457 | XCTAssertEqual(length, 33) 458 | } catch { 459 | XCTFail() 460 | } 461 | } 462 | } 463 | -------------------------------------------------------------------------------- /PDFGeneratorTests/PDFPasswordTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPasswordTests.swift 3 | // PDFGenerator 4 | // 5 | // Created by Suguru Kishimoto on 7/23/16. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import PDFGenerator 11 | class PDFPasswordTests: XCTestCase { 12 | func test() { 13 | let p1: PDFPassword = "123456" 14 | XCTAssertEqual(p1.userPassword, "123456") 15 | XCTAssertEqual(p1.ownerPassword, "123456") 16 | do { 17 | try p1.verify() 18 | } catch _ { 19 | XCTFail() 20 | } 21 | 22 | let p2: PDFPassword = PDFPassword(user: "123456", owner: "abcdef") 23 | XCTAssertNotEqual(p2.userPassword, p2.ownerPassword) 24 | do { 25 | try p2.verify() 26 | } catch _ { 27 | XCTFail() 28 | } 29 | 30 | let p3: PDFPassword = PDFPassword(user: "ああああ", owner: "abcdef") 31 | do { 32 | try p3.verify() 33 | XCTFail() 34 | } catch PDFGenerateError.invalidPassword(let password) { 35 | XCTAssertEqual(p3.userPassword, password) 36 | } catch _ { 37 | XCTFail() 38 | } 39 | 40 | let p4: PDFPassword = PDFPassword(user: "123456", owner: "ああああ") 41 | do { 42 | try p4.verify() 43 | XCTFail() 44 | } catch PDFGenerateError.invalidPassword(let password) { 45 | XCTAssertEqual(p4.ownerPassword, password) 46 | } catch _ { 47 | XCTFail() 48 | } 49 | 50 | let p5: PDFPassword = PDFPassword(user: "1234567890123456789012345678901234567890", owner: "abcdef") 51 | do { 52 | try p5.verify() 53 | XCTFail() 54 | } catch PDFGenerateError.tooLongPassword(let length) { 55 | XCTAssertEqual(p5.userPassword.count, length) 56 | } catch _ { 57 | XCTFail() 58 | } 59 | 60 | let p6: PDFPassword = PDFPassword(user: "123456", owner: "abcdefghijabcdefghijabcdefghijabcdefghij") 61 | do { 62 | try p6.verify() 63 | XCTFail() 64 | } catch PDFGenerateError.tooLongPassword(let length) { 65 | XCTAssertEqual(p6.ownerPassword.count, length) 66 | } catch _ { 67 | XCTFail() 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Features | 3 | Requirements | 4 | Installation | 5 | Usage | 6 | Communication | 7 | LICENSE 8 |

9 | 10 | # PDFGenerator 11 | [![Build Status](https://travis-ci.org/sgr-ksmt/PDFGenerator.svg?branch=master)](https://travis-ci.org/sgr-ksmt/PDFGenerator) 12 | [![GitHub release](https://img.shields.io/github/release/sgr-ksmt/PDFGenerator.svg)](https://github.com/sgr-ksmt/PDFGenerator/releases) 13 | [![codecov](https://codecov.io/gh/sgr-ksmt/PDFGenerator/branch/master/graph/badge.svg)](https://codecov.io/gh/sgr-ksmt/PDFGenerator) 14 | [![Language](https://img.shields.io/badge/language-Swift%204.2-orange.svg)]() 15 | [![Carthage](https://img.shields.io/badge/Carthage-✓-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 16 | [![CocoaPods](https://img.shields.io/badge/Cocoa%20Pods-✓-4BC51D.svg?style=flat)](https://cocoapods.org/pods/PDFGenerator) 17 | [![CocoaPodsDL](https://img.shields.io/cocoapods/dt/PDFGenerator.svg)]() 18 | [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/matteocrippa/awesome-swift#pdf) 19 | [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) 20 |
21 | 22 | `PDFGenerator` is a simple PDF generator that generates with `UIView`, `UIImage`, ...etc . 23 | 24 | ```swift 25 | do { 26 | let page: [PDFPage] = [ 27 | .whitePage(CGSize(width: 200.0, height: 100.0)), 28 | .image(image1) 29 | .image(image2) 30 | .imagePath(lastPageImagePath) 31 | .whitePage(CGSize(width: 200.0, height: 100.0)) 32 | ] 33 | let path = NSTemporaryDirectory().appending("sample1.pdf") 34 | try PDFGenerator.generate(page, to: path, password: "123456") 35 | } catch let error { 36 | print(error) 37 | } 38 | ``` 39 | 40 | ## Features 41 | - **Swift 5 is ready** :pray: 42 | - Multiple pages support. 43 | - Also generate PDF with `image path`, `image binary`, `image ref (CGImage)` 44 | - Good memory management. 45 | - UIScrollView support : If view is `UIScrollView`, `UITableView`, `UICollectionView`, `UIWebView`, drawn whole content. 46 | - Outputs as binary(`Data`) or writes to Disk(in given file path) directly. 47 | - Corresponding to Error-Handling. Strange PDF has never been generated!!. 48 | - DPI support. : Default dpi is 72. 49 | - Password protection support. 50 | 51 | ## Requirements 52 | - iOS 9.0+ 53 | - Xcode 11+ 54 | - Swift 5.1 55 | 56 | ## Installation 57 | 58 | ### Carthage 59 | 60 | - Add the following to your *Cartfile*: 61 | 62 | ```bash 63 | github "sgr-ksmt/PDFGenerator" ~> 3.1 64 | ``` 65 | 66 | - Then run command: 67 | 68 | ```bash 69 | $ carthage update 70 | ``` 71 | 72 | - Add the framework as described. 73 |
Details: [Carthage Readme](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) 74 | 75 | 76 | #### CocoaPods 77 | 78 | **PDFGenerator** is available through [CocoaPods](http://cocoapods.org). To install 79 | it, simply add the following line to your Podfile: 80 | 81 | ```ruby 82 | pod 'PDFGenerator', '~> 3.1' 83 | ``` 84 | 85 | and run `pod install` 86 | 87 | ## Usage 88 | 89 | ### Generate from view(s) or image(s) 90 | - UIView → PDF 91 | 92 | ```swift 93 | func generatePDF() { 94 | let v1 = UIScrollView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 100.0)) 95 | let v2 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 200.0)) 96 | let v3 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 200.0)) 97 | v1.backgroundColor = .red 98 | v1.contentSize = CGSize(width: 100.0, height: 200.0) 99 | v2.backgroundColor = .green 100 | v3.backgroundColor = .blue 101 | 102 | let dst = URL(fileURLWithPath: NSTemporaryDirectory().appending("sample1.pdf")) 103 | // outputs as Data 104 | do { 105 | let data = try PDFGenerator.generated(by: [v1, v2, v3]) 106 | try data.write(to: dst, options: .atomic) 107 | } catch (let error) { 108 | print(error) 109 | } 110 | 111 | // writes to Disk directly. 112 | do { 113 | try PDFGenerator.generate([v1, v2, v3], to: dst) 114 | } catch (let error) { 115 | print(error) 116 | } 117 | } 118 | ``` 119 | 120 | `Also PDF can generate from image(s), image path(s) same as example.` 121 | 122 | ### Generate from PDFPage object 123 | 124 | - (UIVIew or UIImage) → PDF 125 | 126 | Use `PDFPage`. 127 | 128 | ```swift 129 | public enum PDFPage { 130 | case whitePage(CGSize) // = A white view 131 | case view(UIView) 132 | case image(UIImage) 133 | case imagePath(String) 134 | case binary(Data) 135 | case imageRef(CGImage) 136 | } 137 | ``` 138 | 139 | ```swift 140 | func generatePDF() { 141 | let v1 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 100.0)) 142 | v1.backgroundColor = .red 143 | let v2 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 200.0)) 144 | v2.backgroundColor = .green 145 | 146 | let page1 = PDFPage.View(v1) 147 | let page2 = PDFPage.View(v2) 148 | let page3 = PDFPage.WhitePage(CGSizeMake(200, 100)) 149 | let page4 = PDFPage.Image(UIImage(contentsOfFile: "path/to/image1.png")!) 150 | let page5 = PDFPage.ImagePath("path/to/image2.png") 151 | let pages = [page1, page2, page3, page4, page5] 152 | 153 | let dst = NSTemporaryDirectory().appending("sample1.pdf") 154 | do { 155 | try PDFGenerator.generate(pages, to: dst) 156 | } catch (let e) { 157 | print(e) 158 | } 159 | } 160 | ``` 161 | 162 | ### Generate custom dpi PDF 163 | ```swift 164 | // generate dpi300 PDF (default: 72dpi) 165 | func generatePDF() { 166 | let v1 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 100.0)) 167 | v1.backgroundColor = .red 168 | let v2 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 200.0)) 169 | v2.backgroundColor = .green 170 | 171 | let page1 = PDFPage.View(v1) 172 | let page2 = PDFPage.View(v2) 173 | let pages = [page1, page2] 174 | 175 | let dst = NSTemporaryDirectory().appending("sample1.pdf") 176 | do { 177 | try PDFGenerator.generate(pages, to: dst, dpi: .dpi_300) 178 | } catch (let e) { 179 | print(e) 180 | } 181 | } 182 | ``` 183 | 184 | ### Password protection 185 | ```swift 186 | // generate PDF with password: 123456 187 | func generatePDF() { 188 | let v1 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 100.0)) 189 | v1.backgroundColor = .red 190 | let v2 = UIView(frame: CGRect(x: 0.0,y: 0, width: 100.0, height: 200.0)) 191 | v2.backgroundColor = .green 192 | 193 | let page1 = PDFPage.view(v1) 194 | let page2 = PDFPage.view(v2) 195 | let pages = [page1, page2] 196 | 197 | let dst = NSTemporaryDirectory().appending("sample1.pdf") 198 | do { 199 | try PDFGenerator.generate(pages, to: dst, password: "123456") 200 | // or use PDFPassword model 201 | try PDFGenerator.generate(pages, to: dst, password: PDFPassword("123456")) 202 | // or use PDFPassword model and set user/owner password 203 | try PDFGenerator.generate(pages, to: dst, password: PDFPassword(user: "123456", owner: "abcdef")) 204 | } catch let error { 205 | print(error) 206 | } 207 | } 208 | ``` 209 | 210 | ## Communication 211 | - If you found a bug, please open an issue. :bow: 212 | - Also, if you have a feature request, please open an issue. :thumbsup: 213 | - If you want to contribute, submit a pull request.:muscle: 214 | 215 | ## License 216 | 217 | **PDFGenerator** is under MIT license. See the [LICENSE](LICENSE) file for more info. 218 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - "Demo/*" 4 | - "PDFGeneratorTests/*" 5 | 6 | comment: false 7 | --------------------------------------------------------------------------------