// // KMNWatermarkPropertyController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/11/1. // import Cocoa import KMComponentLibrary @objc protocol KMNWatermarkPropertyControllerDelegate: AnyObject { @objc optional func watermarkPropertyControllerDidRemoveWatermark(_ controller: KMNWatermarkPropertyController, _ type: CPDFWatermarkType) //水印数据有更新 @objc optional func watermarkPropertyControllerDidUpdate(_ controller: KMNWatermarkPropertyController) //切换到模板界面 @objc optional func watermarkPropertyControllerDidChangetoTemplate(_ controller: KMNWatermarkPropertyController) //成功保存到模板 @objc optional func watermarkPropertyControllerSaveTemplateSuccess(_ controller: KMNWatermarkPropertyController, _ data: KMPDFWatermarkData) //取消修改模板信息 @objc optional func watermarkPropertyControllerCancelTemplateEdit(_ controller: KMNWatermarkPropertyController) } class KMNWatermarkPropertyController: NSViewController { @IBOutlet var contendView: NSView! @IBOutlet var leftTopButton: ComponentButton! @IBOutlet var titleLabel: NSTextField! @IBOutlet var templateButton: ComponentButton! @IBOutlet var typeTabsBGView: NSView! @IBOutlet var typeTabs: ComponentTabs! @IBOutlet var infoContendView: NSView! @IBOutlet var infoScrollView: KMScrollView! @IBOutlet var infoScrollTopConst: NSLayoutConstraint! @IBOutlet var infoContendBottomConst: NSLayoutConstraint! //Text @IBOutlet var textBGView: NSView! @IBOutlet var text_textareaView: ComponentTextarea! @IBOutlet var fontNameSelect: ComponentSelect! @IBOutlet var fontStyleSelect: ComponentSelect! @IBOutlet var fontSizeSelect: ComponentSelect! @IBOutlet var fontColorGroup: ComponentCColorGroup! //File @IBOutlet var fileBGView: NSView! @IBOutlet var fileInputView: ComponentInput! @IBOutlet var fileInputAddonView: ComponentInputAddon! //Appearance @IBOutlet var appearanceBGView: NSView! @IBOutlet var appearanceLabel: NSTextField! @IBOutlet var appearance_RotateSelect: ComponentSelect! @IBOutlet var appearance_OpacitySelect: ComponentSelect! @IBOutlet var appearanceScaleCheckbox: ComponentCheckBox! @IBOutlet var appearanceScaleSelect: ComponentSelect! @IBOutlet var appearanceTopPageRadio: ComponentRadio! @IBOutlet var appearanceBottomPageRadio: ComponentRadio! @IBOutlet var appearanceBGTopConst: NSLayoutConstraint! //Position @IBOutlet var positionBGView: NSView! @IBOutlet var positionLabel: NSTextField! @IBOutlet var positionItemView: ComponentCPosition! @IBOutlet var positionXInput: ComponentInputNumber! @IBOutlet var positionYInput: ComponentInputNumber! @IBOutlet var positionTileCheckbox: ComponentCheckBox! @IBOutlet var positionTileHorImage: NSImageView! @IBOutlet var positionTileVertImage: NSImageView! @IBOutlet var positionTileHoriInput: ComponentInputNumber! @IBOutlet var positionTileVertInput: ComponentInputNumber! //Save @IBOutlet var saveTemplateBGView: NSView! @IBOutlet var saveButton: ComponentButton! private var textTabProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: KMLocalizedString("Text")) private var fileTabProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: KMLocalizedString("File")) private var familyNames = CPDFFont.familyNames var watermarkData: KMPDFWatermarkData = KMWatermarkManager.defaultManager.defaultWatermarkData public var editSubType: KMPDFEditSubModeType = .add public var isInBatchMode: Bool = false var originalDataDict: Dictionary? weak open var delegate: KMNWatermarkPropertyControllerDelegate? //MARK: - func override func viewDidLoad() { super.viewDidLoad() // Do view setup here. infoScrollView.documentView = infoContendView infoScrollView.scrollerStyle = .overlay infoScrollView.layout() setupProperty() reloadData() refreshScrollView() } func setupProperty() { contendView.wantsLayer = true contendView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold") leftTopButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "watermark_arrowLeft"), keepPressState: false) leftTopButton.setTarget(self, action: #selector(leftTopButtonClicked(_:))) templateButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "icon_wm_template"), keepPressState: false) templateButton.setTarget(self, action: #selector(templateButtonClicked(_:))) typeTabs.updateItemProperty([textTabProperty, fileTabProperty]) typeTabs.delegate = self //Text text_textareaView.properties = ComponentTextareaProperty(size: .s, placeholderString: KMLocalizedString("Please enter..."), totalCount: -1, text: "") text_textareaView.delegate = self fontNameSelect.properties = ComponentSelectProperties(size: .s, state: .normal, text: "") var menuItemArr: [ComponentMenuitemProperty] = [] for string in familyNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontNameSelect.updateMenuItemsArr(menuItemArr) fontNameSelect.selectItemAtIndex(0) fontNameSelect.delegate = self fontStyleSelect.properties = ComponentSelectProperties(size: .s, state: .normal, text: "") fontStyleSelect.delegate = self fontSizeSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "12 pt", textUnit: " pt", regexString: "0123456789 pt") var sizeItemArr: [ComponentMenuitemProperty] = [] for string in ["6","8","10","12","14", "16","18","20","22","24", "26","28","30","32","34", "36","40","48","64","80", "96","112"] { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) sizeItemArr.append(item) } fontSizeSelect.updateMenuItemsArr(sizeItemArr) fontSizeSelect.selectItemAtIndex(0) fontSizeSelect.delegate = self let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[4]) fontColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) fontColorGroup.delegate = self //File fileInputView.properties = ComponentInputProperty(size: .s, state:.pressed , placeholder: "Select Source File...", text: "", creatable: false) fileInputView.properties.propertyInfo.cornerRadius_topLeft = 0 fileInputView.properties.propertyInfo.cornerRadius_topRight = 0 fileInputView.properties.propertyInfo.cornerRadius_bottomLeft = 0 fileInputView.properties.propertyInfo.cornerRadius_bottomRight = 0 fileInputView.reloadData() fileInputView.delegate = self fileInputAddonView.properties = ComponentInputAddonProperty(size: .s, state: .normal, addOnBefore: false, onlyRead: false, addonType: .imageWithColor, iconImage: NSImage(named: "icon_folder")) fileInputAddonView.setTarget(self, action: #selector(chooseURLAction(_ :))) //Appearance appearanceLabel.stringValue = KMLocalizedString("Appearance") appearanceLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") appearanceLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") appearance_RotateSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "0°", textUnit: "°", regexString: "0123456789°-") var rotateItems: [ComponentMenuitemProperty] = [] for string in ["0°", "45°", "-45°", "90°", "-90°"] { let item = ComponentMenuitemProperty(type: .normal, text: string) rotateItems.append(item) } appearance_RotateSelect.updateMenuItemsArr(rotateItems) appearance_RotateSelect.delegate = self appearance_OpacitySelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "100%", textUnit: "%", regexString: "0123456789%") var opacityItems: [ComponentMenuitemProperty] = [] for string in ["25%", "50%", "75%", "100%"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } appearance_OpacitySelect.updateMenuItemsArr(opacityItems) appearance_OpacitySelect.delegate = self appearanceScaleCheckbox.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("Scale relative to target page"), checkboxType: .normal) appearanceScaleCheckbox.setTarget(self, action: #selector(checkBoxClicked(_:))) appearanceScaleSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "100%", textUnit: "%", regexString: "0123456789%") var appearanceScaleItems: [ComponentMenuitemProperty] = [] for string in ["25%", "50%", "75%", "100%", "125%", "150%","200%"] { let item = ComponentMenuitemProperty(type: .normal, text: string) appearanceScaleItems.append(item) } appearanceScaleSelect.updateMenuItemsArr(appearanceScaleItems) appearanceScaleSelect.delegate = self appearanceTopPageRadio.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("At the top of the page"), checkboxType: .normal) appearanceTopPageRadio.setTarget(self, action: #selector(radioClicked(_:))) appearanceBottomPageRadio.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("At the bottom of the page"), checkboxType: .normal) appearanceBottomPageRadio.setTarget(self, action: #selector(radioClicked(_:))) //Position positionLabel.stringValue = KMLocalizedString("Position (mm)") positionLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") positionLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") positionItemView.properties = ComponentCPositionProperty(rowCount: 3, columnCount: 3, dash: false, selRow: 1, selColumn: 1) positionItemView.delegate = self positionXInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: -1000, maxSize: 1000, text: "0") positionXInput.delegate = self positionYInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: -1000, maxSize: 1000, text: "0") positionYInput.delegate = self positionTileCheckbox.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("Tile"), checkboxType: .normal) positionTileCheckbox.setTarget(self, action: #selector(checkBoxClicked(_:))) positionTileHoriInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") positionTileHoriInput.delegate = self positionTileVertInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") positionTileVertInput.delegate = self //Save saveTemplateBGView.wantsLayer = true saveTemplateBGView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor saveButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .m, showLeftIcon: true, buttonText: KMLocalizedString("Save as Template"), icon: NSImage(named: "icon_wm_template_save"), keepPressState: false) saveButton.setTarget(self, action: #selector(saveButtonClicked(_:))) } func refreshScrollView() { infoScrollView.contentViewSize = CGSizeMake(264, 672) if watermarkData.watermarkType == .text { infoScrollView.contentViewSize = CGSizeMake(264, 672) } else if watermarkData.watermarkType == .image { infoScrollView.contentViewSize = CGSizeMake(264, 512) } infoScrollView.scrollToTop() } func reloadData() { titleLabel.stringValue = KMLocalizedString("Add Watermark") leftTopButton.isHidden = true saveTemplateBGView.isHidden = false templateButton.properties.icon = NSImage(named: "icon_wm_template") typeTabsBGView.isHidden = false infoScrollTopConst.constant = 56 if editSubType == .edit { typeTabsBGView.isHidden = true infoScrollTopConst.constant = 8 titleLabel.stringValue = KMLocalizedString("Edit Template") leftTopButton.isHidden = false saveTemplateBGView.isHidden = true templateButton.properties.icon = NSImage(named: "edit_save") } templateButton.reloadData() if self.isInBatchMode { infoContendBottomConst.constant = 0 saveTemplateBGView.isHidden = true } else { infoContendBottomConst.constant = 72 saveTemplateBGView.isHidden = false } fileBGView.isHidden = true textBGView.isHidden = true textTabProperty.state = .normal fileTabProperty.state = .normal if watermarkData.watermarkType == .text { textBGView.isHidden = false appearanceBGTopConst.constant = 200 textTabProperty.state = .pressed } else if watermarkData.watermarkType == .image { fileBGView.isHidden = false appearanceBGTopConst.constant = 48 fileTabProperty.state = .pressed } typeTabs.refreshItems() //Text text_textareaView.properties.text = watermarkData.text ?? "" text_textareaView.reloadData() if let index = familyNames.firstIndex(of: watermarkData.fontName) { fontNameSelect.selectItemAtIndex(index) } fontNameSelect.properties.text = watermarkData.fontName fontNameSelect.reloadData() let styleNames = CPDFFont.fontNames(forFamilyName: watermarkData.fontName) var menuItemArr: [ComponentMenuitemProperty] = [] for string in styleNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontStyleSelect.updateMenuItemsArr(menuItemArr) if watermarkData.fontStyle == nil { fontStyleSelect.selectItemAtIndex(0) watermarkData.fontStyle = styleNames.first } else { fontStyleSelect.properties.text = watermarkData.fontStyle fontStyleSelect.reloadData() } positionTileCheckbox.properties.isDisabled = false let fontSizeStr = String(format: "%.0f", watermarkData.fontSize) if let index = KMWatermarkManager.getFontSize().firstIndex(of: fontSizeStr) { fontSizeSelect.selectItemAtIndex(index) } fontSizeSelect.properties.text = String(format: "%.0f", watermarkData.fontSize) + " pt" fontSizeSelect.properties.isDisabled = false if watermarkData.isScale { fontSizeSelect.properties.isDisabled = true positionTileCheckbox.properties.isDisabled = true watermarkData.isTilePage = false } fontSizeSelect.reloadData() fontColorGroup.currentColor = watermarkData.textColor fontColorGroup.refreshUI() //File fileInputView.properties.text = watermarkData.imagePath ?? "" fileInputView.reloadData() //Appearance appearance_RotateSelect.properties.text = String(format: "%.0f", watermarkData.rotation) + "°" appearance_RotateSelect.reloadData() if watermarkData.opacity > 1 { watermarkData.opacity = 1 } appearance_OpacitySelect.properties.text = String(format: "%.0f", watermarkData.opacity*100) + "%" appearance_OpacitySelect.reloadData() appearanceScaleCheckbox.properties.checkboxType = watermarkData.isScale ? .selected : .normal appearanceScaleCheckbox.reloadData() appearanceScaleSelect.properties.isDisabled = watermarkData.isScale ? false : true appearanceScaleSelect.properties.text = String(format: "%.0f", watermarkData.scale*100) + "%" appearanceScaleSelect.reloadData() appearanceTopPageRadio.properties.checkboxType = watermarkData.isFront ? .selected : .normal appearanceTopPageRadio.reloadData() appearanceBottomPageRadio.properties.checkboxType = watermarkData.isFront ? .normal : .selected appearanceBottomPageRadio.reloadData() //Position positionXInput.properties.text = String(format: "%.0f", watermarkData.tx) positionXInput.reloadData() positionYInput.properties.text = String(format: "%.0f", watermarkData.ty) positionYInput.reloadData() positionTileHoriInput.properties.text = String(format: "%.0f", watermarkData.horizontalSpacing) positionTileVertInput.properties.text = String(format: "%.0f", watermarkData.verticalSpacing) if watermarkData.isTilePage { positionTileHorImage.image = NSImage(named: "tile_spacing_horiz") positionTileVertImage.image = NSImage(named: "tile_spacing_vert") positionTileHoriInput.properties.isDisabled = false positionTileVertInput.properties.isDisabled = false } else { positionTileHorImage.image = NSImage(named: "tile_spacing_horiz_dis") positionTileVertImage.image = NSImage(named: "tile_spacing_vert_dis") positionTileHoriInput.properties.isDisabled = true positionTileVertInput.properties.isDisabled = true } positionTileHoriInput.reloadData() positionTileVertInput.reloadData() positionTileCheckbox.properties.checkboxType = watermarkData.isTilePage ? .selected : .normal positionTileCheckbox.reloadData() saveButton.properties.isDisabled = false if watermarkData.watermarkType == .text { if watermarkData.text?.count == 0 { saveButton.properties.isDisabled = true } } else if watermarkData.watermarkType == .image { if watermarkData.imagePath == nil { saveButton.properties.isDisabled = true } } saveButton.reloadData() } func clearData() { watermarkData = KMPDFWatermarkData() reloadData() } //MARK: - Action @objc func leftTopButtonClicked(_ sender: ComponentButton) { if sender == leftTopButton { var isChanged = false if let dict = self.originalDataDict { isChanged = KMWatermarkManager.compareIsChangedModel(watermarkData, withDict: dict as NSDictionary) } if isChanged == true { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = KMLocalizedString("Save template changes?") alert.informativeText = KMLocalizedString("Cancel and they will not be saved.") alert.addButton(withTitle: KMLocalizedString("Save")) alert.addButton(withTitle: KMLocalizedString("Cancel")) alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in if response == NSApplication.ModalResponse.alertFirstButtonReturn { self.templateButtonClicked(self.templateButton) } else { if let dict = self.originalDataDict { KMWatermarkManager.defaultManager.setDictToWatermarK(dict: dict as NSDictionary, self.watermarkData) let _ = KMWatermarkManager.defaultManager.updateWatermark(watermark: self.watermarkData) } self.delegate?.watermarkPropertyControllerCancelTemplateEdit?(self) } } } else { delegate?.watermarkPropertyControllerCancelTemplateEdit?(self) } } } @objc func templateButtonClicked(_ sender: ComponentButton) { if editSubType == .edit { let _ = KMWatermarkManager.defaultManager.updateWatermark(watermark: watermarkData) delegate?.watermarkPropertyControllerSaveTemplateSuccess?(self, self.watermarkData) } else { if watermarkData.watermarkType == .image, let _ = watermarkData.imagePath { watermarkData.text = nil watermarkData.watermarkType = .image } else { watermarkData.watermarkType = .text watermarkData.imagePath = nil } delegate?.watermarkPropertyControllerDidChangetoTemplate?(self) } } @objc func checkBoxClicked(_ sender: ComponentCheckBox) { if sender == appearanceScaleCheckbox { watermarkData.isScale = appearanceScaleCheckbox.properties.checkboxType == .selected ? true : false } else if sender == positionTileCheckbox { watermarkData.isTilePage = positionTileCheckbox.properties.checkboxType == .selected ? true : false } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } @objc func radioClicked(_ sender: ComponentRadio) { if sender == appearanceTopPageRadio { watermarkData.isFront = true } else if sender == appearanceBottomPageRadio { watermarkData.isFront = false } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } @objc func saveButtonClicked(_ sender: ComponentButton) { let saveWindow: KMWatermarkSaveWindow = KMWatermarkSaveWindow(windowNibName: "KMWatermarkSaveWindow") if watermarkData.watermarkType == .image { saveWindow.nameValue = watermarkData.imagePath?.lastPathComponent ?? "" } else if watermarkData.watermarkType == .text { saveWindow.nameValue = watermarkData.text ?? "" } saveWindow.saveHandler = {[weak self] string in guard let weakSelf = self else { return } DispatchQueue.main.async { weakSelf.watermarkData.watermarkName = string ?? "" if KMWatermarkManager.defaultManager.addWatermark(watermark: weakSelf.watermarkData) == true { weakSelf.delegate?.watermarkPropertyControllerSaveTemplateSuccess?(weakSelf, weakSelf.watermarkData) } } } saveWindow.own_beginSheetModal(for: view.window) { string in } } @objc func chooseURLAction(_ sender: NSView) { self.chooseURLAction(sender, nil) } func chooseURLAction(_ sender: NSView, _ filePath: String? = nil) { let openPanel = NSOpenPanel() openPanel.canChooseDirectories = false openPanel.canChooseFiles = true openPanel.allowsMultipleSelection = false openPanel.allowedFileTypes = ["jpg", "jpeg", "png", "pdf"] openPanel.beginSheetModal(for: self.view.window!) { (result) in if result == NSApplication.ModalResponse.OK { guard let url = openPanel.url else { return } let filePath = url.path let outFolder = KMWatermarkManager.defaultManager.watermarkImageFolder let fileName = filePath.getLastComponentDeleteExtension if filePath.extension.lowercased() == ".pdf" { let pdf = CPDFDocument(url: url) guard !pdf!.isEncrypted else { return } if let image = self.generateThumbnail(for: url) { if let outFolderPath = outFolder?.stringByAppendingPathComponent(fileName + ".png") { try?image.pngData()?.write(to: URL(fileURLWithPath: outFolderPath)) if FileManager.default.fileExists(atPath: outFolderPath) { self.watermarkData.imagePath = outFolderPath } self.delegate?.watermarkPropertyControllerDidUpdate?(self) self.reloadData() } } } else { let image = NSImage(contentsOfFile: url.path) if image == nil || NSImage.isDamageImage(image, imagePath: url.path) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "The file \"\(url.lastPathComponent)\" could not be opened." alert.informativeText = "It may be damaged or use a file format that PDF Reader Pro doesn’t recognize." alert.addButton(withTitle: "Cancel") alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in if response == NSApplication.ModalResponse.alertFirstButtonReturn { // Cancel button clicked } } return } if let resultPath = outFolder?.stringByAppendingPathComponent(fileName + ".png") { try?FileManager.default.copyItem(atPath: url.path, toPath: resultPath) self.watermarkData.imagePath = resultPath } self.delegate?.watermarkPropertyControllerDidUpdate?(self) self.reloadData() } } } } func generateThumbnail(for pdfURL: URL) -> NSImage? { // 创建PDF文档 guard let pdfDocument = CGPDFDocument(pdfURL as CFURL), let pdfPage = pdfDocument.page(at: 1) else { print("打开PDF文件失败") return nil } // 获取页面尺寸 let pageRect = pdfPage.getBoxRect(.mediaBox) // 创建图形上下文 let size = NSSize(width: 200, height: 300) let bitsPerComponent = 8 let bytesPerPixel = 4 let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: bitsPerComponent, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo) // 绘制PDF页面 context?.saveGState() context?.translateBy(x: 0, y: size.height) context?.scaleBy(x: 1.0, y: -1.0) // 反转坐标系 context?.scaleBy(x: size.width / pageRect.width, y: size.height / pageRect.height) // 缩放 context?.drawPDFPage(pdfPage) context?.restoreGState() // 创建NSImage if let cgImage = context?.makeImage() { return NSImage(cgImage: cgImage, size: NSSize(width: size.width, height: size.height)) } else { return nil } } //MARK: - Mouse override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) view.window?.makeFirstResponder(nil) } } //MARK: - ComponentTabsDelegate extension KMNWatermarkPropertyController: ComponentTabsDelegate { func componentTabsDidSelected(_ view: ComponentTabs, _ property: ComponentTabsProperty) { if property == textTabProperty { if watermarkData.watermarkType != .text { delegate?.watermarkPropertyControllerDidRemoveWatermark?(self, .text) } watermarkData.watermarkType = .text } else if property == fileTabProperty { if watermarkData.watermarkType != .image { delegate?.watermarkPropertyControllerDidRemoveWatermark?(self, .image) } watermarkData.watermarkType = .image } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) refreshScrollView() } } //MARK: - ComponentTextareaDelegate extension KMNWatermarkPropertyController: ComponentTextareaDelegate { func componentTextareaTextDidChange(_ view: ComponentTextarea) { watermarkData.text = view.properties.text delegate?.watermarkPropertyControllerDidUpdate?(self) } func componentTextareaTextDidEndEditing(_ view: ComponentTextarea) { watermarkData.text = view.properties.text delegate?.watermarkPropertyControllerDidUpdate?(self) } } //MARK: - ComponentCColorDelegate extension KMNWatermarkPropertyController: ComponentCColorDelegate { func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) { if view == fontColorGroup { watermarkData.textColor = color ?? NSColor.clear } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } } //MARK: - ComponentSelectDelegate extension KMNWatermarkPropertyController: ComponentSelectDelegate { func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) { if view == fontNameSelect { watermarkData.fontName = menuItemProperty?.text ?? "Helvetica" watermarkData.fontStyle = nil } else if view == fontStyleSelect { watermarkData.fontStyle = menuItemProperty?.text } else if view == fontSizeSelect { if let text = menuItemProperty?.text { let result = text.stringByDeleteCharString(" pt") watermarkData.fontSize = result.stringToCGFloat() } } else if view == appearance_OpacitySelect { if let text = menuItemProperty?.text { let result = text.stringByDeleteCharString("%") watermarkData.opacity = result.stringToCGFloat()/100 } } else if view == appearance_RotateSelect { if let text = menuItemProperty?.text { let result = text.stringByDeleteCharString("°") watermarkData.rotation = result.stringToCGFloat() } } else if view == appearanceScaleSelect { if let text = menuItemProperty?.text { let result = text.stringByDeleteCharString("%") watermarkData.scale = result.stringToCGFloat()/100 } } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } func componentSelectTextDidChange(_ view: ComponentSelect) { if view == fontSizeSelect { if let string = view.properties.text { let size = string.stringToCGFloat() watermarkData.fontSize = size delegate?.watermarkPropertyControllerDidUpdate?(self) } } } func componentSelectTextDidEndEditing(_ view: ComponentSelect) { if view == fontSizeSelect { if let string = view.properties.text { let result = string.stringByDeleteCharString(" pt") let size = result.stringToCGFloat() watermarkData.fontSize = size } } else if view == appearance_OpacitySelect { if let text = appearance_OpacitySelect.properties.text { let result = text.stringByDeleteCharString("%") watermarkData.opacity = result.stringToCGFloat()/100 } } else if view == appearance_RotateSelect { if let text = appearance_RotateSelect.properties.text { let result = text.stringByDeleteCharString("°") watermarkData.rotation = result.stringToCGFloat() } } else if view == appearanceScaleSelect { if let text = appearanceScaleSelect.properties.text { let result = text.stringByDeleteCharString("%") watermarkData.scale = result.stringToCGFloat()/100 } } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } } //MARK: - ComponentCPositionDelegate extension KMNWatermarkPropertyController: ComponentCPositionDelegate { func componentCPositionDidChoose(_ view: NSView, _ row: Int, _ column: Int) { if view == positionItemView { if row == 0 { watermarkData.verticalPosition = .bottom } else if row == 1 { watermarkData.verticalPosition = .center } else if row == 2 { watermarkData.verticalPosition = .top } if column == 0 { watermarkData.horizontalPosition = .left } else if column == 1 { watermarkData.horizontalPosition = .center } else if column == 2 { watermarkData.horizontalPosition = .right } } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } } //MARK: - ComponentInputNumberDelegate extension KMNWatermarkPropertyController: ComponentInputNumberDelegate { func componentInputNumberDidValueChanged(inputNumber: ComponentInputNumber?) { if inputNumber == positionXInput { if let text = inputNumber?.properties.text { watermarkData.tx = text.stringToCGFloat() } } else if inputNumber == positionYInput { if let text = inputNumber?.properties.text { watermarkData.ty = text.stringToCGFloat() } } else if inputNumber == positionTileHoriInput { if let text = inputNumber?.properties.text { watermarkData.horizontalSpacing = text.stringToCGFloat() } } else if inputNumber == positionTileVertInput { if let text = inputNumber?.properties.text { watermarkData.verticalSpacing = text.stringToCGFloat() } } reloadData() delegate?.watermarkPropertyControllerDidUpdate?(self) } } //MARK: - ComponentInputDelegate extension KMNWatermarkPropertyController: ComponentInputDelegate { func componentInputDidCoverButtonClicked(inputView: ComponentInput) { if inputView.properties.text.count > 0 && inputView.properties.creatable == false { self.chooseURLAction(inputView, inputView.properties.text) } } }