// // KMDocumentController.swift // PDF Reader Pro // // Created by wanjun on 2022/12/5. // import Cocoa let KMPDFDocumentType: String = "com.adobe.pdf" let KMPDFBundleDocumentType: String = "net.sourceforge.skim-app.pdfd" let KMNotesDocumentType: String = "net.sourceforge.skim-app.skimnotes" let KMNotesTextDocumentType: String = "public.plain-text" let KMNotesRTFDocumentType: String = "public.rtf" let KMNotesRTFDDocumentType: String = "com.apple.rtfd" let KMNotesFDFDocumentType: String = "com.adobe.fdf" let KMPostScriptDocumentType: String = "com.adobe.postscript" let KMEncapsulatedPostScriptDocumentType: String = "com.adobe.encapsulated-postscript" let KMDVIDocumentType: String = "org.tug.tex.dvi" let KMXDVDocumentType: String = "org.tug.tex.xdv" let KMFolderDocumentType: String = "public.folder" let KMDocumentSetupAliasKey: String = "_BDAlias" let KMDocumentSetupFileNameKey: String = "fileName" class KMDocumentController: NSDocumentController { func fetchUniquePath(_ originalPath: String) -> String { var path = originalPath let dManager = FileManager.default if !dManager.fileExists(atPath: path) { if path.extension.count < 1 { path = path.stringByAppendingPathExtension("pdf") } return path } else { let originalFullFileName = path.lastPathComponent let originalFileName = path.lastPathComponent.deletingPathExtension.lastPathComponent let originalExtension = path.extension let startIndex: Int = 0 let endIndex: Int = startIndex + originalPath.count - originalFullFileName.count - 1 let fileLocatePath = originalPath.substring(to: endIndex) var i = 1 while (1 != 0) { var newName = String(format: "%@%ld", originalFileName, i) newName = String(format: "%@%@", newName, originalExtension) let newPath = fileLocatePath.stringByAppendingPathComponent(newName) if !dManager.fileExists(atPath: newPath) { return newPath } else { i+=1 continue } } } } func kNewDocumentTempSavePath(_ fileName: String) -> String { let searchPath = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last let append1 = searchPath?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!) let append2 = append1!.stringByAppendingPathComponent(String(format: "%@", fileName)) return append2 } func savePdf(_ filePath: String) -> Void { let pdfData = try! Data(contentsOf: URL(fileURLWithPath: filePath)) let document = try! self.makeUntitledDocument(ofType: KMPDFDocumentType) as NSDocument if ((try? document.read(from: pdfData, ofType: KMPDFDocumentType)) != nil) { self.addDocument(document) document.makeWindowControllers() document.showWindows() } } // MARK: Action @IBAction func importFromFile(_ sender: Any) { } @IBAction func openBlankPage(_ sender: Any) { let fileName = String(format: "%@.pdf", NSLocalizedString("Untitled", comment: "")) let savePath = fetchUniquePath(kNewDocumentTempSavePath(fileName)) let pdfDocument = CPDFDocument() pdfDocument?.insertPage(CGSize(width: 595, height: 842), at: 0) pdfDocument?.write(to: URL(fileURLWithPath: savePath)) NSDocumentController.shared.openDocument(withContentsOf: URL(fileURLWithPath: savePath), display: true) { document, documentWasAlreadyOpen, error in if error != nil { NSApp.presentError(error!) return } if document is KMMainDocument { let newDocument = document (newDocument as! KMMainDocument).isNewCreated = false } } } @IBAction func newDocumentFromImage(_ sender: Any) { // let openPanel = NSOpenPanel() // openPanel.allowedFileTypes = KMImageAccessoryController.supportedImageTypes() // openPanel.allowsMultipleSelection = true // openPanel.message = NSLocalizedString("Select images to create a new document. To select multiple files press cmd ⌘ button on keyboard and click on the target files one by one.", comment: "") // openPanel.beginSheetModal(for: NSApp.mainWindow!) { response in // let savePath = self.kNewDocumentTempSavePath("convertToPDF.pdf") // if response == .OK { // KMConvertPDFManager.convertImages(openPanel.urls, savePath: savePath) { success, errorDic in // if !success || !FileManager.default.fileExists(atPath: savePath) { // if FileManager.default.fileExists(atPath: savePath) { // try? FileManager.default.removeItem(atPath: savePath) // } // // let alert = NSAlert() // alert.alertStyle = .critical // alert.messageText = NSLocalizedString("Conversion Failed", comment: "") // alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) // alert.runModal() // return // } // self.savePdf(savePath) // try? FileManager.default.removeItem(atPath: savePath) // } // } // } } @IBAction func importFromWebPage(_ sender: Any) { let windowController: KMURLToPDFWindowController = KMURLToPDFWindowController.init(windowNibName: NSNib.Name("KMURLToPDFWindowController")) windowController.beginSheetModalForWindow(NSWindow.currentWindow()) { filePath in if FileManager.default.fileExists(atPath: filePath) { NSDocumentController.shared.openDocument(withContentsOf: URL(fileURLWithPath: filePath), display: true) { document, documentWasAlreadyOpen, error in if error != nil { NSApp.presentError(error!) return } if document is KMMainDocument { (document as! KMMainDocument).isNewCreated = true } } } } } @IBAction func importFromScanner(_ sender: Any) { } } extension NSDocumentController { func openDocumentWithURLFromPasteboard(_ pboard: NSPasteboard, showNotes: Bool, outError: NSErrorPointer) -> Any? { guard let theURLs = NSURL.readURLs(from: pboard), let theURL = theURLs.first else { if showNotes == false { outError?.pointee = NSError.readPasteboardError(withLocalizedDescription: NSLocalizedString("Unable to load data from clipboard", comment: "Error description")) } return nil } // guard (theURL as AnyObject).isFileURL else { // if showNotes == false { // return SKDownloadController.shared.addDownload(for: theURL) // } // return nil // } var document: Any? do { let type = try self.typeForContents(of: theURL as! URL) // if showNotes == false || KMNotesDocument.readableTypes.contains(type) { // document = try self.openDocument(withContentsOf: theURL as! URL, display: true) // } else if KMMainDocument.readableTypes.contains(type) { for doc in self.documents { if let sourceURL = (doc as? NSObject)?.value(forKey: "sourceFileURL") as? URL, sourceURL == theURL as! URL { document = doc break } } if let existingDoc = document as? NSDocument { existingDoc.showWindows() } else { var error: NSError? var data: Data? if NSWorkspace.shared.type(type, conformsToType: KMPDFBundleDocumentType) { let skimFileURL = try self.bundledFileURLWithExtension("skim", inPDFBundleAtURL: theURL as! URL) data = try Data(contentsOf: skimFileURL) } // else { // data = try SKNExtendedAttributeManager.shared().extendedAttributeNamed(SKIM_NOTES_KEY, atPath: theURL.path, traverseLink: true) // } // let newDocument = try makeUntitledDocument(ofType: KMNotesDocumentType) // newDocument.sourceFileURL = theURL // // if data == nil || newDocument.read(from: data ?? Data(), ofType: KMNotesDocumentType) { // self.addDocument(newDocument) // newDocument.makeWindowControllers() // newDocument.showWindows() // } else { // document = nil // outError?.pointee = error // } } } } catch { outError?.pointee = error as NSError } return document } func bundledFileURLWithExtension(_ extension: String, inPDFBundleAtURL theURL: URL) throws -> URL { let bundleContents = try FileManager.default.contentsOfDirectory(at: theURL, includingPropertiesForKeys: nil, options: []) for fileURL in bundleContents { if fileURL.pathExtension == `extension` { return fileURL } } throw NSError(domain: "YourDomain", code: 404, userInfo: [NSLocalizedDescriptionKey: "File with extension not found in PDF bundle"]) } func openDocumentWithImageFromPasteboard(_ pboard: NSPasteboard, error outError: NSErrorPointer) -> Any? { var document: NSDocument? var data: Data? var type: String? if pboard.canReadItem(withDataConformingToTypes: [NSPasteboard.PasteboardType.pdf.rawValue]) { pboard.types data = pboard.data(forType: NSPasteboard.PasteboardType.pdf) type = KMPDFDocumentType } else if pboard.canReadItem(withDataConformingToTypes: [KMEncapsulatedPostScriptDocumentType]) { pboard.types data = pboard.data(forType: NSPasteboard.PasteboardType(rawValue: KMEncapsulatedPostScriptDocumentType)) type = isEncapsulatedPostScriptData(data!) ? KMEncapsulatedPostScriptDocumentType : KMPostScriptDocumentType } else if pboard.canReadItem(withDataConformingToTypes: [NSPasteboard.PasteboardType.tiff.rawValue]) { pboard.types data = convertTIFFDataToPDF(pboard.data(forType: NSPasteboard.PasteboardType.tiff)!) type = KMPDFDocumentType } else { let images = pboard.readObjects(forClasses: [NSImage.self], options: [:]) as? [NSImage] let strings = pboard.readObjects(forClasses: [NSAttributedString.self], options: [:]) as? [NSAttributedString] if let images = images, images.count > 0 { data = convertTIFFDataToPDF(images[0].tiffRepresentation!) type = KMPDFDocumentType } else if let strings = strings, strings.count > 0 { data = KMOCTool.convertStringsToPDF(withString: strings) // convertStringsToPDF(strings!) type = KMPDFDocumentType } } if let data = data, let type = type { var error: NSError? document = try?makeUntitledDocument(ofType: type) if ((try?document?.read(from: data, ofType:type)) != nil) { addDocument(document!) document?.makeWindowControllers() document?.showWindows() } else { document = nil if let outError = outError { outError.pointee = error } } } else if let outError = outError { outError.pointee = NSError.readPasteboardError(withLocalizedDescription: NSLocalizedString("Unable to load data from clipboard", comment: "Error description")) } return document } func isEncapsulatedPostScriptData(_ data: Data) -> Bool { let epsHeaderData = Data(bytes: [69, 80, 83, 70, 45], count: 5) let rg: Range = (14..<20) return (data.count >= 20 && (data.range(of: epsHeaderData, options: .anchored, in: rg) != nil)) } func convertTIFFDataToPDF(_ tiffData: Data) -> Data? { guard let imsrc = CGImageSourceCreateWithData(tiffData as CFData, [kCGImageSourceTypeIdentifierHint: kUTTypeTIFF] as CFDictionary), CGImageSourceGetCount(imsrc) > 0, let cgImage = CGImageSourceCreateImageAtIndex(imsrc, 0, nil) else { return nil } let pdfData = NSMutableData(capacity: tiffData.count) let consumer = CGDataConsumer(data: pdfData! as CFMutableData)! var rect = CGRect(x: 0, y: 0, width: CGFloat(cgImage.width), height: CGFloat(cgImage.height)) let ctxt = CGContext(consumer: consumer, mediaBox: &rect, nil) ctxt!.beginPDFPage(nil) ctxt!.draw(cgImage, in: rect) ctxt!.endPDFPage() ctxt!.closePDF() return pdfData as? Data } }