123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- //
- // KMMergeWindowController.swift
- // PDF Reader Pro
- //
- // Created by lizhe on 2023/11/8.
- //
- import Cocoa
- typealias KMMergeWindowControllerCancelAction = (_ controller: KMMergeWindowController) -> Void
- typealias KMMergeWindowControllerAddFilesAction = (_ controller: KMMergeWindowController) -> Void
- typealias KMMergeWindowControllerMergeAction = (_ controller: KMMergeWindowController, _ filePath: String) -> Void
- typealias KMMergeWindowControllerClearAction = (_ controller: KMMergeWindowController) -> Void
- class KMMergeWindowController: KMBaseWindowController {
- @IBOutlet weak var mergeView: KMMergeView!
-
- // var cancelAction: KMMergeWindowControllerCancelAction?
-
- var oldPDFDocument: PDFDocument = PDFDocument()
- var password: String = ""
-
- var oriDucumentUrl: URL? {
- didSet {
- oldPDFDocument = PDFDocument(url: oriDucumentUrl!)!
- oldPDFDocument.unlock(withPassword: self.password)
- }
- }
-
- var type: KMMergeViewType = .add
-
- var pageIndex: Int?
-
- var mergeAction: KMMergeWindowControllerMergeAction?
- // - (id)initWithPDFDocument:(PDFDocument *)document password:(NSString *)password
- // {
- // if (self = [super initWithWindowNibName:@"KMPDFEditAppendWindow"]) {
- //
- // // self.PDFDocument = document;
- // self.PDFDocument = [[PDFDocument alloc] init];
- // self.editType = KMPDFPageEditAppend;
- // _lockFilePathArr = [[NSMutableArray alloc] init];
- // _files = [[NSMutableArray alloc] init];
- //
- // KMFileAttribute *file = [[KMFileAttribute alloc] init];
- // file.myPDFDocument = document;
- // file.filePath = document.documentURL.path;
- // file.oriFilePath = self.oriDucumentUrl.path;
- // if (password && password.length > 0) {
- // file.password = password;
- // file.isLocked = YES;
- // }
- // [self.files addObject:file];
- // }
- // return self;
- // }
- convenience init(document: PDFDocument, password: String) {
- self.init(windowNibName: "KMMergeWindowController")
- self.password = password
-
- }
-
- override func windowDidLoad() {
- super.windowDidLoad()
- self.window!.title = NSLocalizedString("Merge PDF Files", comment: "");
- // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
- self.mergeView.type = self.type
- mergeView.addFilesAction = { [weak self] view in
- guard let self = self else { return }
- self.addFile()
- }
-
- mergeView.clearAction = { view in
-
- }
-
- mergeView.mergeAction = { [weak self] view, files, size in
- guard let self = self else { return }
- self.mergeFiles(files: files, size: size)
- }
-
- mergeView.cancelAction = { [weak self] view in
- guard let self = self else { return }
-
- self._clearImageData()
- self.cancelAction?(self)
- }
- }
- }
- extension KMMergeWindowController {
- func addFile() {
- var size = 0.0
- let files = self.mergeView.files
- for file in files {
- size = size + file.fileSize
- }
-
- if KMMemberInfo.shared.isMemberAllFunction == false && (files.count >= 2 || size > 20 * 1024 * 1024) {
- let winC = KMProductCompareWC.shared
- if self.kEventTag == 1 {
- winC.kEventName = "Onbrd_Merge_BuyNow"
- } else {
- winC.kEventName = "Reading_Merge_BuyNow"
- }
- KMMemberInfo.shared.advancedFunctionUsage()
- return
- }
-
- let openPanel = NSOpenPanel()
- // openPanel.allowedFileTypes = ["pdf"]
- openPanel.allowedFileTypes = KMTools.imageExtensions + KMTools.pdfExtensions
- if KMPurchaseManager.manager.state == .subscription {
- openPanel.allowsMultipleSelection = true
- openPanel.message = NSLocalizedString("Select files to merge. To select multiple files press cmd ⌘ button on keyboard and click on the target files one by one.", comment: "")
- } else {
- openPanel.allowsMultipleSelection = false
- openPanel.message = NSLocalizedString("Select files to merge, only one file can be selected at a time.", comment: "")
- }
- openPanel.beginSheetModal(for: self.window!) { (result) in
- if result == NSApplication.ModalResponse.OK {
- var array: [URL] = []
- for fileURL in openPanel.urls {
- if KMTools.isImageType(fileURL.pathExtension) {
- if let image = NSImage(contentsOf: fileURL) {
- if let page = PDFPage(image: image) {
- let document = PDFDocument()
- document.insert(page, at: 0)
-
- var path = self._saveImagePath() + "/" + fileURL.deletingPathExtension().lastPathComponent + ".pdf"
- path = KMTools.getUniqueFilePath(filePath: path)
- let result = document.write(toFile: path)
- if result {
- // array.append(URL(fileURLWithPath: path))
- let file = KMFileAttribute()
- file.filePath = path
- file.oriFilePath = fileURL.path
- file.myPDFDocument = document
-
- let attribe = try?FileManager.default.attributesOfItem(atPath: fileURL.path)
- let fileSize = attribe?[FileAttributeKey.size] as? CGFloat ?? 0
- size = fileSize + size
- if KMMemberInfo.shared.isMemberAllFunction == false && (files.count >= 2 || size > 20 * 1024 * 1024) {
- KMMemberInfo.shared.advancedFunctionUsage()
- return
- }
-
- self.mergeView.files.append(file)
- }
- }
- } else {
- Task {
- _ = await KMAlertTool.runModel(message: NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: ""))
- }
- }
- } else {
- array.append(fileURL)
- }
- }
-
- let attribe = try?FileManager.default.attributesOfItem(atPath: openPanel.urls.first!.path)
- let fileSize = attribe?[FileAttributeKey.size] as? CGFloat ?? 0
- size = fileSize + size
- if KMMemberInfo.shared.isMemberAllFunction == false && (files.count >= 2 || size > 20 * 1024 * 1024) {
- KMMemberInfo.shared.advancedFunctionUsage()
- return
- }
- self.mergeView.addFilePaths(urls: array)
- }
- }
- }
-
- private func _saveImagePath() -> String {
- let rootPath = KMDataManager.fetchAppSupportOfBundleIdentifierDirectory()
- let path = rootPath.appendingPathComponent("Merge").path
- if FileManager.default.fileExists(atPath: path) == false {
- try?FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: false)
- }
- return path
- }
-
- private func _clearImageData() {
- let path = self._saveImagePath()
- if FileManager.default.fileExists(atPath: path) {
- try?FileManager.default.removeItem(atPath: path)
- }
- }
-
- func mergeFiles(files: [KMFileAttribute], size: CGSize = .zero) {
- var size = 0.0
- for file in files {
- size = size + file.fileSize
- }
-
- if KMMemberInfo.shared.isMemberAllFunction == false && (files.count >= 2 || size > 20 * 1024 * 1024) {
- KMMemberInfo.shared.advancedFunctionUsage()
- return
- }
- var filesCount = 1
- if self.oriDucumentUrl != nil {
- filesCount = 0
- }
- if files.count <= filesCount {
- let alert = NSAlert.init()
- alert.alertStyle = .critical
- alert.messageText = NSLocalizedString("To start merging, please select at least 2 files.", comment: "")
- alert.runModal()
- return
- }
-
- var rootPDFOutlineArray: [PDFOutline] = []
- var allPage = true //只有是全部才支持大纲的合并
- for file in files {
- if file.fetchSelectPages().count == 0 {
- let alert = NSAlert.init()
- alert.alertStyle = .critical
- alert.messageText = "\(file.filePath.lastPathComponent) + \(NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: ""))"
- alert.runModal()
- return
- }
-
- allPage = file.bAllPage
-
- /*防止文件被地址变换后crash*/
- guard let tDocument = PDFDocument(url: NSURL(fileURLWithPath: file.filePath) as URL) else {
- print("文件不存在")
- let alert = NSAlert.init()
- alert.alertStyle = .critical
- alert.messageText = "\(file.filePath.lastPathComponent) + \(NSLocalizedString("Failed to merge!", comment: ""))"
- alert.runModal()
- return
- }
-
- var outlineArray: [PDFOutline] = []
-
- // if file.isLocked {
- tDocument.unlock(withPassword: file.password)
- // }
-
- if tDocument.outlineRoot != nil {
- rootPDFOutlineArray.append((tDocument.outlineRoot)!)
- self.fetchAllOfChildren((tDocument.outlineRoot)!, containerArray: &outlineArray)
- outlineArray.removeObject((tDocument.outlineRoot)!)
- } else {
- let rootOutline = PDFOutline.init()
- tDocument.outlineRoot = rootOutline
- if tDocument.outlineRoot != nil {
- rootPDFOutlineArray.append(tDocument.outlineRoot!)
- }
- }
-
- for number in file.fetchSelectPages() {
- let page = tDocument.page(at: number - 1)
- // if pageIndex != nil {
- // self.oldPDFDocument.insert(page!, at: pageIndex!)
- // pageIndex = pageIndex! + 1
- // } else {
- self.oldPDFDocument.insert(page!, at: self.oldPDFDocument.pageCount)
- // }
- // self.insertIndexSet.addIndex:(self.pdfDocument.pageCount - 1)
- }
- }
-
- var theFilepath = files.first?.filePath
- if let filepath = files.first?.oriFilePath {
- theFilepath = filepath
- }
- let fileName = (theFilepath?.deletingPathExtension.lastPathComponent ?? "") + "_Merged"
- DispatchQueue.main.async {
- if self.oldPDFDocument.outlineRoot == nil {
- self.oldPDFDocument.outlineRoot = PDFOutline.init()
- }
- // if allPage {
- var insertIndex = 0
- for i in 0..<rootPDFOutlineArray.count {
- let rootOutline = rootPDFOutlineArray[i]
- for j in 0..<rootOutline.numberOfChildren {
- self.oldPDFDocument.outlineRoot?.insertChild(rootOutline.child(at: j)!, at: insertIndex)
- insertIndex = insertIndex + 1
- }
- }
-
- self.handleReDraw()
-
- if self.oriDucumentUrl != nil {
-
- let newPath = self.oldPDFDocument.documentURL!.path
- var options: [PDFDocumentWriteOption : Any] = [:]
- var success = false
- let password = self.password
- let pdf = self.oldPDFDocument
- // if pdf.isEncrypted {
- // options.updateValue(password, forKey: .userPasswordOption)
- // options.updateValue(password, forKey: .ownerPasswordOption)
- // success = pdf.write(toFile: newPath, withOptions: options)
- // } else {
- // success = pdf.write(toFile: newPath)
- // }
-
- // var success = self.oldPDFDocument.write(toFile: self.oldPDFDocument.documentURL!.path)
- // if success {
- let savePanelAccessoryViewController = KMSavePanelAccessoryController.init()
- let savePanel = NSSavePanel()
- savePanel.nameFieldStringValue = fileName
- savePanel.allowedFileTypes = ["pdf"]
- savePanel.accessoryView = savePanelAccessoryViewController.view
- // self.savePanelAccessoryViewController = savePanelAccessoryViewController;
- savePanel.beginSheetModal(for: self.window!) { result in
- if result == .OK {
- self._clearImageData()
- self.cancelAction?()
- var outputSavePanel = savePanel.url?.path ?? ""
- DispatchQueue.main.async {
- var success = false
- if pdf.isEncrypted {
- options.updateValue(password, forKey: .userPasswordOption)
- options.updateValue(password, forKey: .ownerPasswordOption)
- success = pdf.write(toFile: outputSavePanel, withOptions: options)
- } else {
- success = pdf.write(toFile: outputSavePanel)
- }
-
- if success {
- if savePanelAccessoryViewController.needOpen {
- NSDocumentController.shared.openDocument(withContentsOf: savePanel.url!, display: true) { document, open, error in
-
- }
- } else {
- NSWorkspace.shared.activateFileViewerSelecting([NSURL(fileURLWithPath: outputSavePanel) as URL])
- }
- } else {
- let alert = NSAlert.init()
- alert.alertStyle = .critical
- alert.messageText = "\(String(describing: files.first?.filePath.lastPathComponent)) + \(NSLocalizedString("Failed to merge!", comment: ""))"
- alert.runModal()
- }
- }
- }
- }
- // self.mergeAction?(self, self.oldPDFDocument.documentURL!.path)
- // } else {
- // print("合并失败")
- // }
- } else {
- let savePanelAccessoryViewController = KMSavePanelAccessoryController.init()
- let savePanel = NSSavePanel()
- savePanel.nameFieldStringValue = fileName
- savePanel.allowedFileTypes = ["pdf"]
- savePanel.accessoryView = savePanelAccessoryViewController.view
- // self.savePanelAccessoryViewController = savePanelAccessoryViewController;
- savePanel.beginSheetModal(for: self.window!) { result in
- if result == .OK {
- self._clearImageData()
- self.cancelAction?()
- var outputSavePanel = savePanel.url?.path
- DispatchQueue.main.async {
- var success = self.oldPDFDocument.write(toFile: outputSavePanel!)
- if !success {
- success = ((try?self.oldPDFDocument.dataRepresentation()?.write(to: URL(string: outputSavePanel!)!)) != nil)
- }
-
- if success {
- if savePanelAccessoryViewController.needOpen {
- NSDocumentController.shared.openDocument(withContentsOf: savePanel.url!, display: true) { document, open, error in
-
- }
- } else {
- NSWorkspace.shared.activateFileViewerSelecting([NSURL(fileURLWithPath: outputSavePanel!) as URL])
- }
- } else {
- let alert = NSAlert.init()
- alert.alertStyle = .critical
- alert.messageText = "\(String(describing: files.first?.filePath.lastPathComponent)) + \(NSLocalizedString("Failed to merge!", comment: ""))"
- alert.runModal()
- }
- }
- }
- }
- }
- // }
- }
- }
-
- func fetchAllOfChildren(_ aOutline: PDFOutline, containerArray aMArray: inout [PDFOutline]) {
- if !aMArray.contains(aOutline) {
- aMArray.append(aOutline)
- }
-
- for i in 0..<aOutline.numberOfChildren {
- if let childOutline = aOutline.child(at: i) {
- aMArray.append(childOutline)
- fetchAllOfChildren(childOutline, containerArray: &aMArray)
- }
- }
- }
-
- func handleReDraw() {
- if mergeView.originalSizeButton.state == .on {
-
- } else {
- let size = self.mergeView.newPageSize
- if size.width < 0 {
- return
- }
-
- var pagesArray: [PDFPage] = []
- let pageCount = self.oldPDFDocument.pageCount
- for i in 0..<pageCount {
- pagesArray.append(self.oldPDFDocument.page(at: 0)!)
- self.oldPDFDocument.removePage(at: 0)
- }
-
- for i in 0..<pageCount {
- let page: KMMergePDFPage = KMMergePDFPage.init()
- page.setBounds(NSMakeRect(0, 0, size.width, size.height), for: .mediaBox)
- page.drawingPage = pagesArray[i]
- self.oldPDFDocument.insert(page, at: i)
- }
-
- if self.oldPDFDocument.outlineRoot != nil {
- let childCount = self.oldPDFDocument.outlineRoot?.numberOfChildren
- var outlineArray: [PDFOutline] = []
-
- for i in 0..<childCount! {
- outlineArray.append((self.oldPDFDocument.outlineRoot?.child(at: i))!)
- }
-
- for outline in outlineArray {
- outline.removeFromParent()
- }
- }
- }
- }
- }
- class KMMergePDFPage: PDFPage {
- var drawingPage: PDFPage?
-
- override func draw(with box: PDFDisplayBox, to context: CGContext) {
- super.draw(with: box, to: context)
-
- let pageSize = self.bounds(for: .cropBox).size
- self.drawPage(with: context, page: self.drawingPage!, pageSize: pageSize)
- }
-
- func drawPage(with context: CGContext, page: PDFPage, pageSize: CGSize) {
- var originalSize = page.bounds(for: .cropBox).size
-
- // 如果页面的旋转角度为90或者270,宽高交换
- if page.rotation % 180 != 0 {
- originalSize = CGSize(width: originalSize.height, height: originalSize.width)
- }
-
- let wRatio = pageSize.width / originalSize.width
- let hRatio = pageSize.height / originalSize.height
- let ratio = min(wRatio, hRatio)
-
- context.saveGState()
- let xTransform = (pageSize.width - originalSize.width * ratio) / 2
- let yTransform = (pageSize.height - originalSize.height * ratio) / 2
- context.translateBy(x: xTransform, y: yTransform)
- context.scaleBy(x: ratio, y: ratio)
-
- if #available(macOS 10.12, *) {
- page.draw(with: .cropBox, to: context)
- page.transformContext(for: .cropBox)
- } else {
- NSGraphicsContext.saveGraphicsState()
- NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false)
- page.draw(with: .cropBox)
- NSGraphicsContext.restoreGraphicsState()
- page.transformContext(for: .cropBox)
- }
-
- context.restoreGState()
- }
- }
|