├── LICENSE ├── .gitignore └── PDFDocument.swift /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Amir Abbas Mousavian 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 | -------------------------------------------------------------------------------- /.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 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /PDFDocument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFDocuments.swift 3 | // ExtDownloader 4 | // 5 | // Created by Amir Abbas Mousavian on 4/7/95. 6 | // Copyright © 1395 Mousavian. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | #if os(iOS) 12 | import UIKit 13 | typealias PDFDocumentImageClass = UIImage 14 | #elseif os(OSX) 15 | import Cocoa 16 | typealias PDFDocumentImageClass = NSImage 17 | #endif 18 | 19 | class PDFDocument { 20 | let reference: CGPDFDocument 21 | let data: Data? 22 | 23 | var pagesCount: Int { 24 | return reference.numberOfPages 25 | } 26 | 27 | var allowsCopying: Bool { 28 | return reference.allowsCopying 29 | } 30 | 31 | var allowsPrinting: Bool { 32 | return reference.allowsPrinting 33 | } 34 | 35 | var isEncrypted: Bool { 36 | return reference.isEncrypted 37 | } 38 | 39 | var isUnlocked: Bool { 40 | return reference.isUnlocked 41 | } 42 | 43 | var version: (major: Int, minor: Int) { 44 | var majorVersion: Int32 = 0; 45 | var minorVersion: Int32 = 0; 46 | reference.getVersion(majorVersion: &majorVersion, minorVersion: &minorVersion); 47 | return (Int(majorVersion), Int(minorVersion)) 48 | } 49 | 50 | fileprivate func getKey(_ key: String, from dict: CGPDFDictionaryRef) -> String? { 51 | var cfValue: CGPDFStringRef? = nil 52 | if (CGPDFDictionaryGetString(dict, key, &cfValue)), let value = CGPDFStringCopyTextString(cfValue!) { 53 | return value as String 54 | } 55 | return nil 56 | } 57 | 58 | var pages: [PDFPage] = [] 59 | var title: String? 60 | var author: String? 61 | var creator: String? 62 | var subject: String? 63 | var creationDate: Date? 64 | var modifiedDate: Date? 65 | 66 | dynamic func updateFields() { 67 | func convertDate(_ date: String) -> Date? { 68 | var dateStr = date 69 | if dateStr.hasPrefix("D:") { 70 | dateStr = date.substring(from: date.characters.index(date.startIndex, offsetBy: 2)) 71 | } 72 | let dateFormatter = DateFormatter() 73 | dateFormatter.dateFormat = "yyyyMMddHHmmssTZD" 74 | if let result = dateFormatter.date(from: dateStr) { 75 | return result 76 | } 77 | dateFormatter.dateFormat = "yyyyMMddHHmmss" 78 | if let result = dateFormatter.date(from: dateStr) { 79 | return result 80 | } 81 | return nil 82 | } 83 | 84 | guard let dict = reference.info else { return } 85 | self.title = self.getKey("Title", from: dict) 86 | self.author = self.getKey("Author", from: dict) 87 | self.creator = self.getKey("Creator", from: dict) 88 | self.subject = self.getKey("Subject", from: dict) 89 | 90 | if let creationDateString = self.getKey("CreationDate", from: dict) { 91 | self.creationDate = convertDate(creationDateString) 92 | } 93 | 94 | if let modifiedDateString = self.getKey("ModDate", from: dict) { 95 | self.modifiedDate = convertDate(modifiedDateString) 96 | } 97 | 98 | let pagesCount = self.pagesCount 99 | pages.removeAll(keepingCapacity: true) 100 | for i in 1...pagesCount { 101 | if let pageRef = reference.page(at: i) { 102 | let page = PDFPage(reference: pageRef) 103 | pages.append(page) 104 | } 105 | } 106 | } 107 | 108 | func write(url: URL) { 109 | var infoDict = [String: AnyObject]() 110 | infoDict[kCGPDFContextTitle as String] = self.title as NSString? 111 | infoDict[kCGPDFContextAuthor as String] = self.author as NSString? 112 | infoDict[kCGPDFContextCreator as String] = self.creator as NSString? 113 | infoDict[kCGPDFContextSubject as String] = self.subject as NSString? 114 | 115 | let anyPage = pages.last 116 | var rect = anyPage?.frame ?? CGRect.zero 117 | guard let pdfContext = CGContext(url as CFURL, mediaBox: &rect, infoDict as CFDictionary?) else { 118 | return 119 | } 120 | 121 | for page in self.pages { 122 | page.draw(pdfContext: pdfContext) 123 | } 124 | } 125 | 126 | func write(path: String) { 127 | let url = URL(fileURLWithPath: path) 128 | self.write(url: url) 129 | } 130 | 131 | init (reference: CGPDFDocument) { 132 | self.reference = reference 133 | data = nil 134 | updateFields() 135 | } 136 | 137 | init? (data: Data) { 138 | let myPDFData: CFData = data as CFData; 139 | if let provider = CGDataProvider(data: myPDFData), let reference = CGPDFDocument(provider) { 140 | self.reference = reference 141 | self.data = data 142 | updateFields() 143 | return 144 | } 145 | return nil 146 | } 147 | 148 | init? (images: [UIImage]) { 149 | let pdfData = NSMutableData() 150 | guard let pdfConsumer = CGDataConsumer(data: pdfData), let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: nil, nil) else { 151 | return nil 152 | } 153 | 154 | for image in images where image.cgImage != nil { 155 | let pageWidth = image.size.width 156 | let pageHeight = image.size.height 157 | 158 | var pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight); 159 | pdfContext.beginPage(mediaBox: &pageRect); 160 | pdfContext.draw(image.cgImage!, in: pageRect) 161 | pdfContext.endPage(); 162 | } 163 | 164 | pdfContext.closePDF() 165 | 166 | if pdfData.length == 0 { 167 | return nil 168 | } 169 | 170 | self.data = pdfData as Data 171 | if let provider = CGDataProvider(data: pdfData), let reference = CGPDFDocument(provider) { 172 | self.reference = reference 173 | updateFields() 174 | return 175 | } 176 | return nil 177 | } 178 | 179 | convenience init? (url: URL) { 180 | if let reference = CGPDFDocument(url as CFURL) { 181 | self.init(reference: reference) 182 | return 183 | } 184 | return nil 185 | } 186 | 187 | convenience init? (path: String) { 188 | self.init(url: URL(fileURLWithPath: path)) 189 | } 190 | 191 | func unlock(_ password: String) -> Bool { 192 | return reference.unlockWithPassword((password as NSString).utf8String!) 193 | } 194 | } 195 | 196 | class PDFPage { 197 | let reference: CGPDFPage 198 | let pageNumber: Int 199 | let frame: CGRect 200 | 201 | init(reference: CGPDFPage) { 202 | self.reference = reference 203 | self.pageNumber = reference.pageNumber; 204 | self.frame = reference.getBoxRect(CGPDFBox.mediaBox); 205 | } 206 | 207 | var size: CGSize { 208 | return frame.size 209 | } 210 | 211 | func draw(pdfContext context: CGContext) { 212 | let size = frame.size 213 | var rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 214 | let boxData = NSData(bytes: &rect, length: MemoryLayout.size(ofValue: rect)) 215 | let pageDict = [kCGPDFContextMediaBox as String : boxData] 216 | 217 | context.beginPDFPage(pageDict as CFDictionary?); 218 | context.drawPDFPage(reference); 219 | context.endPDFPage(); 220 | } 221 | 222 | func draw(_ context: CGContext, atSize drawSize: CGSize) { 223 | // Flip coordinates 224 | _ = context.ctm; 225 | context.scaleBy(x: 1, y: -1); 226 | context.translateBy(x: 0, y: -drawSize.height); 227 | 228 | // get the rectangle of the cropped inside 229 | let mediaRect = reference.getBoxRect(CGPDFBox.cropBox); 230 | context.scaleBy(x: drawSize.width / mediaRect.size.width, 231 | y: drawSize.height / mediaRect.size.height); 232 | context.translateBy(x: -mediaRect.origin.x, y: -mediaRect.origin.y); 233 | 234 | context.drawPDFPage(reference); 235 | context.endPDFPage(); 236 | } 237 | 238 | func image(pixelsPerPoint ppp: Int = 1) -> PDFDocumentImageClass? { 239 | var size = frame.size 240 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 241 | size.width *= CGFloat(ppp) 242 | size.height *= CGFloat(ppp) 243 | 244 | UIGraphicsBeginImageContext(size) 245 | 246 | guard let context = UIGraphicsGetCurrentContext() else { 247 | return nil 248 | } 249 | 250 | context.saveGState() 251 | let transform = reference.getDrawingTransform(CGPDFBox.mediaBox, rect: rect, rotate: 0, preserveAspectRatio: true) 252 | context.concatenate(transform) 253 | 254 | context.translateBy(x: 0, y: size.height) 255 | context.scaleBy(x: CGFloat(ppp), y: CGFloat(-ppp)) 256 | context.drawPDFPage(reference); 257 | 258 | context.restoreGState(); 259 | 260 | let resultingImage = UIGraphicsGetImageFromCurrentImageContext(); 261 | UIGraphicsEndImageContext(); 262 | 263 | return resultingImage; 264 | } 265 | } 266 | --------------------------------------------------------------------------------