PDFGenerator.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. //
  2. // PDFGenerator.swift
  3. // PDFGenerator
  4. //
  5. // Created by Suguru Kishimoto on 2016/02/04.
  6. //
  7. //
  8. import Foundation
  9. import UIKit
  10. /// PDFGenerator
  11. public final class PDFGenerator {
  12. fileprivate typealias Process = () throws -> Void
  13. /// Avoid creating instance.
  14. fileprivate init() {}
  15. /**
  16. Generate from page object.
  17. - parameter page: A `PDFPage`'s object.
  18. - parameter outputPath: An outputPath to save PDF.
  19. - throws: A `PDFGenerateError` thrown if some error occurred.
  20. */
  21. public class func generate(_ page: PDFPage, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  22. try generate([page], to: path, dpi: dpi, password: password)
  23. }
  24. /**
  25. Generate from page objects.
  26. - parameter pages: Array of `PDFPage`'s objects.
  27. - parameter outputPath: An outputPath to save PDF.
  28. - throws: A `PDFGenerateError` thrown if some error occurred.
  29. */
  30. public class func generate(_ pages: [PDFPage], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  31. guard !pages.isEmpty else {
  32. throw PDFGenerateError.emptyPage
  33. }
  34. guard !path.isEmptyPath else {
  35. throw PDFGenerateError.emptyOutputPath
  36. }
  37. do {
  38. try render(to: path, password: password) {
  39. try render(pages, dpi: dpi)
  40. }
  41. } catch let error {
  42. _ = try? FileManager.default.removeItem(at: path.url)
  43. throw error
  44. }
  45. }
  46. /**
  47. Generate from view.
  48. - parameter view: A view
  49. - parameter outputPath: An outputPath to save PDF.
  50. - throws: A `PDFGenerateError` thrown if some error occurred.
  51. */
  52. public class func generate(_ view: UIView, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  53. try generate([view], to: path, dpi: dpi, password: password)
  54. }
  55. /**
  56. Generate from views.
  57. - parameter views: Array of views.
  58. - parameter outputPath: An outputPath to save PDF.
  59. - throws: A `PDFGenerateError` thrown if some error occurred.
  60. */
  61. public class func generate(_ views: [UIView], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  62. try generate(PDFPage.pages(views), to: path, dpi: dpi, password: password)
  63. }
  64. /**
  65. Generate from image.
  66. - parameter image: An image.
  67. - parameter outputPath: An outputPath to save PDF.
  68. - throws: A `PDFGenerateError` thrown if some error occurred.
  69. */
  70. public class func generate(_ image: UIImage, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  71. try generate([image], to: path, dpi: dpi, password: password)
  72. }
  73. /**
  74. Generate from images.
  75. - parameter images: Array of images.
  76. - parameter outputPath: An outputPath to save PDF.
  77. - throws: A `PDFGenerateError` thrown if some error occurred.
  78. */
  79. public class func generate(_ images: [UIImage], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  80. try generate(PDFPage.pages(images), to: path, dpi: dpi, password: password)
  81. }
  82. /**
  83. Generate from image path.
  84. - parameter imagePath: An image path.
  85. - parameter outputPath: An outputPath to save PDF.
  86. - throws: A `PDFGenerateError` thrown if some error occurred.
  87. */
  88. public class func generate(_ imagePath: String, to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  89. try generate([imagePath], to: path, dpi: dpi, password: password)
  90. }
  91. /**
  92. Generate from image paths.
  93. - parameter imagePaths: Arrat of image paths.
  94. - parameter outputPath: An outputPath to save PDF.
  95. - throws: A `PDFGenerateError` thrown if some error occurred.
  96. */
  97. public class func generate(_ imagePaths: [String], to path: FilePathConvertible, dpi: DPIType = .default, password: PDFPassword = "") throws {
  98. try generate(PDFPage.pages(imagePaths), to: path, dpi: dpi, password: password)
  99. }
  100. /**
  101. Generate from page object.
  102. - parameter page: A `PDFPage`'s object.
  103. - throws: A `PDFGenerateError` thrown if some error occurred.
  104. - returns: PDF's binary data (NSData)
  105. */
  106. public class func generated(by page: PDFPage, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  107. return try generated(by: [page], dpi: dpi, password: password)
  108. }
  109. /**
  110. Generate from page objects.
  111. - parameter pages: Array of `PDFPage`'s objects.
  112. - throws: A `PDFGenerateError` thrown if some error occurred.
  113. - returns: PDF's binary data (NSData)
  114. */
  115. public class func generated(by pages: [PDFPage], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  116. guard !pages.isEmpty else {
  117. throw PDFGenerateError.emptyPage
  118. }
  119. return try rendered(with: password) { try render(pages, dpi: dpi) }
  120. }
  121. /**
  122. Generate from view.
  123. - parameter view: A view
  124. - throws: A `PDFGenerateError` thrown if some error occurred.
  125. - returns: PDF's binary data (NSData)
  126. */
  127. public class func generated(by view: UIView, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  128. return try generated(by: [view], dpi: dpi, password: password)
  129. }
  130. /**
  131. Generate from views.
  132. - parameter views: Array of views.
  133. - throws: A `PDFGenerateError` thrown if some error occurred.
  134. - returns: PDF's binary data (NSData)
  135. */
  136. public class func generated(by views: [UIView], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  137. return try generated(by: PDFPage.pages(views), dpi: dpi, password: password)
  138. }
  139. /**
  140. Generate from image.
  141. - parameter image: An image.
  142. - throws: A `PDFGenerateError` thrown if some error occurred.
  143. - returns: PDF's binary data (NSData)
  144. */
  145. public class func generated(by image: UIImage, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  146. return try generated(by: [image], dpi: dpi, password: password)
  147. }
  148. /**
  149. Generate from images.
  150. - parameter images: Array of images.
  151. - throws: A `PDFGenerateError` thrown if some error occurred.
  152. - returns: PDF's binary data (NSData)
  153. */
  154. public class func generated(by images: [UIImage], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  155. return try generated(by: PDFPage.pages(images), dpi: dpi, password: password)
  156. }
  157. /**
  158. Generate from image path.
  159. - parameter imagePath: An image path.
  160. - throws: A `PDFGenerateError` thrown if some error occurred.
  161. - returns: PDF's binary data (NSData)
  162. */
  163. public class func generated(by imagePath: String, dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  164. return try generated(by: [imagePath], dpi: dpi, password: password)
  165. }
  166. /**
  167. Generate from image paths.
  168. - parameter imagePaths: Arrat of image paths.
  169. - throws: A `PDFGenerateError` thrown if some error occurred.
  170. - returns: PDF's binary data (NSData)
  171. */
  172. public class func generated(by imagePaths: [String], dpi: DPIType = .default, password: PDFPassword = "") throws -> Data {
  173. return try generated(by: PDFPage.pages(imagePaths), dpi: dpi, password: password)
  174. }
  175. }
  176. // MARK: Private Extension
  177. /// PDFGenerator private extensions (render processes)
  178. private extension PDFGenerator {
  179. class func render(_ page: PDFPage, dpi: DPIType) throws {
  180. let scaleFactor = dpi.scaleFactor
  181. try autoreleasepool {
  182. switch page {
  183. case .whitePage(let size):
  184. let view = UIView(frame: CGRect(origin: .zero, size: size))
  185. view.backgroundColor = .white
  186. try view.renderPDFPage(scaleFactor: scaleFactor)
  187. case .view(let view):
  188. try view.renderPDFPage(scaleFactor: scaleFactor)
  189. case .image(let image):
  190. try image.asUIImage().renderPDFPage(scaleFactor: scaleFactor)
  191. case .imagePath(let ip):
  192. try ip.asUIImage().renderPDFPage(scaleFactor: scaleFactor)
  193. case .binary(let data):
  194. try data.asUIImage().renderPDFPage(scaleFactor: scaleFactor)
  195. case .imageRef(let cgImage):
  196. try cgImage.asUIImage().renderPDFPage(scaleFactor: scaleFactor)
  197. }
  198. }
  199. }
  200. class func render(_ pages: [PDFPage], dpi: DPIType) throws {
  201. try pages.forEach { try render($0, dpi: dpi) }
  202. }
  203. class func render(to path: FilePathConvertible, password: PDFPassword, process: Process) rethrows {
  204. try { try password.verify() }()
  205. UIGraphicsBeginPDFContextToFile(path.path, .zero, password.toDocumentInfo())
  206. try process()
  207. UIGraphicsEndPDFContext()
  208. }
  209. class func rendered(with password: PDFPassword, process: Process) rethrows -> Data {
  210. try { try password.verify() }()
  211. let data = NSMutableData()
  212. UIGraphicsBeginPDFContextToData(data, .zero, password.toDocumentInfo())
  213. try process()
  214. UIGraphicsEndPDFContext()
  215. return data as Data
  216. }
  217. }