├── UIImage+PDF.zip ├── Swift Image+PDF ├── animalsCat.pdf ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── UIImage+PDF │ ├── UIView+Image.swift │ ├── UIPDFView.swift │ ├── PDFResourceHelper.swift │ └── UIImage+PDF.swift ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── AppDelegate.swift └── ViewController.swift ├── Swift Image+PDF.xcodeproj ├── xcuserdata │ └── rb.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── Swift Image+PDF.xcscheme ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── rb.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── Swift Image+PDF.xcscmblueprint └── project.pbxproj └── README.md /UIImage+PDF.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanBambura/Swift-UIImage-PDF/HEAD/UIImage+PDF.zip -------------------------------------------------------------------------------- /Swift Image+PDF/animalsCat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanBambura/Swift-UIImage-PDF/HEAD/Swift Image+PDF/animalsCat.pdf -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/xcuserdata/rb.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/project.xcworkspace/xcuserdata/rb.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanBambura/Swift-UIImage-PDF/HEAD/Swift Image+PDF.xcodeproj/project.xcworkspace/xcuserdata/rb.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/xcuserdata/rb.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Swift Image+PDF.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 319AD2AF1C802CBC003845A4 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Swift Image+PDF/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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Swift Image+PDF/UIImage+PDF/UIView+Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Image.swift 3 | // WAPersonal 4 | // 5 | // Created by Roman Bambura on 2/25/16. 6 | // Copyright © 2016 Roman Bambura. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | var imageShot: UIImage{ 14 | 15 | get{ 16 | //self.layer.shouldRasterize = true 17 | 18 | //print(self.contentScaleFactor) 19 | //UIGraphicsBeginImageContextWithOptions( self.bounds.size, false, self.contentScaleFactor) 20 | 21 | UIGraphicsBeginImageContextWithOptions( self.bounds.size, false, 3) 22 | self.layer.render(in: UIGraphicsGetCurrentContext()!) 23 | let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!; 24 | UIGraphicsEndImageContext(); 25 | return image; 26 | } 27 | } 28 | 29 | 30 | 31 | func savePNG(_ filePath: String){ 32 | if let data = UIImagePNGRepresentation(self.imageShot){ 33 | try? data.write(to: URL(fileURLWithPath: filePath), options: [.atomic]) 34 | } 35 | } 36 | 37 | 38 | func saveJPEG(_ filePath: String, quality:CGFloat){ 39 | if let data = UIImageJPEGRepresentation(self.imageShot, quality){ 40 | try? data.write(to: URL(fileURLWithPath: filePath), options: []) 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Swift Image+PDF/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 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Swift Image+PDF/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 | -------------------------------------------------------------------------------- /Swift Image+PDF/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 | -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/project.xcworkspace/xcshareddata/Swift Image+PDF.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "A066FFF00290EBA4EC1E72C6FF51C9DEBB1D46B0", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "A066FFF00290EBA4EC1E72C6FF51C9DEBB1D46B0" : 0, 8 | "F0F16AFC1EEA4EE9481A6656D286995C37BA90C6" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "A5A39272-10E8-494C-9708-4A6B438AF1EF", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "A066FFF00290EBA4EC1E72C6FF51C9DEBB1D46B0" : "Swift-UIImage-PDF\/", 13 | "F0F16AFC1EEA4EE9481A6656D286995C37BA90C6" : "" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Swift Image+PDF", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Swift Image+PDF.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/RomanBambura\/Swift-UIImage-PDF.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "A066FFF00290EBA4EC1E72C6FF51C9DEBB1D46B0" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/RomanBambura\/Swift-Image-PDF.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F0F16AFC1EEA4EE9481A6656D286995C37BA90C6" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /Swift Image+PDF/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Swift Image+PDF 4 | // 5 | // Created by Roman Bambura on 2/26/16. 6 | // Copyright © 2016 Roman Bambura. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // 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. 24 | // 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. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Swift Image+PDF/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Swift Image+PDF 4 | // 5 | // Created by Roman Bambura on 2/26/16. 6 | // Copyright © 2016 Roman Bambura. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | 17 | // UIView From PDF Use UIPDFView For perfect image sharpness animation 18 | let pdfView = UIPDFView(withName: "animalsCat", frame: CGRect(x: 10, y: 20, width: 100, height: 100) ) 19 | pdfView.backgroundColor = UIColor.clear 20 | view.addSubview(pdfView) 21 | 22 | 23 | // UIImages From PDF With Disc Cache user/Documents/CachedAssets/ 24 | // file will be cached with name animalsCat_100X100@2x // scale 2 25 | // animalsCat_100X100@1x // scale 1 26 | 27 | // Size To Fit 28 | if let pdfImage: UIImage = UIImage.imageWithPDFNamed("animalsCat", fitSize: CGSize(width:100, height:100)){ 29 | 30 | let pdfImageView: UIImageView = UIImageView(image: pdfImage) 31 | pdfImageView.center = CGPoint(x:200, y:200) 32 | 33 | view.addSubview(pdfImageView) 34 | } 35 | 36 | 37 | if let pdfImage2: UIImage = UIImage.imageWithPDFNamed("animalsCat", size: CGSize(width:80, height:80)){ 38 | 39 | let pdfImageView2: UIImageView = UIImageView(image: pdfImage2) 40 | pdfImageView2.center = CGPoint(x:200, y:290) 41 | 42 | view.addSubview(pdfImageView2) 43 | } 44 | 45 | // With Width 46 | if let pdfImage3: UIImage = UIImage.imageWithPDFNamed("animalsCat", width: 70){ 47 | 48 | let pdfImageView3: UIImageView = UIImageView(image: pdfImage3) 49 | pdfImageView3.center = CGPoint(x:200, y:380) 50 | 51 | view.addSubview(pdfImageView3) 52 | } 53 | 54 | // With Height 55 | if let pdfImage4: UIImage = UIImage.imageWithPDFNamed("animalsCat", height: 40){ 56 | 57 | let pdfImageView4: UIImageView = UIImageView(image: pdfImage4) 58 | pdfImageView4.center = CGPoint(x:200, y:440) 59 | 60 | view.addSubview(pdfImageView4) 61 | } 62 | } 63 | 64 | override func didReceiveMemoryWarning() { 65 | super.didReceiveMemoryWarning() 66 | // Dispose of any resources that can be recreated. 67 | } 68 | 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIImage+PDF Swift 2 | UIImage+PDF provides a UIImage class category method to render a UIImage from any PDF stored in the application bundle. The motivation for this was to enable the easy use of scaleable vector assets in Swift iOS apps. 3 | 4 | #Usage 5 | Add the sources files in the UIImage+PDF sub folder to your project. 6 | 7 | # UIPDFView 8 | Use UIPDFView for animation 9 | ```swift 10 | UIPDFView(name: String, frame: CGRect) 11 | 12 | Sample: 13 | let pdfView = UIPDFView(name: "animalsCat", frame: CGRectMake(10, 20, 100, 100)) 14 | pdfView.backgroundColor = UIColor.clearColor() 15 | view.addSubview(pdfView) 16 | ``` 17 | 18 | # UIImage 19 | PDF asset name used without extension 20 | 21 | Sample: 22 | ```swift 23 | if let pdfImage: UIImage = UIImage.imageWithPDFNamed("animalsCat", fitSize: CGSizeMake(100, 100)){ 24 | 25 | let pdfImageView: UIImageView = UIImageView(image: pdfImage) 26 | pdfImageView.center = CGPointMake(200, 200) 27 | 28 | view.addSubview(pdfImageView) 29 | } 30 | ``` 31 | PDF Asset with size to fit 32 | ```swift 33 | UIImage.imageWithPDFNamed(name: String, fitSize: CGSize) 34 | ``` 35 | 36 | 37 | PDF Asset with Size 38 | ```swift 39 | UIImage.imageWithPDFNamed(name: String, size: CGSize) 40 | ``` 41 | 42 | PDF Asset with Width 43 | ```swift 44 | UIImage.imageWithPDFNamed(name: String, width: CGFloat) 45 | ``` 46 | 47 | PDF Asset with Height 48 | ```swift 49 | UIImage.imageWithPDFNamed(name: String, height: CGFloat) 50 | ``` 51 | 52 | # Disk Cacheing 53 | Cached PDF assets to PNG images with readable names and scale factor ready to reuse.
54 | UIImage+PDF now transparently caches all rendered PDFs in Application_Home/Documents/CachedAssets. This substantially improves application latency with large PDFs, especially on larger devices. To disable disk cacheing, call: 55 | ```swift 56 | UIImage.shouldCacheOnDisk = false 57 | ``` 58 | 59 | # Memory Cacheing 60 | UIImage+PDF can now use NSCache to cache rendered PDFs in memory. This feature is disabled by default. To enable it call: 61 | ```swift 62 | UIImage.shouldCacheInMemory = true 63 | ``` 64 | 65 | # PDF file size 66 | By default Adobe Illustrator saves exported PDFs very inefficiently. For best results, select File -> Save a Copy, select PDF format and then uncheck all the general options. Once you are ready to ship your app, run all your PDF assets through ShrinkIt (see below). 67 | 68 | Other vector graphics editors which natively use the OSX Quartz renderer, such as Sketch, will create much more compact PDFs. 69 | 70 | The amazing devs at Panic have now released a PDF shrinking utility, ShrinkIt, which should take a lot of the pain out of a vector asset workflow. 71 | 72 | #Licence 73 | Copyright 2016 Roman Bambura - @RomanBambura. All rights reserved. 74 | 75 | Permission is given to use this source code file without charge in any project, commercial or otherwise, entirely at your risk, with the condition that any redistribution (in part or whole) of source code must retain this copyright and permission notice. Attribution in compiled projects is appreciated but not required. 76 | 77 | #Further Reading 78 | Matt Gemmell has an excellent article on his blog explaining how to use PDF images in iOS apps. 79 | 80 | #UIImage+PDF Objective-C 81 | Nigel Timothy Barber UIImage-PDF 82 | -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/xcuserdata/rb.xcuserdatad/xcschemes/Swift Image+PDF.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Swift Image+PDF/UIImage+PDF/UIPDFView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIPDFView.swift 3 | // 4 | // 5 | // Created by Roman Bambura on 2/23/16. 6 | // Copyright © 2016 Roman Bambura. All rights reserved. 7 | // http://www.sonettic.com 8 | // 9 | 10 | import UIKit 11 | 12 | class UIPDFView: UIView { 13 | 14 | let page = 1 15 | var m_resourceName:String? 16 | var m_resourceURL:URL? 17 | var m_resourceData:Data? 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | super.init(coder: aDecoder) 21 | } 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | } 26 | 27 | init(withName name: String, frame: CGRect) { 28 | super.init(frame: frame) 29 | 30 | m_resourceName = name + ".pdf" 31 | 32 | m_resourceURL = PDFResourceHelper.resourceURLForName(m_resourceName!) 33 | 34 | } 35 | 36 | // Resource Name 37 | var resourceName:String?{ 38 | 39 | set{ 40 | m_resourceName = newValue 41 | 42 | self.setNeedsDisplay() 43 | } 44 | get{ 45 | return m_resourceName 46 | } 47 | } 48 | 49 | // Resource URL 50 | var resourceURL:URL?{ 51 | 52 | set{ 53 | m_resourceURL = newValue 54 | 55 | self.setNeedsDisplay() 56 | } 57 | get{ 58 | return m_resourceURL 59 | } 60 | } 61 | 62 | // Resource Data 63 | var resourceData:Data?{ 64 | set{ 65 | m_resourceData = newValue; 66 | 67 | self.setNeedsDisplay() 68 | } 69 | get{ 70 | return m_resourceData 71 | } 72 | } 73 | 74 | 75 | static func pageCountForURL(_ resourceURL: URL?) -> Int{ 76 | 77 | var pageCount = 1; 78 | 79 | if resourceURL != nil 80 | { 81 | if let document: CGPDFDocument = CGPDFDocument( resourceURL as! CFURL){ 82 | 83 | pageCount = document.numberOfPages; 84 | } 85 | } 86 | 87 | return pageCount; 88 | } 89 | 90 | 91 | func renderIntoContext(_ ctx: CGContext, url resourceURL: URL?, data resourceData:Data?, size: CGSize, page:Int, preserveAspectRatio:Bool){ 92 | 93 | var document: CGPDFDocument? 94 | 95 | if resourceURL != nil 96 | { 97 | document = CGPDFDocument( resourceURL as! CFURL )! 98 | } 99 | else if resourceData != nil 100 | { 101 | if let provider: CGDataProvider = CGDataProvider( data: resourceData as! CFData ) 102 | { 103 | document = CGPDFDocument( provider )! 104 | } 105 | } 106 | 107 | if let page1: CGPDFPage = document?.page(at: page ){ 108 | 109 | let destRect: CGRect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 110 | 111 | ctx.fill(destRect); 112 | ctx.translateBy(x: 0.0, y: destRect.size.height); 113 | ctx.scaleBy(x: 1.0, y: -1.0); 114 | ctx.concatenate(page1.getDrawingTransform(CGPDFBox.cropBox, rect: destRect, rotate: 0, preserveAspectRatio: preserveAspectRatio)); 115 | ctx.drawPDFPage(page1); 116 | } 117 | } 118 | 119 | // Only override drawRect: if you perform custom drawing. 120 | // An empty implementation adversely affects performance during animation. 121 | override func draw(_ rect: CGRect){ 122 | 123 | if let ctx: CGContext = UIGraphicsGetCurrentContext() 124 | { 125 | self.backgroundColor?.set() 126 | ctx.fill(rect ) 127 | layer.render(in: ctx) 128 | renderIntoContext(ctx, url:resourceURL, data:resourceData, size:rect.size, page:page, preserveAspectRatio:true) 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Swift Image+PDF/UIImage+PDF/PDFResourceHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFResourceHelper.swift 3 | // WAPersonal 4 | // 5 | // Created by Roman Bambura on 2/25/16. 6 | // Copyright © 2016 Roman Bambura. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PDFResourceHelper { 12 | 13 | static func resourceURLForName(_ resourceName:String?) -> URL?{ 14 | 15 | if let path = Bundle.main.path(forResource: resourceName , ofType: nil){ 16 | return URL(fileURLWithPath:path) 17 | } 18 | return nil 19 | } 20 | 21 | static func mediaRect(_ resourceName: String?) -> CGRect 22 | { 23 | return self.mediaRectForURL(self.resourceURLForName(resourceName)!) 24 | } 25 | 26 | static func mediaRectForURL(_ resourceURL: URL) -> CGRect 27 | { 28 | return mediaRectForURL(resourceURL, page:1) 29 | } 30 | 31 | 32 | 33 | 34 | static func mediaRectForURL(_ resourceURL: URL?, page: Int)-> CGRect{ 35 | 36 | var rect:CGRect = CGRect.null 37 | 38 | if resourceURL != nil 39 | { 40 | if let pdf:CGPDFDocument = CGPDFDocument(resourceURL as! CFURL) 41 | { 42 | 43 | if let page1:CGPDFPage = pdf.page(at: page) 44 | { 45 | 46 | rect = page1.getBoxRect(CGPDFBox.cropBox) 47 | 48 | let rotationAngle = page1.rotationAngle 49 | 50 | if (rotationAngle == 90 || rotationAngle == 270) 51 | { 52 | let temp = rect.size.width 53 | rect.size.width = rect.size.height 54 | rect.size.height = temp 55 | } 56 | } 57 | } 58 | } 59 | 60 | return rect; 61 | } 62 | 63 | static func renderIntoContext(_ ctx: CGContext, url resourceURL: URL?, data resourceData:Data?, size: CGSize, page:Int, preserveAspectRatio:Bool){ 64 | 65 | var document: CGPDFDocument? 66 | 67 | if resourceURL != nil 68 | { 69 | document = CGPDFDocument( resourceURL as! CFURL )! 70 | } 71 | else if resourceData != nil 72 | { 73 | if let provider: CGDataProvider = CGDataProvider( data: resourceData as! CFData ) 74 | { 75 | document = CGPDFDocument( provider )! 76 | } 77 | } 78 | 79 | if let page1: CGPDFPage = document?.page(at: page ){ 80 | 81 | let destRect: CGRect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 82 | 83 | let drawingTransform: CGAffineTransform = page1.getDrawingTransform(CGPDFBox.cropBox, rect: destRect, rotate: 0, preserveAspectRatio: preserveAspectRatio); 84 | ctx.concatenate(drawingTransform) 85 | ctx.drawPDFPage(page1 ) 86 | } 87 | } 88 | 89 | static func mediaRectForData(_ data: Data?, page: Int) -> CGRect{ 90 | 91 | var rect:CGRect = CGRect.null 92 | 93 | if data != nil 94 | { 95 | if let provider:CGDataProvider = CGDataProvider( data: data as! CFData ) 96 | { 97 | 98 | if let document:CGPDFDocument = CGPDFDocument( provider ){ 99 | 100 | if let page1:CGPDFPage = document.page(at: page ) 101 | { 102 | 103 | rect = page1.getBoxRect(CGPDFBox.cropBox ) 104 | 105 | let rotationAngle = page1.rotationAngle 106 | 107 | if (rotationAngle == 90 || rotationAngle == 270) 108 | { 109 | let temp = rect.size.width 110 | rect.size.width = rect.size.height 111 | rect.size.height = temp 112 | } 113 | } 114 | } 115 | } 116 | } 117 | 118 | return rect; 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Swift Image+PDF.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 319AD2B41C802CBC003845A4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2B31C802CBC003845A4 /* AppDelegate.swift */; }; 11 | 319AD2B61C802CBC003845A4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2B51C802CBC003845A4 /* ViewController.swift */; }; 12 | 319AD2B91C802CBC003845A4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319AD2B71C802CBC003845A4 /* Main.storyboard */; }; 13 | 319AD2BB1C802CBC003845A4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 319AD2BA1C802CBC003845A4 /* Assets.xcassets */; }; 14 | 319AD2BE1C802CBC003845A4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319AD2BC1C802CBC003845A4 /* LaunchScreen.storyboard */; }; 15 | 319AD2CE1C802D74003845A4 /* PDFResourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2CA1C802D74003845A4 /* PDFResourceHelper.swift */; }; 16 | 319AD2CF1C802D74003845A4 /* UIImage+PDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2CB1C802D74003845A4 /* UIImage+PDF.swift */; }; 17 | 319AD2D01C802D74003845A4 /* UIPDFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2CC1C802D74003845A4 /* UIPDFView.swift */; }; 18 | 319AD2D11C802D74003845A4 /* UIView+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319AD2CD1C802D74003845A4 /* UIView+Image.swift */; }; 19 | 319AD2D31C80395C003845A4 /* animalsCat.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 319AD2D21C80395C003845A4 /* animalsCat.pdf */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 319AD2B01C802CBC003845A4 /* Swift Image+PDF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Swift Image+PDF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 319AD2B31C802CBC003845A4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | 319AD2B51C802CBC003845A4 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | 319AD2B81C802CBC003845A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | 319AD2BA1C802CBC003845A4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | 319AD2BD1C802CBC003845A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | 319AD2BF1C802CBC003845A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | 319AD2CA1C802D74003845A4 /* PDFResourceHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFResourceHelper.swift; sourceTree = ""; }; 31 | 319AD2CB1C802D74003845A4 /* UIImage+PDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+PDF.swift"; sourceTree = ""; }; 32 | 319AD2CC1C802D74003845A4 /* UIPDFView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIPDFView.swift; sourceTree = ""; }; 33 | 319AD2CD1C802D74003845A4 /* UIView+Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Image.swift"; sourceTree = ""; }; 34 | 319AD2D21C80395C003845A4 /* animalsCat.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = animalsCat.pdf; path = "Swift Image+PDF/animalsCat.pdf"; sourceTree = SOURCE_ROOT; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 319AD2AD1C802CBC003845A4 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 319AD2A71C802CBC003845A4 = { 49 | isa = PBXGroup; 50 | children = ( 51 | 319AD2B21C802CBC003845A4 /* Swift Image+PDF */, 52 | 319AD2B11C802CBC003845A4 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 319AD2B11C802CBC003845A4 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 319AD2B01C802CBC003845A4 /* Swift Image+PDF.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | 319AD2B21C802CBC003845A4 /* Swift Image+PDF */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 319AD2B31C802CBC003845A4 /* AppDelegate.swift */, 68 | 319AD2B51C802CBC003845A4 /* ViewController.swift */, 69 | 319AD2C91C802D74003845A4 /* UIImage+PDF */, 70 | 319AD2BA1C802CBC003845A4 /* Assets.xcassets */, 71 | 319AD2B71C802CBC003845A4 /* Main.storyboard */, 72 | 319AD2BC1C802CBC003845A4 /* LaunchScreen.storyboard */, 73 | 319AD2BF1C802CBC003845A4 /* Info.plist */, 74 | ); 75 | path = "Swift Image+PDF"; 76 | sourceTree = ""; 77 | }; 78 | 319AD2C91C802D74003845A4 /* UIImage+PDF */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 319AD2CA1C802D74003845A4 /* PDFResourceHelper.swift */, 82 | 319AD2CD1C802D74003845A4 /* UIView+Image.swift */, 83 | 319AD2CC1C802D74003845A4 /* UIPDFView.swift */, 84 | 319AD2CB1C802D74003845A4 /* UIImage+PDF.swift */, 85 | 319AD2D21C80395C003845A4 /* animalsCat.pdf */, 86 | ); 87 | path = "UIImage+PDF"; 88 | sourceTree = ""; 89 | }; 90 | /* End PBXGroup section */ 91 | 92 | /* Begin PBXNativeTarget section */ 93 | 319AD2AF1C802CBC003845A4 /* Swift Image+PDF */ = { 94 | isa = PBXNativeTarget; 95 | buildConfigurationList = 319AD2C21C802CBC003845A4 /* Build configuration list for PBXNativeTarget "Swift Image+PDF" */; 96 | buildPhases = ( 97 | 319AD2AC1C802CBC003845A4 /* Sources */, 98 | 319AD2AD1C802CBC003845A4 /* Frameworks */, 99 | 319AD2AE1C802CBC003845A4 /* Resources */, 100 | ); 101 | buildRules = ( 102 | ); 103 | dependencies = ( 104 | ); 105 | name = "Swift Image+PDF"; 106 | productName = "Swift Image+PDF"; 107 | productReference = 319AD2B01C802CBC003845A4 /* Swift Image+PDF.app */; 108 | productType = "com.apple.product-type.application"; 109 | }; 110 | /* End PBXNativeTarget section */ 111 | 112 | /* Begin PBXProject section */ 113 | 319AD2A81C802CBC003845A4 /* Project object */ = { 114 | isa = PBXProject; 115 | attributes = { 116 | LastSwiftUpdateCheck = 0730; 117 | LastUpgradeCheck = 0820; 118 | ORGANIZATIONNAME = "Roman Bambura"; 119 | TargetAttributes = { 120 | 319AD2AF1C802CBC003845A4 = { 121 | CreatedOnToolsVersion = 7.3; 122 | LastSwiftMigration = 0820; 123 | }; 124 | }; 125 | }; 126 | buildConfigurationList = 319AD2AB1C802CBC003845A4 /* Build configuration list for PBXProject "Swift Image+PDF" */; 127 | compatibilityVersion = "Xcode 3.2"; 128 | developmentRegion = English; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | en, 132 | Base, 133 | ); 134 | mainGroup = 319AD2A71C802CBC003845A4; 135 | productRefGroup = 319AD2B11C802CBC003845A4 /* Products */; 136 | projectDirPath = ""; 137 | projectRoot = ""; 138 | targets = ( 139 | 319AD2AF1C802CBC003845A4 /* Swift Image+PDF */, 140 | ); 141 | }; 142 | /* End PBXProject section */ 143 | 144 | /* Begin PBXResourcesBuildPhase section */ 145 | 319AD2AE1C802CBC003845A4 /* Resources */ = { 146 | isa = PBXResourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 319AD2BE1C802CBC003845A4 /* LaunchScreen.storyboard in Resources */, 150 | 319AD2BB1C802CBC003845A4 /* Assets.xcassets in Resources */, 151 | 319AD2B91C802CBC003845A4 /* Main.storyboard in Resources */, 152 | 319AD2D31C80395C003845A4 /* animalsCat.pdf in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | 319AD2AC1C802CBC003845A4 /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 319AD2B61C802CBC003845A4 /* ViewController.swift in Sources */, 164 | 319AD2D01C802D74003845A4 /* UIPDFView.swift in Sources */, 165 | 319AD2B41C802CBC003845A4 /* AppDelegate.swift in Sources */, 166 | 319AD2CF1C802D74003845A4 /* UIImage+PDF.swift in Sources */, 167 | 319AD2D11C802D74003845A4 /* UIView+Image.swift in Sources */, 168 | 319AD2CE1C802D74003845A4 /* PDFResourceHelper.swift in Sources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXSourcesBuildPhase section */ 173 | 174 | /* Begin PBXVariantGroup section */ 175 | 319AD2B71C802CBC003845A4 /* Main.storyboard */ = { 176 | isa = PBXVariantGroup; 177 | children = ( 178 | 319AD2B81C802CBC003845A4 /* Base */, 179 | ); 180 | name = Main.storyboard; 181 | sourceTree = ""; 182 | }; 183 | 319AD2BC1C802CBC003845A4 /* LaunchScreen.storyboard */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | 319AD2BD1C802CBC003845A4 /* Base */, 187 | ); 188 | name = LaunchScreen.storyboard; 189 | sourceTree = ""; 190 | }; 191 | /* End PBXVariantGroup section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | 319AD2C01C802CBC003845A4 /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | CLANG_ANALYZER_NONNULL = YES; 199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 200 | CLANG_CXX_LIBRARY = "libc++"; 201 | CLANG_ENABLE_MODULES = YES; 202 | CLANG_ENABLE_OBJC_ARC = YES; 203 | CLANG_WARN_BOOL_CONVERSION = YES; 204 | CLANG_WARN_CONSTANT_CONVERSION = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INFINITE_RECURSION = YES; 209 | CLANG_WARN_INT_CONVERSION = YES; 210 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNREACHABLE_CODE = YES; 213 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 214 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 215 | COPY_PHASE_STRIP = NO; 216 | DEBUG_INFORMATION_FORMAT = dwarf; 217 | ENABLE_STRICT_OBJC_MSGSEND = YES; 218 | ENABLE_TESTABILITY = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu99; 220 | GCC_DYNAMIC_NO_PIC = NO; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_OPTIMIZATION_LEVEL = 0; 223 | GCC_PREPROCESSOR_DEFINITIONS = ( 224 | "DEBUG=1", 225 | "$(inherited)", 226 | ); 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 234 | MTL_ENABLE_DEBUG_INFO = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = iphoneos; 237 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 238 | TARGETED_DEVICE_FAMILY = "1,2"; 239 | }; 240 | name = Debug; 241 | }; 242 | 319AD2C11C802CBC003845A4 /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INFINITE_RECURSION = YES; 257 | CLANG_WARN_INT_CONVERSION = YES; 258 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Release; 283 | }; 284 | 319AD2C31C802CBC003845A4 /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 288 | INFOPLIST_FILE = "Swift Image+PDF/Info.plist"; 289 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 290 | PRODUCT_BUNDLE_IDENTIFIER = "sonettic.Swift-Image-PDF"; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | SWIFT_VERSION = 3.0; 293 | }; 294 | name = Debug; 295 | }; 296 | 319AD2C41C802CBC003845A4 /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 300 | INFOPLIST_FILE = "Swift Image+PDF/Info.plist"; 301 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 302 | PRODUCT_BUNDLE_IDENTIFIER = "sonettic.Swift-Image-PDF"; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_VERSION = 3.0; 305 | }; 306 | name = Release; 307 | }; 308 | /* End XCBuildConfiguration section */ 309 | 310 | /* Begin XCConfigurationList section */ 311 | 319AD2AB1C802CBC003845A4 /* Build configuration list for PBXProject "Swift Image+PDF" */ = { 312 | isa = XCConfigurationList; 313 | buildConfigurations = ( 314 | 319AD2C01C802CBC003845A4 /* Debug */, 315 | 319AD2C11C802CBC003845A4 /* Release */, 316 | ); 317 | defaultConfigurationIsVisible = 0; 318 | defaultConfigurationName = Release; 319 | }; 320 | 319AD2C21C802CBC003845A4 /* Build configuration list for PBXNativeTarget "Swift Image+PDF" */ = { 321 | isa = XCConfigurationList; 322 | buildConfigurations = ( 323 | 319AD2C31C802CBC003845A4 /* Debug */, 324 | 319AD2C41C802CBC003845A4 /* Release */, 325 | ); 326 | defaultConfigurationIsVisible = 0; 327 | defaultConfigurationName = Release; 328 | }; 329 | /* End XCConfigurationList section */ 330 | }; 331 | rootObject = 319AD2A81C802CBC003845A4 /* Project object */; 332 | } 333 | -------------------------------------------------------------------------------- /Swift Image+PDF/UIImage+PDF/UIImage+PDF.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+PDF.swift 3 | // 4 | // Created by Roman Bambura on 2/23/16. 5 | // Copyright © 2016 Roman Bambura. All rights reserved. 6 | // http://www.sonettic.com 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | 13 | // MARK: Control cache 14 | fileprivate static var _imagesCache: NSCache? 15 | fileprivate static var _shouldCache: Bool = false 16 | fileprivate static var _shouldCacheOnDisk: Bool = true 17 | fileprivate static var _assetName: String? 18 | fileprivate static var _resourceName: String? 19 | 20 | 21 | static var cachedAssetsDirectory: String{ 22 | get{ 23 | return "CachedAssets" 24 | } 25 | } 26 | 27 | static var resourceName: String?{ 28 | 29 | set{ 30 | _resourceName = newValue 31 | _assetName = newValue!.components(separatedBy: ".")[0] 32 | } 33 | get{ 34 | return _resourceName 35 | } 36 | } 37 | 38 | static var assetName: String? { 39 | set{ 40 | _assetName = newValue 41 | _resourceName = _assetName! + ".pdf" 42 | } 43 | get{ 44 | return _assetName 45 | } 46 | } 47 | 48 | 49 | static var shouldCacheInMemory:Bool{ 50 | 51 | set{ 52 | _shouldCache = newValue 53 | 54 | if( _shouldCache && _imagesCache == nil) 55 | { 56 | _imagesCache = NSCache() 57 | } 58 | } 59 | get{ 60 | return _shouldCache 61 | } 62 | } 63 | 64 | static var shouldCacheOnDisk: Bool { 65 | 66 | set{ 67 | _shouldCacheOnDisk = newValue; 68 | } 69 | get{ 70 | return _shouldCacheOnDisk 71 | } 72 | 73 | } 74 | 75 | 76 | // Mark: Public Func 77 | 78 | // Mark: Convenience methods 79 | class func pdfAssetNamed(_ name: String) -> UIImage?{ 80 | 81 | assetName = name 82 | 83 | return self.originalSizeImageWithPDFNamed(resourceName!) 84 | } 85 | 86 | class func pdfAssetWithContentsOfFile(_ path: String) -> UIImage?{ 87 | return self.originalSizeImageWithPDFURL( URL(fileURLWithPath: path)) 88 | } 89 | 90 | class func screenScale() -> CGFloat{ 91 | return UIScreen.main.scale 92 | } 93 | 94 | 95 | // Mark: Get UIImage With PDF Name Without Extension 96 | 97 | // Mark: UIImage With Size 98 | class func imageWithPDFNamed(_ name: String, size:CGSize) -> UIImage? { 99 | 100 | assetName = name 101 | 102 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName)!, size:size) 103 | } 104 | 105 | // Mark: UIImage With Width 106 | class func imageWithPDFNamed(_ name: String, width:CGFloat) -> UIImage?{ 107 | 108 | assetName = name 109 | 110 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName), width: width) 111 | } 112 | 113 | // Mark: UIImage With Height 114 | class func imageWithPDFNamed(_ name: String, height:CGFloat) -> UIImage?{ 115 | 116 | assetName = name 117 | 118 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName), height: height) 119 | } 120 | 121 | // Mark: UIImage Size To Fit 122 | class func imageWithPDFNamed(_ name: String, fitSize size: CGSize) -> UIImage? { 123 | 124 | assetName = name 125 | 126 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(_resourceName), fitSize:size) 127 | } 128 | 129 | 130 | 131 | // Mark: Resource name 132 | // Size 133 | fileprivate class func imageWithPDFNamed(_ name: String, size: CGSize, page: Int) -> UIImage? { 134 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName!), size:size, page:page) 135 | } 136 | 137 | // Width 138 | fileprivate class func imageWithPDFNamed(_ name: String, width:CGFloat, page: Int) -> UIImage?{ 139 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName), width: width, page: page) 140 | } 141 | 142 | // Height 143 | fileprivate class func imageWithPDFNamed( _ name: String, height:CGFloat, page: Int) -> UIImage?{ 144 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(resourceName), height : height, page: page) 145 | } 146 | 147 | // Fit 148 | fileprivate class func imageWithPDFNamed(_ name: String, fitSize size: CGSize, page: Int) -> UIImage? { 149 | return self.imageWithPDFURL( PDFResourceHelper.resourceURLForName(name), fitSize:size, page:page) 150 | } 151 | 152 | // Original Size 153 | fileprivate class func originalSizeImageWithPDFNamed(_ resourceName: String, page: Int) -> UIImage?{ 154 | return self.originalSizeImageWithPDFURL( PDFResourceHelper.resourceURLForName( resourceName ), page:page) 155 | } 156 | 157 | fileprivate class func originalSizeImageWithPDFNamed(_ resourceName: String) -> UIImage?{ 158 | return self.originalSizeImageWithPDFURL( PDFResourceHelper.resourceURLForName( resourceName) ) 159 | } 160 | 161 | 162 | // Mark: Resource Data 163 | class func originalSizeImageWithPDFData( _ data: Data ) -> UIImage? { 164 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForData(data, page:1) 165 | return self.imageWithPDFData(data, size:mediaRect.size, page:1 ) 166 | } 167 | 168 | class func imageWithPDFData(_ data: Data, width:CGFloat) -> UIImage?{ 169 | return self.imageWithPDFData(data, width:width, page:1) 170 | } 171 | 172 | class func imageWithPDFData(_ data: Data?, width:CGFloat, page:Int) -> UIImage?{ 173 | 174 | if ( data == nil ){ 175 | return UIImage() 176 | } 177 | 178 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForData(data, page:page) 179 | let aspectRatio: CGFloat = mediaRect.size.width / mediaRect.size.height 180 | let size: CGSize = CGSize( width: width, height: ceil( width / aspectRatio )) 181 | 182 | return self.imageWithPDFData(data, size:size, page:page) 183 | } 184 | 185 | 186 | class func imageWithPDFData(_ data: Data?, height:CGFloat) -> UIImage? { 187 | return self.imageWithPDFData(data, height:height, page:1) 188 | } 189 | 190 | class func imageWithPDFData(_ data: Data?, height:CGFloat, page:Int) -> UIImage?{ 191 | 192 | if ( data == nil ){ 193 | return UIImage() 194 | } 195 | 196 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForData(data, page:page) 197 | let aspectRatio: CGFloat = mediaRect.size.width / mediaRect.size.height 198 | let size: CGSize = CGSize( width: ceil( height / aspectRatio ), height: height) 199 | 200 | return self.imageWithPDFData(data, size:size, page:page) 201 | } 202 | 203 | class func imageWithPDFData( _ data: Data?, fitSize size:CGSize) -> UIImage? { 204 | return self.imageWithPDFData(data, fitSize:size, page:1) 205 | } 206 | 207 | class func imageWithPDFData(_ data: Data?, fitSize size: CGSize, page: Int) -> UIImage? { 208 | return self.imageWithPDFData(data, size:size, page:page, preserveAspectRatio:true) 209 | } 210 | 211 | class func imageWithPDFData(_ data: Data?, size: CGSize ) -> UIImage? { 212 | return self.imageWithPDFData(data, size:size, page:1) 213 | } 214 | 215 | class func imageWithPDFData( _ data: Data?, size: CGSize, page: Int) -> UIImage? { 216 | return self.imageWithPDFData(data, size:size, page:page, preserveAspectRatio:false) 217 | } 218 | 219 | class func imageWithPDFData( _ data: Data?, size: CGSize, page: Int, preserveAspectRatio: Bool) -> UIImage?{ 220 | 221 | if(data == nil || size.equalTo(CGSize.zero) || page == 0){ 222 | return UIImage(); 223 | } 224 | 225 | var pdfImage: UIImage? 226 | 227 | let cacheFilename: String = self.cacheFileNameForResourceNamed(self.assetName!, size: size) 228 | let cacheFilePath: String = self.cacheFilePathForResourceNamed(cacheFilename) 229 | 230 | if(_shouldCacheOnDisk && FileManager.default.fileExists(atPath: cacheFilePath)) 231 | { 232 | pdfImage = UIImage(contentsOfFile: cacheFilePath) 233 | } 234 | else 235 | { 236 | 237 | let screenScale: CGFloat = UIScreen.main.scale 238 | let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() 239 | let ctx: CGContext = CGContext(data: nil, width: Int(size.width * screenScale), height: Int(size.height * screenScale), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo().rawValue)! 240 | ctx.scaleBy(x: screenScale, y: screenScale); 241 | 242 | PDFResourceHelper.renderIntoContext(ctx, url:nil, data:data, size:size, page:page, preserveAspectRatio:preserveAspectRatio) 243 | if let image: CGImage = ctx.makeImage(){ 244 | pdfImage = UIImage(cgImage: image, scale: screenScale, orientation: UIImageOrientation.up) 245 | } 246 | 247 | if(_shouldCacheOnDisk) 248 | { 249 | if let data = UIImagePNGRepresentation( pdfImage! ) { 250 | try? data.write(to: URL(fileURLWithPath: cacheFilePath), options: []) 251 | } 252 | } 253 | } 254 | 255 | /** 256 | * Cache image to in memory if active 257 | */ 258 | if (pdfImage != nil && _shouldCache) 259 | { 260 | _imagesCache?.setObject(pdfImage!, forKey: cacheFilename as AnyObject) 261 | } 262 | 263 | return pdfImage; 264 | } 265 | 266 | // Mark: Resource URLs 267 | 268 | class func imageWithPDFURL(_ URL: Foundation.URL?, size: CGSize, page:Int) -> UIImage?{ 269 | return self.imageWithPDFURL(URL, size:size, page:page, preserveAspectRatio:false) 270 | } 271 | 272 | class func imageWithPDFURL(_ URL: Foundation.URL?, size:CGSize, page: Int, preserveAspectRatio:Bool) -> UIImage? { 273 | 274 | if(URL == nil || size.equalTo(CGSize.zero) || page == 0){ 275 | return nil 276 | } 277 | 278 | var pdfImage: UIImage? 279 | 280 | let cacheFilename: String = self.cacheFileNameForResourceNamed(self.assetName!, size: size) 281 | let cacheFilePath: String = self.cacheFilePathForResourceNamed(cacheFilename) 282 | 283 | /** 284 | * Check in Memory cached image before checking file system 285 | */ 286 | if (_shouldCache) 287 | { 288 | pdfImage = _imagesCache!.object(forKey: cacheFilename as AnyObject) as? UIImage 289 | 290 | if (pdfImage != nil) { 291 | return pdfImage 292 | } 293 | } 294 | 295 | if(_shouldCacheOnDisk && FileManager.default.fileExists(atPath: cacheFilePath)) 296 | { 297 | pdfImage = UIImage(contentsOfFile: cacheFilePath) 298 | 299 | } 300 | else 301 | { 302 | 303 | let screenScale: CGFloat = UIScreen.main.scale 304 | let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() 305 | let ctx: CGContext = CGContext(data: nil, width: Int(size.width * screenScale), height: Int(size.height * screenScale), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo().rawValue)! 306 | ctx.scaleBy(x: screenScale, y: screenScale); 307 | 308 | PDFResourceHelper.renderIntoContext(ctx, url:URL, data:nil, size:size, page:page, preserveAspectRatio:preserveAspectRatio) 309 | if let image: CGImage = ctx.makeImage(){ 310 | pdfImage = UIImage(cgImage: image, scale: screenScale, orientation: UIImageOrientation.up) 311 | } 312 | 313 | if(_shouldCacheOnDisk) 314 | { 315 | if let data = UIImagePNGRepresentation( pdfImage! ) { 316 | try? data.write(to: Foundation.URL(fileURLWithPath: cacheFilePath), options: []) 317 | } 318 | } 319 | } 320 | 321 | /** 322 | * Cache image to in memory if active 323 | */ 324 | if (pdfImage != nil && _shouldCache) 325 | { 326 | _imagesCache?.setObject(pdfImage!, forKey: cacheFilename as AnyObject) 327 | } 328 | 329 | return pdfImage; 330 | } 331 | 332 | class func imageWithPDFURL(_ URL: Foundation.URL?, size: CGSize) -> UIImage?{ 333 | return self.imageWithPDFURL(URL, size:size, page:1, preserveAspectRatio:false) 334 | } 335 | 336 | fileprivate class func imageWithPDFURL(_ URL: Foundation.URL?, fitSize size: CGSize, page: Int) -> UIImage?{ 337 | return self.imageWithPDFURL(URL, size:size, page:page, preserveAspectRatio:true) 338 | } 339 | 340 | class func imageWithPDFURL(_ URL: Foundation.URL?, fitSize size: CGSize) -> UIImage?{ 341 | return self.imageWithPDFURL(URL, fitSize:size, page:1) 342 | } 343 | 344 | class func imageWithPDFURL(_ URL: Foundation.URL?, width: CGFloat, page: Int) -> UIImage?{ 345 | 346 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForURL(URL, page:page) 347 | let aspectRatio: CGFloat = mediaRect.size.width / mediaRect.size.height; 348 | 349 | let size: CGSize = CGSize( width: width, height: ceil( width / aspectRatio )); 350 | 351 | return self.imageWithPDFURL(URL, size:size, page:page) 352 | } 353 | 354 | class func imageWithPDFURL(_ URL: Foundation.URL?, width: CGFloat) -> UIImage? { 355 | return self.imageWithPDFURL(URL, width:width, page:1) 356 | } 357 | 358 | class func imageWithPDFURL(_ URL: Foundation.URL?, height: CGFloat, page: Int) -> UIImage? { 359 | 360 | if ( URL == nil ){ 361 | return nil 362 | } 363 | 364 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForURL(URL, page:page) 365 | let aspectRatio: CGFloat = mediaRect.size.width / mediaRect.size.height; 366 | let size: CGSize = CGSize( width: ceil( height * aspectRatio ), height: height ); 367 | 368 | return self.imageWithPDFURL(URL, size:size, page:page) 369 | } 370 | 371 | class func imageWithPDFURL(_ URL: Foundation.URL?, height: CGFloat) -> UIImage? { 372 | return self.imageWithPDFURL(URL, height:height, page:1) 373 | } 374 | 375 | class func originalSizeImageWithPDFURL( _ URL: Foundation.URL?, page: Int) -> UIImage? { 376 | 377 | if ( URL == nil ){ 378 | return nil 379 | } 380 | 381 | let mediaRect: CGRect = PDFResourceHelper.mediaRectForURL(URL, page:page) 382 | 383 | return self.imageWithPDFURL(URL, size:mediaRect.size, page:page, preserveAspectRatio:true) 384 | } 385 | 386 | class func originalSizeImageWithPDFURL(_ URL: Foundation.URL?) -> UIImage? { 387 | return self.originalSizeImageWithPDFURL(URL, page:1) 388 | } 389 | 390 | // Mark: Cacheing 391 | fileprivate class func cacheFileNameForResourceNamed(_ resourceName: String, size: CGSize) -> String{ 392 | return String(format: "%@_%dX%d@%dx",resourceName, Int(size.width), Int(size.height), Int(self.screenScale()) ) 393 | } 394 | 395 | fileprivate class func cacheFilePathForResourceNamed(_ resourceName: String, size: CGSize) -> String{ 396 | let fileName: String = self.cacheFileNameForResourceNamed(resourceName, size: size) 397 | return self.cacheFilePathForResourceNamed(fileName) 398 | } 399 | 400 | fileprivate class func cacheFilePathForResourceNamed(_ cacheResourseName: String) -> String{ 401 | 402 | let fileManager: FileManager = FileManager.default 403 | let documentsDirectoryPath: NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString 404 | let cacheDirectory = String(format: "%@/%@", documentsDirectoryPath, cachedAssetsDirectory) 405 | do{ 406 | try fileManager.createDirectory(atPath: cacheDirectory, withIntermediateDirectories: true, attributes: nil) 407 | }catch{ 408 | print ("CACHES DIRECTORY IMAGE+PDF CAN'T BE CREATED!") 409 | } 410 | 411 | return String(format:"%@/%@.png", cacheDirectory, cacheResourseName) 412 | } 413 | 414 | } 415 | 416 | --------------------------------------------------------------------------------