CPDFDocumentPlugin.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. success = self.document?.writeDecrypt(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  193. } else {
  194. success = self.document?.write(to: URL(fileURLWithPath: savePath), isSaveFontSubset: fontSubSet) ?? false
  195. }
  196. result(success)
  197. case CPDFConstants.print:
  198. self.pdfViewController?.enterPrintState()
  199. case CPDFConstants.removePassword:
  200. let url = self.document?.documentURL
  201. let success = self.document?.writeDecrypt(to: url, isSaveFontSubset: true) ?? false
  202. result(success)
  203. case CPDFConstants.setPassword:
  204. let info = call.arguments as? [String: Any]
  205. let userPassword : String = self.getValue(from: info, key: "user_password", defaultValue: "")
  206. let ownerPassword : String = self.getValue(from: info, key: "owner_password", defaultValue: "")
  207. let allowsPrinting : Bool = self.getValue(from: info, key: "allows_printing", defaultValue: true)
  208. let allowsCopying : Bool = self.getValue(from: info, key: "allows_copying", defaultValue: true)
  209. let encryptAlgo : String = self.getValue(from: info, key: "encrypt_algo", defaultValue: "rc4")
  210. var level: Int = 0
  211. // Encryption mode, the type passed in is:rc4, aes128, aes256, noEncryptAlgo
  212. switch encryptAlgo {
  213. case "rc4":
  214. level = 0
  215. case "aes128":
  216. level = 1
  217. case "aes256":
  218. level = 2
  219. case "noEncryptAlgo":
  220. level = 3
  221. default:
  222. level = 3
  223. }
  224. var options:[CPDFDocumentWriteOption: Any] = [:]
  225. options[CPDFDocumentWriteOption.userPasswordOption] = userPassword
  226. options[CPDFDocumentWriteOption.ownerPasswordOption] = ownerPassword
  227. options[CPDFDocumentWriteOption.allowsPrintingOption] = allowsPrinting
  228. options[CPDFDocumentWriteOption.allowsCopyingOption] = allowsCopying
  229. options[CPDFDocumentWriteOption.encryptionLevelOption] = NSNumber(value: level)
  230. let url = self.document?.documentURL
  231. let success = self.document?.write(to: url, withOptions: options, isSaveFontSubset: true)
  232. result(success)
  233. case CPDFConstants.getEncryptAlgorithm:
  234. let level: CPDFDocumentEncryptionLevel = self.document?.encryptionLevel ?? .noEncryptAlgo
  235. switch level {
  236. case .RC4:
  237. result("rc4")
  238. case .AES128:
  239. result("aes128")
  240. case .AES256:
  241. result("aes256")
  242. case .noEncryptAlgo:
  243. result("noEncryptAlgo")
  244. @unknown default:
  245. result("noEncryptAlgo")
  246. }
  247. case CPDFConstants.createWatermark:
  248. self.createWatermark(call: call, result: result)
  249. case CPDFConstants.removeAllWatermark:
  250. let watrmarks = self.document?.watermarks() ?? []
  251. for watermark in watrmarks {
  252. self.document?.removeWatermark(watermark)
  253. }
  254. let url = self.document?.documentURL
  255. self.document?.write(to: url, isSaveFontSubset: true)
  256. self.pdfViewController?.pdfListView?.layoutDocumentView()
  257. default:
  258. result(FlutterMethodNotImplemented)
  259. }
  260. });
  261. }
  262. private func createWatermark(call: FlutterMethodCall, result: FlutterResult) {
  263. let info = call.arguments as? [String: Any]
  264. // text, image
  265. let type : String = self.getValue(from: info, key: "type", defaultValue: "")
  266. // "0,1,2,3,4"
  267. let pages : String = self.getValue(from: info, key: "pages", defaultValue: "")
  268. let textContent : String = self.getValue(from: info, key: "text_content", defaultValue: "")
  269. let imagePath : String = self.getValue(from: info, key: "image_path", defaultValue: "")
  270. let textColor : String = self.getValue(from: info, key: "text_color", defaultValue: "#000000")
  271. let fontSize : Int = self.getValue(from: info, key: "font_size", defaultValue: 24)
  272. let scale : Double = self.getValue(from: info, key: "scale", defaultValue: 1.0)
  273. let rotation : Double = self.getValue(from: info, key: "rotation", defaultValue: 45)
  274. let opacity : Double = self.getValue(from: info, key: "opacity", defaultValue: 1.0)
  275. // top, center, bottom
  276. let verticalAlignment : String = self.getValue(from: info, key: "vertical_alignment", defaultValue: "center")
  277. // left, center, right
  278. let horizontalAlignment : String = self.getValue(from: info, key: "horizontal_alignment", defaultValue: "center")
  279. let verticalOffset : Double = self.getValue(from: info, key: "vertical_offset", defaultValue: 0)
  280. let horizontalOffset : Double = self.getValue(from: info, key: "horizontal_offset", defaultValue: 0)
  281. let isFront : Bool = self.getValue(from: info, key: "is_front", defaultValue: true)
  282. let isTilePage : Bool = self.getValue(from: info, key: "is_tile_page", defaultValue: false)
  283. let horizontalSpacing : Double = self.getValue(from: info, key: "horizontal_spacing", defaultValue: 0)
  284. let verticalSpacing : Double = self.getValue(from: info, key: "vertical_spacing", defaultValue: 0)
  285. var vertical:CPDFWatermarkVerticalPosition = .center
  286. var horizontal:CPDFWatermarkHorizontalPosition = .center
  287. switch verticalAlignment {
  288. case "top":
  289. vertical = .top
  290. case "center":
  291. vertical = .center
  292. case "bottom":
  293. vertical = .bottom
  294. default:
  295. vertical = .center
  296. }
  297. switch horizontalAlignment {
  298. case "left":
  299. horizontal = .left
  300. case "center":
  301. horizontal = .center
  302. case "right":
  303. horizontal = .right
  304. default:
  305. horizontal = .center
  306. }
  307. if(pages.isEmpty){
  308. 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))
  309. return
  310. }
  311. if("text" == type){
  312. if(textContent.isEmpty){
  313. result(FlutterError(code: "WATERMARK_FAIL", message: "Add text watermark, the text cannot be empty", details: nil))
  314. return
  315. }
  316. } else{
  317. if(imagePath.isEmpty){
  318. result(FlutterError(code: "WATERMARK_FAIL", message: "image path is empty", details: nil))
  319. return
  320. }
  321. }
  322. if type == "text" {
  323. let textWatermark = CPDFWatermark(document: self.document, type: .text)
  324. textWatermark?.text = textContent
  325. print("textColor:\(textColor)")
  326. let font = CPDFFont(familyName: "Helvetica", fontStyle: "")
  327. textWatermark?.cFont = font
  328. textWatermark?.opacity = opacity
  329. textWatermark?.fontSize = CGFloat(fontSize)
  330. textWatermark?.textColor = ColorHelper.colorWithHexString(hex: textColor)
  331. textWatermark?.scale = scale
  332. textWatermark?.isTilePage = isTilePage
  333. textWatermark?.isFront = isFront
  334. textWatermark?.tx = horizontalOffset
  335. textWatermark?.ty = verticalOffset
  336. textWatermark?.rotation = rotation
  337. textWatermark?.pageString = pages
  338. textWatermark?.horizontalPosition = horizontal
  339. textWatermark?.verticalPosition = vertical
  340. if textWatermark?.isTilePage == true {
  341. textWatermark?.verticalSpacing = verticalSpacing
  342. textWatermark?.horizontalSpacing = horizontalSpacing
  343. }
  344. self.document?.addWatermark(textWatermark)
  345. self.document?.updateWatermark(textWatermark)
  346. } else if type == "image" {
  347. let imageWatermark = CPDFWatermark(document: self.document, type: .image)
  348. imageWatermark?.image = UIImage.init(contentsOfFile: imagePath)
  349. imageWatermark?.opacity = opacity
  350. imageWatermark?.scale = scale
  351. imageWatermark?.isTilePage = isTilePage
  352. imageWatermark?.isFront = isFront
  353. imageWatermark?.tx = horizontalOffset
  354. imageWatermark?.ty = verticalOffset
  355. imageWatermark?.rotation = rotation
  356. imageWatermark?.pageString = pages
  357. imageWatermark?.horizontalPosition = horizontal
  358. imageWatermark?.verticalPosition = vertical
  359. if imageWatermark?.isTilePage == true {
  360. imageWatermark?.verticalSpacing = verticalSpacing
  361. imageWatermark?.horizontalSpacing = horizontalSpacing
  362. }
  363. self.document?.addWatermark(imageWatermark)
  364. self.document?.updateWatermark(imageWatermark)
  365. }
  366. self.pdfViewController?.pdfListView?.layoutDocumentView()
  367. }
  368. func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
  369. guard let value = info?[key] as? T else {
  370. return defaultValue
  371. }
  372. return value
  373. }
  374. }