CPDFDocumentPlugin.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. //
  2. // CPDFDocumentPlugin.swift
  3. // compdfkit_flutter
  4. // Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved.
  5. //
  6. // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
  7. // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
  8. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
  9. // This notice may not be removed from this file.
  10. import Foundation
  11. import ComPDFKit
  12. import Flutter
  13. import ComPDFKit_Tools
  14. public class CPDFDocumentPlugin {
  15. private var document : CPDFDocument?
  16. public var _methodChannel : FlutterMethodChannel
  17. private var pdfViewController : CPDFViewController?
  18. init(uid : String, binaryMessager : FlutterBinaryMessenger) {
  19. _methodChannel = FlutterMethodChannel(name: "com.compdfkit.flutter.document_\(uid)", binaryMessenger: binaryMessager)
  20. registeryMethodChannel()
  21. }
  22. init(pdfViewController : CPDFViewController, uid : String, binaryMessager : FlutterBinaryMessenger){
  23. self.pdfViewController = pdfViewController
  24. _methodChannel = FlutterMethodChannel(name: "com.compdfkit.flutter.document_\(uid)", binaryMessenger: binaryMessager)
  25. registeryMethodChannel()
  26. }
  27. private func registeryMethodChannel(){
  28. _methodChannel.setMethodCallHandler({
  29. (call: FlutterMethodCall, result: FlutterResult) -> Void in
  30. print("ComPDFKit-Flutter: iOS-MethodChannel: CPDFDocumentPlugin [method:\(call.method)]")
  31. if(self.document == nil && self.pdfViewController != nil){
  32. guard let pdfListView = self.pdfViewController?.pdfListView else {
  33. print("pdfViewController error")
  34. return
  35. }
  36. self.document = pdfListView.document
  37. }
  38. switch call.method {
  39. case CPDFConstants.save:
  40. // save pdf
  41. guard let pdfListView = self.pdfViewController!.pdfListView else {
  42. result(true)
  43. return
  44. }
  45. var isSuccess = false
  46. if (pdfListView.isEditing() == true && pdfListView.isEdited() == true) {
  47. pdfListView.commitEditing()
  48. if pdfListView.document.isModified() == true {
  49. isSuccess = pdfListView.document.write(to: pdfListView.document.documentURL)
  50. }
  51. } else {
  52. if(pdfListView.document != nil) {
  53. if pdfListView.document.isModified() == true {
  54. isSuccess = pdfListView.document.write(to: pdfListView.document.documentURL)
  55. }
  56. }
  57. }
  58. if isSuccess {
  59. self._methodChannel.invokeMethod("saveDocument", arguments: nil)
  60. }
  61. result(isSuccess) // or return false
  62. case CPDFConstants.openDocument:
  63. let initInfo = call.arguments as? [String: Any]
  64. let path = initInfo?["filePath"] as? String ?? ""
  65. let password = initInfo?["password"] ?? ""
  66. self.document = CPDFDocument(url: URL(fileURLWithPath: path))
  67. if(self.document?.isLocked == true){
  68. let success = self.document?.unlock(withPassword: password as? String ?? "")
  69. if(success == true){
  70. if let error = self.document?.error as? NSError {
  71. let code = error.code
  72. switch code {
  73. case CPDFDocumentUnknownError:
  74. result("unknown")
  75. case CPDFDocumentFileError:
  76. result("errorFile")
  77. case CPDFDocumentFormatError:
  78. result("errorFormat")
  79. case CPDFDocumentPasswordError:
  80. result("errorPassword")
  81. case CPDFDocumentSecurityError:
  82. result("errorSecurity")
  83. case CPDFDocumentPageError:
  84. result("errorPage")
  85. default:
  86. result("success")
  87. }
  88. } else {
  89. result("success")
  90. }
  91. }else {
  92. result("errorPassword")
  93. }
  94. } else {
  95. if let error = self.document?.error as? NSError {
  96. let code = error.code
  97. switch code {
  98. case CPDFDocumentUnknownError:
  99. result("unknown")
  100. case CPDFDocumentFileError:
  101. result("errorFile")
  102. case CPDFDocumentFormatError:
  103. result("errorFormat")
  104. case CPDFDocumentPasswordError:
  105. result("errorPassword")
  106. case CPDFDocumentSecurityError:
  107. result("errorSecurity")
  108. case CPDFDocumentPageError:
  109. result("errorPage")
  110. default:
  111. result("success")
  112. }
  113. } else {
  114. result("success")
  115. }
  116. }
  117. self.pdfViewController?.pdfListView?.document = self.document
  118. self.pdfViewController?.pdfListView?.setNeedsDisplay()
  119. case CPDFConstants.getFileName:
  120. if(self.document == nil){
  121. print("self.document is nil")
  122. result("is nil")
  123. return
  124. }
  125. let fileName = self.document?.documentURL.lastPathComponent
  126. result(fileName)
  127. case CPDFConstants.isEncrypted:
  128. let isEncrypted = self.document?.isEncrypted ?? false
  129. result(isEncrypted)
  130. case CPDFConstants.isImageDoc:
  131. let isImageDoc = self.document?.isImageDocument() ?? false
  132. result(isImageDoc)
  133. case CPDFConstants.getPermissions:
  134. let permissions = self.document?.permissionsStatus ?? .none
  135. switch permissions {
  136. case .none:
  137. result(0)
  138. case .user:
  139. result(1)
  140. case .owner:
  141. result(2)
  142. default:
  143. result(0)
  144. }
  145. case CPDFConstants.checkOwnerUnlocked:
  146. let owner = self.document?.isCheckOwnerUnlocked() ?? false
  147. result(owner)
  148. case CPDFConstants.checkOwnerPassword:
  149. let info = call.arguments as? [String: Any]
  150. let password = info?["password"] as? String ?? ""
  151. let isOwnerPassword = self.document?.checkOwnerPassword(password) ?? false
  152. result(isOwnerPassword)
  153. case CPDFConstants.hasChange:
  154. let isModified = self.document?.isModified() ?? false
  155. result(isModified)
  156. case CPDFConstants.importAnnotations:
  157. let importPath = call.arguments as? String ?? ""
  158. let success = self.document?.importAnnotation(fromXFDFPath: importPath) ?? false
  159. if success {
  160. self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
  161. }
  162. result(success)
  163. case CPDFConstants.exportAnnotations:
  164. let fileNameWithExtension = self.document?.documentURL.lastPathComponent ?? ""
  165. let fileName = (fileNameWithExtension as NSString).deletingPathExtension
  166. let documentFolder = NSHomeDirectory().appending("/Documents/\(fileName)_xfdf.xfdf")
  167. let succes = self.document?.exportAnnotation(toXFDFPath: documentFolder) ?? false
  168. if succes {
  169. result(documentFolder)
  170. } else {
  171. result("")
  172. }
  173. case CPDFConstants.removeAllAnnotations:
  174. let pageCount = self.document?.pageCount ?? 0
  175. for i in 0..<pageCount {
  176. let page = self.document?.page(at: i)
  177. page?.removeAllAnnotations()
  178. }
  179. self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
  180. self.pdfViewController?.pdfListView?.updateActiveAnnotations([])
  181. result(true)
  182. case CPDFConstants.getPageCount:
  183. let count = self.document?.pageCount ?? 1
  184. result(count)
  185. case CPDFConstants.saveAs:
  186. let info = call.arguments as? [String: Any]
  187. let savePath = self.getValue(from: info, key: "save_path", defaultValue: "") ?? ""
  188. let removeSecurity = self.getValue(from: info, key: "remove_security", defaultValue: false)
  189. let fontSubSet = self.getValue(from: info, key: "font_sub_set", defaultValue: true)
  190. var success = false
  191. if removeSecurity {
  192. if (self.pdfViewController?.pdfListView?.isEditing() == true && self.pdfViewController?.pdfListView?.isEdited() == true) {
  193. self.pdfViewController?.pdfListView?.commitEditing()
  194. success = self.document?.writeDecrypt(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  195. } else {
  196. success = self.document?.writeDecrypt(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  197. }
  198. } else {
  199. if (self.pdfViewController?.pdfListView?.isEditing() == true && self.pdfViewController?.pdfListView?.isEdited() == true) {
  200. self.pdfViewController?.pdfListView?.commitEditing()
  201. success = self.document?.write(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  202. } else {
  203. success = self.document?.write(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  204. }
  205. }
  206. result(success)
  207. case CPDFConstants.print:
  208. self.pdfViewController?.enterPrintState()
  209. case CPDFConstants.removePassword:
  210. let url = self.document?.documentURL
  211. let success = self.document?.writeDecrypt(to: url, isSaveFontSubset: true) ?? false
  212. result(success)
  213. case CPDFConstants.setPassword:
  214. let info = call.arguments as? [String: Any]
  215. let userPassword : String = self.getValue(from: info, key: "user_password", defaultValue: "")
  216. let ownerPassword : String = self.getValue(from: info, key: "owner_password", defaultValue: "")
  217. let allowsPrinting : Bool = self.getValue(from: info, key: "allows_printing", defaultValue: true)
  218. let allowsCopying : Bool = self.getValue(from: info, key: "allows_copying", defaultValue: true)
  219. let encryptAlgo : String = self.getValue(from: info, key: "encrypt_algo", defaultValue: "rc4")
  220. var level: Int = 0
  221. // Encryption mode, the type passed in is:rc4, aes128, aes256, noEncryptAlgo
  222. switch encryptAlgo {
  223. case "rc4":
  224. level = 0
  225. case "aes128":
  226. level = 1
  227. case "aes256":
  228. level = 2
  229. case "noEncryptAlgo":
  230. level = 3
  231. default:
  232. level = 3
  233. }
  234. var options:[CPDFDocumentWriteOption: Any] = [:]
  235. options[CPDFDocumentWriteOption.userPasswordOption] = userPassword
  236. options[CPDFDocumentWriteOption.ownerPasswordOption] = ownerPassword
  237. options[CPDFDocumentWriteOption.allowsPrintingOption] = allowsPrinting
  238. options[CPDFDocumentWriteOption.allowsCopyingOption] = allowsCopying
  239. options[CPDFDocumentWriteOption.encryptionLevelOption] = NSNumber(value: level)
  240. let url = self.document?.documentURL
  241. let success = self.document?.write(to: url, withOptions: options, isSaveFontSubset: true)
  242. result(success)
  243. case CPDFConstants.getEncryptAlgorithm:
  244. let level: CPDFDocumentEncryptionLevel = self.document?.encryptionLevel ?? .noEncryptAlgo
  245. switch level {
  246. case .RC4:
  247. result("rc4")
  248. case .AES128:
  249. result("aes128")
  250. case .AES256:
  251. result("aes256")
  252. case .noEncryptAlgo:
  253. result("noEncryptAlgo")
  254. @unknown default:
  255. result("noEncryptAlgo")
  256. }
  257. case CPDFConstants.createWatermark:
  258. self.createWatermark(call: call, result: result)
  259. case CPDFConstants.removeAllWatermark:
  260. let watrmarks = self.document?.watermarks() ?? []
  261. for watermark in watrmarks {
  262. self.document?.removeWatermark(watermark)
  263. }
  264. let url = self.document?.documentURL
  265. self.document?.write(to: url, isSaveFontSubset: true)
  266. self.pdfViewController?.pdfListView?.layoutDocumentView()
  267. default:
  268. result(FlutterMethodNotImplemented)
  269. }
  270. });
  271. }
  272. private func createWatermark(call: FlutterMethodCall, result: FlutterResult) {
  273. let info = call.arguments as? [String: Any]
  274. // text, image
  275. let type : String = self.getValue(from: info, key: "type", defaultValue: "")
  276. // "0,1,2,3,4"
  277. let pages : String = self.getValue(from: info, key: "pages", defaultValue: "")
  278. let textContent : String = self.getValue(from: info, key: "text_content", defaultValue: "")
  279. let imagePath : String = self.getValue(from: info, key: "image_path", defaultValue: "")
  280. let textColor : String = self.getValue(from: info, key: "text_color", defaultValue: "#000000")
  281. let fontSize : Int = self.getValue(from: info, key: "font_size", defaultValue: 24)
  282. let scale : Double = self.getValue(from: info, key: "scale", defaultValue: 1.0)
  283. let rotation : Double = self.getValue(from: info, key: "rotation", defaultValue: 45)
  284. let opacity : Double = self.getValue(from: info, key: "opacity", defaultValue: 1.0)
  285. // top, center, bottom
  286. let verticalAlignment : String = self.getValue(from: info, key: "vertical_alignment", defaultValue: "center")
  287. // left, center, right
  288. let horizontalAlignment : String = self.getValue(from: info, key: "horizontal_alignment", defaultValue: "center")
  289. let verticalOffset : Double = self.getValue(from: info, key: "vertical_offset", defaultValue: 0)
  290. let horizontalOffset : Double = self.getValue(from: info, key: "horizontal_offset", defaultValue: 0)
  291. let isFront : Bool = self.getValue(from: info, key: "is_front", defaultValue: true)
  292. let isTilePage : Bool = self.getValue(from: info, key: "is_tile_page", defaultValue: false)
  293. let horizontalSpacing : Double = self.getValue(from: info, key: "horizontal_spacing", defaultValue: 0)
  294. let verticalSpacing : Double = self.getValue(from: info, key: "vertical_spacing", defaultValue: 0)
  295. var vertical:CPDFWatermarkVerticalPosition = .center
  296. var horizontal:CPDFWatermarkHorizontalPosition = .center
  297. switch verticalAlignment {
  298. case "top":
  299. vertical = .top
  300. case "center":
  301. vertical = .center
  302. case "bottom":
  303. vertical = .bottom
  304. default:
  305. vertical = .center
  306. }
  307. switch horizontalAlignment {
  308. case "left":
  309. horizontal = .left
  310. case "center":
  311. horizontal = .center
  312. case "right":
  313. horizontal = .right
  314. default:
  315. horizontal = .center
  316. }
  317. if(pages.isEmpty){
  318. result(FlutterError(code: "WATERMARK_FAIL", message: "The page range cannot be empty, please set the page range, for example: pages: \"0,1,2,3\"", details: nil))
  319. return
  320. }
  321. if("text" == type){
  322. if(textContent.isEmpty){
  323. result(FlutterError(code: "WATERMARK_FAIL", message: "Add text watermark, the text cannot be empty", details: nil))
  324. return
  325. }
  326. } else{
  327. if(imagePath.isEmpty){
  328. result(FlutterError(code: "WATERMARK_FAIL", message: "image path is empty", details: nil))
  329. return
  330. }
  331. }
  332. if type == "text" {
  333. let textWatermark = CPDFWatermark(document: self.document, type: .text)
  334. textWatermark?.text = textContent
  335. print("textColor:\(textColor)")
  336. let font = CPDFFont(familyName: "Helvetica", fontStyle: "")
  337. textWatermark?.cFont = font
  338. textWatermark?.opacity = opacity
  339. textWatermark?.fontSize = CGFloat(fontSize)
  340. textWatermark?.textColor = ColorHelper.colorWithHexString(hex: textColor)
  341. textWatermark?.scale = scale
  342. textWatermark?.isTilePage = isTilePage
  343. textWatermark?.isFront = isFront
  344. textWatermark?.tx = horizontalOffset
  345. textWatermark?.ty = verticalOffset
  346. textWatermark?.rotation = rotation
  347. textWatermark?.pageString = pages
  348. textWatermark?.horizontalPosition = horizontal
  349. textWatermark?.verticalPosition = vertical
  350. if textWatermark?.isTilePage == true {
  351. textWatermark?.verticalSpacing = verticalSpacing
  352. textWatermark?.horizontalSpacing = horizontalSpacing
  353. }
  354. self.document?.addWatermark(textWatermark)
  355. self.document?.updateWatermark(textWatermark)
  356. } else if type == "image" {
  357. let imageWatermark = CPDFWatermark(document: self.document, type: .image)
  358. imageWatermark?.image = UIImage.init(contentsOfFile: imagePath)
  359. imageWatermark?.opacity = opacity
  360. imageWatermark?.scale = scale
  361. imageWatermark?.isTilePage = isTilePage
  362. imageWatermark?.isFront = isFront
  363. imageWatermark?.tx = horizontalOffset
  364. imageWatermark?.ty = verticalOffset
  365. imageWatermark?.rotation = rotation
  366. imageWatermark?.pageString = pages
  367. imageWatermark?.horizontalPosition = horizontal
  368. imageWatermark?.verticalPosition = vertical
  369. if imageWatermark?.isTilePage == true {
  370. imageWatermark?.verticalSpacing = verticalSpacing
  371. imageWatermark?.horizontalSpacing = horizontalSpacing
  372. }
  373. self.document?.addWatermark(imageWatermark)
  374. self.document?.updateWatermark(imageWatermark)
  375. }
  376. self.pdfViewController?.pdfListView?.layoutDocumentView()
  377. }
  378. func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
  379. guard let value = info?[key] as? T else {
  380. return defaultValue
  381. }
  382. return value
  383. }
  384. }