// // RCTCPDFView.swift // react-native-compdfkit-pdf // // Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved. // // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. // This notice may not be removed from this file. // import UIKit import ComPDFKit_Tools import ComPDFKit protocol RCTCPDFViewDelegate: AnyObject { func cpdfViewAttached(_ cpdfView: RCTCPDFView) func saveDocumentChange(_ cpdfView: RCTCPDFView) func onPageChanged(_ cpdfView: RCTCPDFView, pageIndex: Int) } class RCTCPDFView: UIView, CPDFViewBaseControllerDelete { weak var delegate: RCTCPDFViewDelegate? public var pdfViewController : CPDFViewController? private var navigationController : CNavigationController? init() { super.init(frame: CGRect(x: 0, y: 0, width: 500, height: 400)) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Private Methods private func createCPDFView() { var documentPath = document.path var success = false let homeDiectory = NSHomeDirectory() let bundlePath = Bundle.main.bundlePath if (documentPath.hasPrefix(homeDiectory) || documentPath.hasPrefix(bundlePath)) { let fileManager = FileManager.default let samplesFilePath = NSHomeDirectory().appending("/Documents/Files") let fileName = document.lastPathComponent let docsFilePath = samplesFilePath + "/" + fileName if !fileManager.fileExists(atPath: samplesFilePath) { try? FileManager.default.createDirectory(atPath: samplesFilePath, withIntermediateDirectories: true, attributes: nil) } try? FileManager.default.copyItem(atPath: document.path, toPath: docsFilePath) documentPath = docsFilePath } else { success = document.startAccessingSecurityScopedResource() } let jsonData = CPDFJSONDataParse(String: configuration) let configurations = jsonData.configuration ?? CPDFConfiguration() pdfViewController = CPDFViewController(filePath: documentPath, password: password, configuration: configurations) pdfViewController?.delegate = self navigationController = CNavigationController(rootViewController: pdfViewController!) navigationController?.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] navigationController?.view.frame = self.frame navigationController?.setViewControllers([pdfViewController!], animated: true) addSubview(navigationController?.view ?? UIView()) self.delegate?.cpdfViewAttached(self) if success { document.stopAccessingSecurityScopedResource() } } // MARK: - Public Methods func saveDocument(completionHandler: @escaping (Bool) -> Void) { if (self.pdfViewController?.pdfListView?.isEditing() == true && self.pdfViewController?.pdfListView?.isEdited() == true) { self.pdfViewController?.pdfListView?.commitEditing() if self.pdfViewController?.pdfListView?.document.isModified() == true { let document = self.pdfViewController?.pdfListView?.document let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false completionHandler(success) } else { completionHandler(true) } } else { if self.pdfViewController?.pdfListView?.document.isModified() == true { let document = self.pdfViewController?.pdfListView?.document let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false completionHandler(success) } else { completionHandler(true) } } } func setMargins(left : Int, top : Int, right : Int, bottom : Int) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.pageBreakMargins = .init(top: CGFloat(top), left: CGFloat(left), bottom: CGFloat(bottom), right: CGFloat(right)) pdfListView.layoutDocumentView() } } func removeAllAnnotations(completionHandler: @escaping (Bool) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let pageCount = pdfListView.document?.pageCount ?? 0 for i in 0.. Void) { if let pdfListView = self.pdfViewController?.pdfListView { let documentFolder = NSHomeDirectory().appending("/Documents/Files") if !FileManager.default.fileExists(atPath: documentFolder) { try? FileManager.default.createDirectory(at: URL(fileURLWithPath: documentFolder), withIntermediateDirectories: true, attributes: nil) } let documentPath = documentFolder + "/\(xfdfFile.lastPathComponent)" try? FileManager.default.copyItem(atPath: xfdfFile.path, toPath: documentPath) if !FileManager.default.fileExists(atPath: documentPath) { print("fail") } let success = pdfListView.document?.importAnnotation(fromXFDFPath: documentPath) ?? false if success { self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages() } completionHandler(success) } else { completionHandler(false) } } func exportAnnotations(completionHandler: @escaping (String) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let fileNameWithExtension = pdfListView.document?.documentURL.lastPathComponent ?? "" let fileName = (fileNameWithExtension as NSString).deletingPathExtension let documentFolder = NSHomeDirectory().appending("/Documents/\(fileName)_xfdf.xfdf") let succes = pdfListView.document?.exportAnnotation(toXFDFPath: documentFolder) ?? false if succes { completionHandler(documentFolder) } else { completionHandler("") } } else { completionHandler("") } } func setDisplayPageIndex(pageIndex : Int) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.go(toPageIndex: pageIndex, animated: false) } } func getCurrentPageIndex(completionHandler: @escaping (Int) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.currentPageIndex) } else { completionHandler(0) } } func hasChange(completionHandler: @escaping (Bool) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let success = pdfListView.document?.isModified() ?? false completionHandler(success) } else { completionHandler(false) } } func setScale(scale : NSNumber){ if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.setScaleFactor(CGFloat(truncating: scale), animated: true) } } func getScale(completionHandler: @escaping (NSNumber) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(NSNumber(value: pdfListView.scaleFactor)) } else { completionHandler(1.0) } } func setReadBackgroundColor(displayMode : NSString) { if let pdfListView = self.pdfViewController?.pdfListView { switch displayMode { case "light": pdfListView.displayMode = .normal case "dark": pdfListView.displayMode = .night case "sepia": pdfListView.displayMode = .soft case "reseda": pdfListView.displayMode = .green default: pdfListView.displayMode = .normal } pdfListView.layoutDocumentView() } } func getReadbackgroundColor(completionHandler: @escaping (NSString) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let dispalyMode = pdfListView.displayMode switch dispalyMode { case .normal: completionHandler("#FFFFFF") case .night: completionHandler("#000000") case .soft: completionHandler("#FFFFFF") case .green: completionHandler("#FFEFBE") case .custom: completionHandler("#CDE6D0") @unknown default: completionHandler("#FFFFFF") } } else { completionHandler("#FFFFFF") } } func setFormFieldHighlight(formFieldHighlight : Bool){ if let pdfListView = self.pdfViewController?.pdfListView { CPDFKitConfig.sharedInstance().setEnableFormFieldHighlight(formFieldHighlight) pdfListView.layoutDocumentView() } } func isFormFieldHighlight(completionHandler: @escaping (Bool) -> Void){ completionHandler(CPDFKitConfig.sharedInstance().enableFormFieldHighlight()) } func setLinkHighlight(linkHighlight : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { CPDFKitConfig.sharedInstance().setEnableLinkFieldHighlight(linkHighlight) pdfListView.layoutDocumentView() } } func isLinkHighlight(completionHandler: @escaping (Bool) -> Void){ completionHandler(CPDFKitConfig.sharedInstance().enableLinkFieldHighlight()) } func setVerticalMode(isVerticalMode : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.displayDirection = isVerticalMode ? .vertical : .horizontal pdfListView.layoutDocumentView() } } func isVerticalMode(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.displayDirection == .vertical) } else { completionHandler(true) } } func setContinueMode(isContinueMode : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.displaysPageBreaks = isContinueMode pdfListView.layoutDocumentView() } } func isContinueMode(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.displaysPageBreaks) }else { completionHandler(true) } } func setDoublePageMode(isDoublePageMode : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.displayTwoUp = isDoublePageMode pdfListView.displaysAsBook = false pdfListView.layoutDocumentView() } } func isDoublePageMode(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.displayTwoUp) }else { completionHandler(true) } } func setCoverPageMode(isCoverPageMode : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.displayTwoUp = isCoverPageMode pdfListView.displaysAsBook = isCoverPageMode pdfListView.layoutDocumentView() } } func isCoverPageMode(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.displaysAsBook) }else { completionHandler(true) } } func setCropMode(isCropMode : Bool) { if let pdfListView = self.pdfViewController?.pdfListView { pdfListView.displayCrop = isCropMode pdfListView.layoutDocumentView() } } func isCropMode(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.displayCrop) }else { completionHandler(true) } } func getFileName(completionHandler: @escaping (String) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.documentURL.lastPathComponent) }else { completionHandler("") } } func isEncrypted(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.isEncrypted) }else { completionHandler(false) } } func isImageDoc(completionHandler: @escaping (Bool) -> Void){ if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.isImageDocument()) }else { completionHandler(false) } } func getPermissions(completionHandler: @escaping (NSNumber) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let permissions = pdfListView.document?.permissionsStatus ?? .none switch permissions { case .none: completionHandler(0) case .user: completionHandler(1) case .owner: completionHandler(2) default: completionHandler(0) } }else { completionHandler(0) } } func getPageCount(completionHandler: @escaping (NSNumber) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.pageCount as NSNumber) }else { completionHandler(0) } } func checkOwnerUnlocked(completionHandler: @escaping (Bool) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.isCheckOwnerUnlocked()) }else { completionHandler(false) } } func checkOwnerPassword(password : String, completionHandler: @escaping (Bool) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { completionHandler(pdfListView.document.checkOwnerPassword(password)) }else { completionHandler(false) } } func getEncryptAlgo(completionHandler: @escaping (String) -> Void) { if let pdfListView = self.pdfViewController?.pdfListView { let encryptAlgo = pdfListView.document.encryptionLevel switch encryptAlgo { case .RC4: completionHandler("rc4") case .AES128: completionHandler("aes128") case .AES256: completionHandler("aes256") case .noEncryptAlgo: completionHandler("noEncryptAlgo") default: completionHandler("noEncryptAlgo") } }else { completionHandler("noEncryptAlgo") } } // MARK: - CPDFViewBaseControllerDelete func PDFViewBaseController(_ baseController: CPDFViewBaseController, SaveState success: Bool) { self.delegate?.saveDocumentChange(self) } func PDFViewBaseController(_ baseController: CPDFViewBaseController, currentPageIndex index: Int) { self.delegate?.onPageChanged(self, pageIndex: index) } // MARK: - RCT Methods private var configuration: String = "" @objc func setConfiguration(_ newSection: String) { configuration = newSection if (document.path.count > 1) && (configuration.count > 1) { createCPDFView() } } private var document: URL = URL(fileURLWithPath: "") @objc func setDocument(_ newSection: URL) { document = newSection if (document.path.count > 1) && (configuration.count > 1) { createCPDFView() } } private var password: String = "" @objc func setPassword(_ newSection: String) { password = newSection } public var onChange: RCTBubblingEventBlock? @objc func setOnChange(_ newSection: @escaping RCTBubblingEventBlock) { onChange = newSection } }