// // KMWatermarkAdjectiveBaseController.swift // PDF Reader Pro // // Created by tangchao on 2022/12/15. // import Cocoa class KMWatermarkTestWindow: NSWindow { var controller: KMWatermarkAdjectivePreViewBaseController! override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) { super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag); isReleasedWhenClosed = false let controller_: KMCropPreviewController = KMCropPreviewController() contentView?.addSubview(controller_.view) controller_.view.frame = CGRect(x: 0, y: 0, width: contentRect.size.width, height: contentRect.size.height) controller = controller_ } } typealias KMWatermarkAdjectivePageNumberViewCallback = (_ value: String) -> () class KMWatermarkAdjectivePageNumberView: NSView , NSTextFieldDelegate{ var numberTextField = NSTextField() var rightLabel = NSTextField(labelWithString: "") var callback: KMWatermarkAdjectivePageNumberViewCallback? override init(frame frameRect: NSRect) { super.init(frame: frameRect) self.initSubViews() } required init?(coder: NSCoder) { super.init(coder: coder) self.initSubViews() } func initSubViews() { self.addSubview(self.numberTextField) self.addSubview(self.rightLabel) self.numberTextField.alignment = .center self.rightLabel.alignment = .left self.numberTextField.textColor = NSColor.white self.rightLabel.textColor = NSColor.white self.numberTextField.isBordered = false self.numberTextField.drawsBackground = false self.numberTextField.focusRingType = .none self.numberTextField.delegate = self self.numberTextField.formatter = TextFieldFormatter() self.numberTextField.maximumNumberOfLines = 1 self.numberTextField.lineBreakMode = .byTruncatingTail } override func layout() { super.layout() let width: CGFloat = NSWidth(self.bounds) let height: CGFloat = NSHeight(self.bounds) let contentH: CGFloat = 20 let contentY: CGFloat = (height-contentH) * 0.5 self.numberTextField.frame = NSRect(x: 10, y: contentY, width: width * 0.5-10, height: contentH) self.rightLabel.frame = NSRect(x: width * 0.5, y: contentY, width: width * 0.5, height: contentH) } func controlTextDidChange(_ obj: Notification) { if (self.numberTextField.isEqual(to: obj.object)) { guard let callback = self.callback else { return } callback(self.numberTextField.stringValue) } } } typealias KMWatermarkAdjectivePreViewItemClick = (_ index: Int, _ param: Any?) -> () /// 水印相关 预览控制器(基类) class KMWatermarkAdjectivePreViewBaseController: NSViewController { @IBOutlet weak var topBarBox: NSBox! lazy var topBarView: KMWatermarkAdjectiveTopBarView = { let view = KMWatermarkAdjectiveTopBarView() return view }() @IBOutlet weak var splitView: NSSplitView! @IBOutlet weak var pageInputBox: NSBox! @IBOutlet weak var preViewBox: NSBox! var preView: CPDFView! var documentURL: URL! var applyDocument: CPDFDocument? var pageNumberView = KMWatermarkAdjectivePageNumberView() var watermarkArray: Array = [] @IBOutlet weak var rightBox: NSBox! var rightViewController: NSViewController! @IBOutlet weak var tipBox: NSBox! private var maskView: NSButton! private var indicator: NSProgressIndicator! var itemClick: KMWatermarkAdjectivePreViewItemClick? var model: KMWatermarkAdjectiveBaseModel? init() { super.init(nibName: "KMWatermarkAdjectivePreViewBaseController", bundle: nil) } required init?(coder: NSCoder) { super.init(nibName: "KMWatermarkAdjectivePreViewBaseController", bundle: nil) } override func viewDidLoad() { super.viewDidLoad() self.topBarView.frame = self.topBarBox.contentView!.bounds self.topBarView.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) self.topBarBox.contentView?.addSubview(self.topBarView) self.topBarBox.fillColor = NSColor.white self.view.wantsLayer = true self.view.layer?.backgroundColor = NSColor.white.cgColor self.topBarView.isCanApply(can: false) self.topBarView.cancelClick = { [unowned self] in self.cancelAction() } self.topBarView.applyClick = { [unowned self] in self.applyAction() } self.topBarView.itemClick = { [weak self] section, item in self!.topItemClick(index: item) } self.rightBox.fillColor = NSColor(white: 243.0/255.0, alpha: 1.0) self.splitView.isVertical = true self.splitView.dividerStyle = .thin self.splitView.delegate = self // self.splitView.arrangedSubviews.first?.frame = NSMakeRect(0, 0, self.splitView.frame.size.width-260, self.splitView.frame.size.height) // self.splitView.arrangedSubviews.last?.frame = NSMakeRect(self.splitView.frame.size.width-260, 0, 260, self.splitView.frame.size.height) let pageNumberView = self.pageNumberView pageNumberView.frame = self.pageInputBox.contentView!.bounds pageNumberView.autoresizingMask = [.width, .height] self.pageInputBox.contentView?.addSubview(pageNumberView) self.pageInputBox.fillColor = NSColor.black self.pageNumberView.callback = { [weak self] string in var number: Int = 0 if (string.isEmpty) { number = 1 } else { number = Int(string)! if (number <= 0) { number = 1 } else if (number > (self?.preView.document.pageCount)!) { number = Int((self?.preView.document.pageCount)!) } } self?.pageNumberView.numberTextField.stringValue = "\(number)" self?.preView.go(toPageIndex: number-1, animated: true) } } override func viewWillAppear() { super.viewWillAppear() if (self.documentURL != nil) { self.preView.document = CPDFDocument(url: self.documentURL) self.pageNumberView.numberTextField.stringValue = "1" self.pageNumberView.rightLabel.stringValue = "/ \(self.preView.document.pageCount)" } } func cancelAction() { guard let callback = self.itemClick else { return } callback(0, nil) } func applyAction() { } func topItemClick(index: Int) { } func right_gotoViewController(viewController: NSViewController) { for subview in self.rightBox.contentView!.subviews { subview.removeFromSuperview() } viewController.view.frame = self.rightBox.contentView!.bounds viewController.view.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) self.rightBox.contentView?.addSubview(viewController.view) self.rightViewController = viewController } func addWatermark(model: KMWatermarkModel, toPath: String, completion: @escaping (_ result: Bool) -> ()) { DispatchQueue.global().async { var property: CPDFWatermark! var scale: CGFloat = model.scale if (!model.text.isEmpty) { property = CPDFWatermark(document: self.preView.document, type: .text) property.text = model.text property.textColor = model.getTextColor() scale = model.getTextFontSize() / 24.0 } else { property = CPDFWatermark(document: self.preView.document, type: .image) property.image = model.image } property.scale = scale property.rotation = -model.rotation property.opacity = model.opacity property.tx = model.horizontalSpace property.ty = model.verticalSpace property.isFront = model.isFront var pageString: String = "" if (model.pageRangeType == .all) { for i in 0 ..< self.preView.document.pageCount { pageString.append("\(i)") if (i != self.preView.document.pageCount-1) { pageString.append(",") } } } else if (model.pageRangeType == .odd) { for i in 0 ..< self.preView.document.pageCount { if (i % 2 == 0) { pageString.append("\(i)") } else { continue } if (i != self.preView.document.pageCount-1) { pageString.append(",") } } } else if (model.pageRangeType == .even) { for i in 0 ..< self.preView.document.pageCount { if (i % 2 == 1) { pageString.append("\(i)") } else { continue } if (i != self.preView.document.pageCount-1) { pageString.append(",") } } } else { pageString = model.pagesString } property.pageString = pageString property.isTilePage = model.isTilePage property.horizontalSpacing = model.tileHorizontalSpace / scale property.verticalSpacing = model.tileVerticalSpace / scale if (model.verticalMode == 0) { property.verticalPosition = .top } else if (model.verticalMode == 1) { property.verticalPosition = .center } else if (model.verticalMode == 2) { property.verticalPosition = .bottom } if (model.horizontalMode == 0) { property.horizontalPosition = .left } else if (model.horizontalMode == 1) { property.horizontalPosition = .center } else if (model.horizontalMode == 2) { property.horizontalPosition = .right } model.watermark = property self.preView.document.addWatermark(property) /// 保存到临时路径 let documentPath = NSTemporaryDirectory() let tempPath: String = "\(documentPath)/\(toPath.lastPathComponent)" if (FileManager.default.fileExists(atPath: tempPath)) { try?FileManager.default.removeItem(atPath: tempPath) } let result = self.preView.document.write(to: URL(fileURLWithPath: tempPath)) if (result) { if (FileManager.default.fileExists(atPath: toPath)) { try?FileManager.default.removeItem(atPath: toPath) } try?FileManager.default.moveItem(atPath: tempPath, toPath: toPath) } else { try?FileManager.default.removeItem(atPath: tempPath) } DispatchQueue.main.async { completion(result) } } func loadAllWatermarks() { self.watermarkArray.removeAll() let watermarks: Array = self.preView.document.watermarks() for watermark in watermarks { let model = KMWatermarkModel() model.scale = watermark.scale model.rotation = -watermark.rotation model.opacity = watermark.opacity model.verticalSpace = watermark.tx model.horizontalSpace = watermark.ty model.pagesString = watermark.pageString // model.textColor = watermark.textColor model.isFront = watermark.isFront model.isTilePage = watermark.isTilePage model.tileVerticalSpace = watermark.verticalSpacing model.tileHorizontalSpace = watermark.horizontalSpacing if (watermark.type == .text) { model.text = watermark.text // model.textFont model.scale = watermark.scale * 24 } else if (watermark.type == .image) { model.image = watermark.image } if (watermark.verticalPosition == .top) { model.verticalMode = 0 } else if (watermark.verticalPosition == .center) { model.verticalMode = 1 } else if (watermark.verticalPosition == .bottom) { model.verticalMode = 2 } if (watermark.horizontalPosition == .left) { model.horizontalMode = 0 } else if (watermark.horizontalPosition == .center) { model.horizontalMode = 1 } else if (watermark.horizontalPosition == .right) { model.horizontalMode = 2 } model.watermark = watermark self.watermarkArray.append(model) } } } func deleteWatermarks(toPath: String, completion: @escaping (_ result: Bool) -> ()) -> () { if (self.preView.document.watermarks().count <= 0) { completion(false) } DispatchQueue.global().async { let array: Array = self.preView.document.watermarks() for model in array { self.preView.document.removeWatermark(model) } /// 保存到临时路径 let documentPath = NSTemporaryDirectory() let tempPath: String = "\(documentPath)/\(toPath.lastPathComponent)" if (FileManager.default.fileExists(atPath: tempPath)) { try?FileManager.default.removeItem(atPath: tempPath) } let result = self.preView.document.write(to: URL(fileURLWithPath: tempPath)) if (result) { if (FileManager.default.fileExists(atPath: toPath)) { try?FileManager.default.removeItem(atPath: toPath) } try?FileManager.default.moveItem(atPath: tempPath, toPath: toPath) } else { try?FileManager.default.removeItem(atPath: tempPath) } DispatchQueue.main.async { completion(result) } } } /// 开始加载loading func beginLoading() { DispatchQueue.main.async { [self] in self.maskView = NSButton() self.maskView.isBordered = false self.maskView.title = "" self.maskView.wantsLayer = true self.maskView.layer?.backgroundColor = NSColor(white: 1, alpha: 0.3 ).cgColor self.maskView.target = self self.maskView.action = #selector(maskViewClick) self.maskView.frame = (self.view.bounds) self.maskView.autoresizingMask = NSView.AutoresizingMask(rawValue: 18) self.view.addSubview(self.maskView) self.indicator = NSProgressIndicator() self.indicator.style = .spinning self.indicator.controlSize = .regular self.indicator.isIndeterminate = true self.view.addSubview(self.indicator) let indicatorSize: CGFloat = 32 self.indicator.frame = NSMakeRect(0.5*(NSWidth((self.view.frame))-indicatorSize), 0.5*(NSHeight((self.view.frame))-indicatorSize), indicatorSize, indicatorSize) self.maskView.autoresizingMask = NSView.AutoresizingMask(rawValue: 45) self.indicator.startAnimation(nil) } } /// 结束加载loading func endLoading() { DispatchQueue.main.async { self.indicator.stopAnimation(nil) self.indicator.removeFromSuperview() self.indicator = nil self.maskView.removeFromSuperview() self.maskView = nil } } @objc func maskViewClick() { } func findPagesString(_ model: KMWatermarkAdjectiveBaseModel) -> String{ if (model.pageRangeType == .all) { /// 全部页面 return "0-\(model.pageCount-1)" } else if (model.pageRangeType == .odd) { /// 奇数页面 var string: String = "" for i in 0 ..< model.pageCount { if (i % 2 == 1) { continue } string.append("\(i)") if (i != model.pageCount-1) { string.append(",") } } return string } else if (model.pageRangeType == .even) { /// 偶数页面 var string: String = "" for i in 0 ..< model.pageCount { if (i % 2 == 0) { continue } string.append("\(i)") if (i != model.pageCount-1) { string.append(",") } } return string } else { /// 自定义 return model.pageRangeString } } } extension KMWatermarkAdjectivePreViewBaseController: NSSplitViewDelegate { // func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { // if (dividerIndex == 0) { // return NSWidth(splitView.frame)-260 // } // return proposedMinimumPosition // } // // func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { // if (dividerIndex == 0) { // return NSWidth(splitView.frame)-260 // } // return proposedMaximumPosition // } func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool { return true } func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool { return true } func splitView(_ splitView: NSSplitView, resizeSubviewsWithOldSize oldSize: NSSize) { let dividerThickness: CGFloat = splitView.dividerThickness var leftRect: NSRect = splitView.subviews.first!.frame var rightRect: NSRect = splitView.subviews.last!.frame let newFrame: NSRect = splitView.frame rightRect.size.height = newFrame.size.height leftRect.origin.x = 0 leftRect.size.width = newFrame.size.width-rightRect.size.width-dividerThickness leftRect.size.height = newFrame.size.height rightRect.origin.x = leftRect.size.width splitView.subviews.first?.frame = leftRect splitView.subviews.last?.frame = rightRect } }