RCTCPDFView.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. //
  2. // RCTCPDFView.swift
  3. // react-native-compdfkit-pdf
  4. //
  5. // Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved.
  6. //
  7. // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
  8. // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
  9. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
  10. // This notice may not be removed from this file.
  11. //
  12. import UIKit
  13. import ComPDFKit_Tools
  14. import ComPDFKit
  15. protocol RCTCPDFViewDelegate: AnyObject {
  16. func cpdfViewAttached(_ cpdfView: RCTCPDFView)
  17. func saveDocumentChange(_ cpdfView: RCTCPDFView)
  18. func onPageChanged(_ cpdfView: RCTCPDFView, pageIndex: Int)
  19. }
  20. class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
  21. weak var delegate: RCTCPDFViewDelegate?
  22. public var pdfViewController : CPDFViewController?
  23. private var navigationController : CNavigationController?
  24. init() {
  25. super.init(frame: CGRect(x: 0, y: 0, width: 500, height: 400))
  26. }
  27. required init?(coder: NSCoder) {
  28. fatalError("init(coder:) has not been implemented")
  29. }
  30. // MARK: - Private Methods
  31. private func createCPDFView() {
  32. var documentPath = document.path
  33. var success = false
  34. let homeDiectory = NSHomeDirectory()
  35. let bundlePath = Bundle.main.bundlePath
  36. if (documentPath.hasPrefix(homeDiectory) || documentPath.hasPrefix(bundlePath)) {
  37. let fileManager = FileManager.default
  38. let samplesFilePath = NSHomeDirectory().appending("/Documents/Files")
  39. let fileName = document.lastPathComponent
  40. let docsFilePath = samplesFilePath + "/" + fileName
  41. if !fileManager.fileExists(atPath: samplesFilePath) {
  42. try? FileManager.default.createDirectory(atPath: samplesFilePath, withIntermediateDirectories: true, attributes: nil)
  43. }
  44. try? FileManager.default.copyItem(atPath: document.path, toPath: docsFilePath)
  45. documentPath = docsFilePath
  46. } else {
  47. success = document.startAccessingSecurityScopedResource()
  48. }
  49. let jsonData = CPDFJSONDataParse(String: configuration)
  50. let configurations = jsonData.configuration ?? CPDFConfiguration()
  51. pdfViewController = CPDFViewController(filePath: documentPath, password: password, configuration: configurations)
  52. pdfViewController?.delegate = self
  53. navigationController = CNavigationController(rootViewController: pdfViewController!)
  54. navigationController?.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  55. navigationController?.view.frame = self.frame
  56. navigationController?.setViewControllers([pdfViewController!], animated: true)
  57. addSubview(navigationController?.view ?? UIView())
  58. self.delegate?.cpdfViewAttached(self)
  59. if success {
  60. document.stopAccessingSecurityScopedResource()
  61. }
  62. }
  63. // MARK: - Public Methods
  64. func saveDocument(completionHandler: @escaping (Bool) -> Void) {
  65. if (self.pdfViewController?.pdfListView?.isEditing() == true && self.pdfViewController?.pdfListView?.isEdited() == true) {
  66. self.pdfViewController?.pdfListView?.commitEditing()
  67. if self.pdfViewController?.pdfListView?.document.isModified() == true {
  68. let document = self.pdfViewController?.pdfListView?.document
  69. let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false
  70. completionHandler(success)
  71. } else {
  72. completionHandler(true)
  73. }
  74. } else {
  75. if self.pdfViewController?.pdfListView?.document.isModified() == true {
  76. let document = self.pdfViewController?.pdfListView?.document
  77. let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false
  78. completionHandler(success)
  79. } else {
  80. completionHandler(true)
  81. }
  82. }
  83. }
  84. func setMargins(left : Int, top : Int, right : Int, bottom : Int) {
  85. if let pdfListView = self.pdfViewController?.pdfListView {
  86. pdfListView.pageBreakMargins = .init(top: CGFloat(top), left: CGFloat(left), bottom: CGFloat(bottom), right: CGFloat(right))
  87. pdfListView.layoutDocumentView()
  88. }
  89. }
  90. func removeAllAnnotations(completionHandler: @escaping (Bool) -> Void) {
  91. if let pdfListView = self.pdfViewController?.pdfListView {
  92. let pageCount = pdfListView.document?.pageCount ?? 0
  93. for i in 0..<pageCount {
  94. let page = pdfListView.document?.page(at: i)
  95. page?.removeAllAnnotations()
  96. }
  97. self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
  98. self.pdfViewController?.pdfListView?.updateActiveAnnotations([])
  99. completionHandler(true)
  100. } else {
  101. completionHandler(false)
  102. }
  103. }
  104. func importAnnotations(xfdfFile : URL, completionHandler: @escaping (Bool) -> Void) {
  105. if let pdfListView = self.pdfViewController?.pdfListView {
  106. let documentFolder = NSHomeDirectory().appending("/Documents/Files")
  107. if !FileManager.default.fileExists(atPath: documentFolder) {
  108. try? FileManager.default.createDirectory(at: URL(fileURLWithPath: documentFolder), withIntermediateDirectories: true, attributes: nil)
  109. }
  110. let documentPath = documentFolder + "/\(xfdfFile.lastPathComponent)"
  111. try? FileManager.default.copyItem(atPath: xfdfFile.path, toPath: documentPath)
  112. if !FileManager.default.fileExists(atPath: documentPath) {
  113. print("fail")
  114. }
  115. let success = pdfListView.document?.importAnnotation(fromXFDFPath: documentPath) ?? false
  116. if success {
  117. self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
  118. }
  119. completionHandler(success)
  120. } else {
  121. completionHandler(false)
  122. }
  123. }
  124. func exportAnnotations(completionHandler: @escaping (String) -> Void) {
  125. if let pdfListView = self.pdfViewController?.pdfListView {
  126. let fileNameWithExtension = pdfListView.document?.documentURL.lastPathComponent ?? ""
  127. let fileName = (fileNameWithExtension as NSString).deletingPathExtension
  128. let documentFolder = NSHomeDirectory().appending("/Documents/\(fileName)_xfdf.xfdf")
  129. let succes = pdfListView.document?.exportAnnotation(toXFDFPath: documentFolder) ?? false
  130. if succes {
  131. completionHandler(documentFolder)
  132. } else {
  133. completionHandler("")
  134. }
  135. } else {
  136. completionHandler("")
  137. }
  138. }
  139. func setDisplayPageIndex(pageIndex : Int) {
  140. if let pdfListView = self.pdfViewController?.pdfListView {
  141. pdfListView.go(toPageIndex: pageIndex, animated: false)
  142. }
  143. }
  144. func getCurrentPageIndex(completionHandler: @escaping (Int) -> Void) {
  145. if let pdfListView = self.pdfViewController?.pdfListView {
  146. completionHandler(pdfListView.currentPageIndex)
  147. } else {
  148. completionHandler(0)
  149. }
  150. }
  151. func hasChange(completionHandler: @escaping (Bool) -> Void) {
  152. if let pdfListView = self.pdfViewController?.pdfListView {
  153. let success = pdfListView.document?.isModified() ?? false
  154. completionHandler(success)
  155. } else {
  156. completionHandler(false)
  157. }
  158. }
  159. func setScale(scale : NSNumber){
  160. if let pdfListView = self.pdfViewController?.pdfListView {
  161. pdfListView.setScaleFactor(CGFloat(truncating: scale), animated: true)
  162. }
  163. }
  164. func getScale(completionHandler: @escaping (NSNumber) -> Void) {
  165. if let pdfListView = self.pdfViewController?.pdfListView {
  166. completionHandler(NSNumber(value: pdfListView.scaleFactor))
  167. } else {
  168. completionHandler(1.0)
  169. }
  170. }
  171. func setReadBackgroundColor(displayMode : NSString) {
  172. if let pdfListView = self.pdfViewController?.pdfListView {
  173. switch displayMode {
  174. case "light":
  175. pdfListView.displayMode = .normal
  176. case "dark":
  177. pdfListView.displayMode = .night
  178. case "sepia":
  179. pdfListView.displayMode = .soft
  180. case "reseda":
  181. pdfListView.displayMode = .green
  182. default:
  183. pdfListView.displayMode = .normal
  184. }
  185. pdfListView.layoutDocumentView()
  186. }
  187. }
  188. func getReadbackgroundColor(completionHandler: @escaping (NSString) -> Void) {
  189. if let pdfListView = self.pdfViewController?.pdfListView {
  190. let dispalyMode = pdfListView.displayMode
  191. switch dispalyMode {
  192. case .normal:
  193. completionHandler("#FFFFFF")
  194. case .night:
  195. completionHandler("#000000")
  196. case .soft:
  197. completionHandler("#FFFFFF")
  198. case .green:
  199. completionHandler("#FFEFBE")
  200. case .custom:
  201. completionHandler("#CDE6D0")
  202. @unknown default:
  203. completionHandler("#FFFFFF")
  204. }
  205. } else {
  206. completionHandler("#FFFFFF")
  207. }
  208. }
  209. func setFormFieldHighlight(formFieldHighlight : Bool){
  210. if let pdfListView = self.pdfViewController?.pdfListView {
  211. CPDFKitConfig.sharedInstance().setEnableFormFieldHighlight(formFieldHighlight)
  212. pdfListView.layoutDocumentView()
  213. }
  214. }
  215. func isFormFieldHighlight(completionHandler: @escaping (Bool) -> Void){
  216. completionHandler(CPDFKitConfig.sharedInstance().enableFormFieldHighlight())
  217. }
  218. func setLinkHighlight(linkHighlight : Bool) {
  219. if let pdfListView = self.pdfViewController?.pdfListView {
  220. CPDFKitConfig.sharedInstance().setEnableLinkFieldHighlight(linkHighlight)
  221. pdfListView.layoutDocumentView()
  222. }
  223. }
  224. func isLinkHighlight(completionHandler: @escaping (Bool) -> Void){
  225. completionHandler(CPDFKitConfig.sharedInstance().enableLinkFieldHighlight())
  226. }
  227. func setVerticalMode(isVerticalMode : Bool) {
  228. if let pdfListView = self.pdfViewController?.pdfListView {
  229. pdfListView.displayDirection = isVerticalMode ? .vertical : .horizontal
  230. pdfListView.layoutDocumentView()
  231. }
  232. }
  233. func isVerticalMode(completionHandler: @escaping (Bool) -> Void){
  234. if let pdfListView = self.pdfViewController?.pdfListView {
  235. completionHandler(pdfListView.displayDirection == .vertical)
  236. } else {
  237. completionHandler(true)
  238. }
  239. }
  240. func setContinueMode(isContinueMode : Bool) {
  241. if let pdfListView = self.pdfViewController?.pdfListView {
  242. pdfListView.displaysPageBreaks = isContinueMode
  243. pdfListView.layoutDocumentView()
  244. }
  245. }
  246. func isContinueMode(completionHandler: @escaping (Bool) -> Void){
  247. if let pdfListView = self.pdfViewController?.pdfListView {
  248. completionHandler(pdfListView.displaysPageBreaks)
  249. }else {
  250. completionHandler(true)
  251. }
  252. }
  253. func setDoublePageMode(isDoublePageMode : Bool) {
  254. if let pdfListView = self.pdfViewController?.pdfListView {
  255. pdfListView.displayTwoUp = isDoublePageMode
  256. pdfListView.displaysAsBook = false
  257. pdfListView.layoutDocumentView()
  258. }
  259. }
  260. func isDoublePageMode(completionHandler: @escaping (Bool) -> Void){
  261. if let pdfListView = self.pdfViewController?.pdfListView {
  262. completionHandler(pdfListView.displayTwoUp)
  263. }else {
  264. completionHandler(true)
  265. }
  266. }
  267. func setCoverPageMode(isCoverPageMode : Bool) {
  268. if let pdfListView = self.pdfViewController?.pdfListView {
  269. pdfListView.displayTwoUp = isCoverPageMode
  270. pdfListView.displaysAsBook = isCoverPageMode
  271. pdfListView.layoutDocumentView()
  272. }
  273. }
  274. func isCoverPageMode(completionHandler: @escaping (Bool) -> Void){
  275. if let pdfListView = self.pdfViewController?.pdfListView {
  276. completionHandler(pdfListView.displaysAsBook)
  277. }else {
  278. completionHandler(true)
  279. }
  280. }
  281. func setCropMode(isCropMode : Bool) {
  282. if let pdfListView = self.pdfViewController?.pdfListView {
  283. pdfListView.displayCrop = isCropMode
  284. pdfListView.layoutDocumentView()
  285. }
  286. }
  287. func isCropMode(completionHandler: @escaping (Bool) -> Void){
  288. if let pdfListView = self.pdfViewController?.pdfListView {
  289. completionHandler(pdfListView.displayCrop)
  290. }else {
  291. completionHandler(true)
  292. }
  293. }
  294. func setPreviewMode(viewMode : String) {
  295. switch viewMode {
  296. case "viewer":
  297. self.pdfViewController?.enterViewerMode()
  298. case "annotations":
  299. self.pdfViewController?.enterAnnotationMode()
  300. case "contentEditor":
  301. self.pdfViewController?.enterEditMode()
  302. case "forms":
  303. self.pdfViewController?.enterFormMode()
  304. case "signatures":
  305. self.pdfViewController?.enterSignatureMode()
  306. default:
  307. self.pdfViewController?.enterViewerMode()
  308. }
  309. }
  310. func getPreviewMode(completionHandler: @escaping (String) -> Void) {
  311. let state = self.pdfViewController?.functionTypeState ?? .viewer
  312. switch state {
  313. case .viewer:
  314. completionHandler("viewer")
  315. case .edit:
  316. completionHandler("contentEditor")
  317. case .annotation:
  318. completionHandler("annotations")
  319. case .form:
  320. completionHandler("forms")
  321. case .signature:
  322. completionHandler("signatures")
  323. default:
  324. completionHandler("viewer")
  325. }
  326. }
  327. func showThumbnailView(isEditMode : Bool) {
  328. if isEditMode {
  329. self.pdfViewController?.enterPDFPageEdit()
  330. } else {
  331. self.pdfViewController?.enterThumbnail()
  332. }
  333. }
  334. func showBotaView() {
  335. self.pdfViewController?.buttonItemClicked_Bota(UIButton(frame: .zero))
  336. }
  337. func showAddWatermarkView() {
  338. self.pdfViewController?.enterPDFWatermark()
  339. }
  340. func showSecurityView() {
  341. self.pdfViewController?.enterPDFSecurity()
  342. }
  343. func showDisplaySettingView() {
  344. self.pdfViewController?.enterPDFSetting()
  345. }
  346. func enterSnipMode() {
  347. self.pdfViewController?.enterPDFSnipImageMode()
  348. }
  349. func exitSnipMode() {
  350. self.pdfViewController?.exitPDFSnipImageMode()
  351. }
  352. func open(document : URL, password : String, completionHandler: @escaping (Bool) -> Void) {
  353. if let pdfListView = self.pdfViewController?.pdfListView {
  354. let newDocument = CPDFDocument(url: document)
  355. if(newDocument?.isLocked == true){
  356. newDocument?.unlock(withPassword: password)
  357. }
  358. pdfListView.document = newDocument
  359. pdfListView.setNeedsDisplay()
  360. completionHandler(true)
  361. } else {
  362. completionHandler(false)
  363. }
  364. }
  365. func getFileName(completionHandler: @escaping (String) -> Void){
  366. if let pdfListView = self.pdfViewController?.pdfListView {
  367. completionHandler(pdfListView.document.documentURL.lastPathComponent)
  368. }else {
  369. completionHandler("")
  370. }
  371. }
  372. func isEncrypted(completionHandler: @escaping (Bool) -> Void){
  373. if let pdfListView = self.pdfViewController?.pdfListView {
  374. completionHandler(pdfListView.document.isEncrypted)
  375. }else {
  376. completionHandler(false)
  377. }
  378. }
  379. func isImageDoc(completionHandler: @escaping (Bool) -> Void){
  380. if let pdfListView = self.pdfViewController?.pdfListView {
  381. completionHandler(pdfListView.document.isImageDocument())
  382. }else {
  383. completionHandler(false)
  384. }
  385. }
  386. func getPermissions(completionHandler: @escaping (NSNumber) -> Void) {
  387. if let pdfListView = self.pdfViewController?.pdfListView {
  388. let permissions = pdfListView.document?.permissionsStatus ?? .none
  389. switch permissions {
  390. case .none:
  391. completionHandler(0)
  392. case .user:
  393. completionHandler(1)
  394. case .owner:
  395. completionHandler(2)
  396. default:
  397. completionHandler(0)
  398. }
  399. }else {
  400. completionHandler(0)
  401. }
  402. }
  403. func getPageCount(completionHandler: @escaping (NSNumber) -> Void) {
  404. if let pdfListView = self.pdfViewController?.pdfListView {
  405. completionHandler(pdfListView.document.pageCount as NSNumber)
  406. }else {
  407. completionHandler(0)
  408. }
  409. }
  410. func checkOwnerUnlocked(completionHandler: @escaping (Bool) -> Void) {
  411. if let pdfListView = self.pdfViewController?.pdfListView {
  412. completionHandler(pdfListView.document.isCheckOwnerUnlocked())
  413. }else {
  414. completionHandler(false)
  415. }
  416. }
  417. func checkOwnerPassword(password : String, completionHandler: @escaping (Bool) -> Void) {
  418. if let pdfListView = self.pdfViewController?.pdfListView {
  419. completionHandler(pdfListView.document.checkOwnerPassword(password))
  420. }else {
  421. completionHandler(false)
  422. }
  423. }
  424. func removePassword(completionHandler: @escaping (Bool) -> Void) {
  425. if let pdfListView = self.pdfViewController?.pdfListView {
  426. let newDocument = pdfListView.document
  427. let url = newDocument?.documentURL
  428. let success = newDocument?.writeDecrypt(to: url, isSaveFontSubset: true) ?? false
  429. completionHandler(success)
  430. } else {
  431. completionHandler(false)
  432. }
  433. }
  434. func setPassword(info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
  435. if let pdfListView = self.pdfViewController?.pdfListView {
  436. let _info = info as? [String: Any]
  437. let userPassword : String = self.getValue(from: _info, key: "user_password", defaultValue: "")
  438. let ownerPassword : String = self.getValue(from: _info, key: "owner_password", defaultValue: "")
  439. let allowsPrinting : Bool = self.getValue(from: _info, key: "allows_printing", defaultValue: true)
  440. let allowsCopying : Bool = self.getValue(from: _info, key: "allows_copying", defaultValue: true)
  441. let encryptAlgo : String = self.getValue(from: _info, key: "encrypt_algo", defaultValue: "rc4")
  442. var level: Int = 0
  443. // Encryption mode, the type passed in is:rc4, aes128, aes256, noEncryptAlgo
  444. switch encryptAlgo {
  445. case "rc4":
  446. level = 0
  447. case "aes128":
  448. level = 1
  449. case "aes256":
  450. level = 2
  451. case "noEncryptAlgo":
  452. level = 3
  453. default:
  454. level = 3
  455. }
  456. var options:[CPDFDocumentWriteOption: Any] = [:]
  457. options[CPDFDocumentWriteOption.userPasswordOption] = userPassword
  458. options[CPDFDocumentWriteOption.ownerPasswordOption] = ownerPassword
  459. options[CPDFDocumentWriteOption.allowsPrintingOption] = allowsPrinting
  460. options[CPDFDocumentWriteOption.allowsCopyingOption] = allowsCopying
  461. options[CPDFDocumentWriteOption.encryptionLevelOption] = NSNumber(value: level)
  462. let newDocument = pdfListView.document
  463. let url = newDocument?.documentURL
  464. let success = newDocument?.write(to: url, withOptions: options, isSaveFontSubset: true) ?? false
  465. completionHandler(success)
  466. } else {
  467. completionHandler(false)
  468. }
  469. }
  470. func getEncryptAlgo(completionHandler: @escaping (String) -> Void) {
  471. if let pdfListView = self.pdfViewController?.pdfListView {
  472. let encryptAlgo = pdfListView.document.encryptionLevel
  473. switch encryptAlgo {
  474. case .RC4:
  475. completionHandler("rc4")
  476. case .AES128:
  477. completionHandler("aes128")
  478. case .AES256:
  479. completionHandler("aes256")
  480. case .noEncryptAlgo:
  481. completionHandler("noEncryptAlgo")
  482. default:
  483. completionHandler("noEncryptAlgo")
  484. }
  485. }else {
  486. completionHandler("noEncryptAlgo")
  487. }
  488. }
  489. func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
  490. guard let value = info?[key] as? T else {
  491. return defaultValue
  492. }
  493. return value
  494. }
  495. // MARK: - CPDFViewBaseControllerDelete
  496. func PDFViewBaseController(_ baseController: CPDFViewBaseController, SaveState success: Bool) {
  497. self.delegate?.saveDocumentChange(self)
  498. }
  499. func PDFViewBaseController(_ baseController: CPDFViewBaseController, currentPageIndex index: Int) {
  500. self.delegate?.onPageChanged(self, pageIndex: index)
  501. }
  502. // MARK: - RCT Methods
  503. private var configuration: String = ""
  504. @objc func setConfiguration(_ newSection: String) {
  505. configuration = newSection
  506. if (document.path.count > 1) && (configuration.count > 1) {
  507. createCPDFView()
  508. }
  509. }
  510. private var document: URL = URL(fileURLWithPath: "")
  511. @objc func setDocument(_ newSection: URL) {
  512. document = newSection
  513. if (document.path.count > 1) && (configuration.count > 1) {
  514. createCPDFView()
  515. }
  516. }
  517. private var password: String = ""
  518. @objc func setPassword(_ newSection: String) {
  519. password = newSection
  520. }
  521. public var onChange: RCTBubblingEventBlock?
  522. @objc func setOnChange(_ newSection: @escaping RCTBubblingEventBlock) {
  523. onChange = newSection
  524. }
  525. }