// // KMTextBoxController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/11/26. // import Cocoa import KMComponentLibrary class KMTextBoxController: NSViewController { @IBOutlet var fontBGView: NSView! @IBOutlet var fontLabel: NSTextField! @IBOutlet var fontNameSelect: ComponentSelect! @IBOutlet var fontStyleSelect: ComponentSelect! @IBOutlet var fontSizeSelect: ComponentSelect! @IBOutlet var fontAlignmentGroup: ComponentCSelectorGroup! //Color @IBOutlet var colorBGView: NSView! @IBOutlet var colorLabel: NSTextField! @IBOutlet var colorGroup: ComponentCColorGroup! @IBOutlet var borderColorGroup: ComponentCColorGroup! @IBOutlet var fillColorGroup: ComponentCColorGroup! @IBOutlet var opacitySlider: ComponentSlider! @IBOutlet var opacitySelect: ComponentSelect! //Line @IBOutlet var lineBGView: NSView! @IBOutlet var lineLabel: NSTextField! @IBOutlet var lineTypeSelector: ComponentCSelectorGroup! @IBOutlet var lineWidthSlider: ComponentSlider! @IBOutlet var lineWidthSelect: ComponentSelect! @IBOutlet var linedashInfoView: NSView! @IBOutlet var lineDashSlider: ComponentSlider! @IBOutlet var lineDashSelect: ComponentSelect! private var familyNames = CPDFFont.familyNames private let fontAlign_leftItem: ComponentCSelectorProperty = ComponentCSelectorProperty.init(size: .m, state: .normal, iconImage: NSImage(named: "fontAlign_left"), identifier: "fontAlign_left") private let fontAlign_centerItem: ComponentCSelectorProperty = ComponentCSelectorProperty.init(size: .m, state: .normal, iconImage: NSImage(named: "fontAlign_center"), identifier: "fontAlign_center") private let fontAlign_rightItem: ComponentCSelectorProperty = ComponentCSelectorProperty.init(size: .m, state: .normal, iconImage: NSImage(named: "fontAlign_right"), identifier: "fontAlign_right") private let fontAlign_justifyItem: ComponentCSelectorProperty = ComponentCSelectorProperty.init(size: .m, state: .normal, iconImage: NSImage(named: "fontAlign_justify"), identifier: "fontAlign_justify") private let solidProperty = ComponentCSelectorProperty.init(size: .s, state: .normal, text: "", iconImage: NSImage(named: "lineStyle_solid")) private let dashProperty = ComponentCSelectorProperty.init(size: .s, state: .normal, text: "", iconImage: NSImage(named: "lineStyle_dash")) private var annotations: [CPDFFreeTextAnnotation] = [] var pdfView: CPDFListView? //MARK: - func override func viewDidAppear() { super.viewDidAppear() opacitySlider.reloadData() lineWidthSlider.reloadData() lineDashSlider.reloadData() } override func viewDidLoad() { super.viewDidLoad() // Do view setup here. setupProperty() } func setupProperty() { //Font fontNameSelect.properties = ComponentSelectProperties(size: .s, state: .normal, text: "") if true { var menuItemArr: [ComponentMenuitemProperty] = [] for string in familyNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontNameSelect.updateMenuItemsArr(menuItemArr) } 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") if true { var sizeItemArr: [ComponentMenuitemProperty] = [] for string in KMHeaderFooterManager.getFontSize() { let item = ComponentMenuitemProperty(type: .normal, text: string + " pt", identifier: string) sizeItemArr.append(item) } fontSizeSelect.updateMenuItemsArr(sizeItemArr) } fontSizeSelect.delegate = self if true { let itemArr: [ComponentCSelectorProperty] = [fontAlign_leftItem, fontAlign_centerItem, fontAlign_rightItem, fontAlign_justifyItem] fontAlignmentGroup.updateItemProperty(itemArr) fontAlignmentGroup.delegate = self } //Color colorLabel.stringValue = KMLocalizedString("Color") colorLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") colorLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") if true { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: KMPDFWatermarkData.watermarkDefaultColors()[4]) colorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) colorGroup.delegate = self } if true { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: KMPDFWatermarkData.watermarkDefaultColors()[4]) borderColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) borderColorGroup.delegate = self } if true { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMPDFWatermarkData.watermarkDefaultColors()[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: KMPDFWatermarkData.watermarkDefaultColors()[4]) fillColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) fillColorGroup.delegate = self } opacitySlider.properties = ComponentSliderProperty(size: .m, percent: 1) opacitySlider.delegate = self opacitySelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "100%", textUnit: "%", regexString: "0123456789%") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["25%", "50%", "75%", "100%"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } opacitySelect.updateMenuItemsArr(opacityItems) } opacitySelect.delegate = self //Line lineLabel.stringValue = KMLocalizedString("Line") lineLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") lineLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") lineTypeSelector.updateItemProperty([solidProperty, dashProperty]) lineTypeSelector.delegate = self lineWidthSlider.properties = ComponentSliderProperty(size: .m, percent: 1) lineWidthSlider.delegate = self lineWidthSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "2", textUnit: " pt", regexString: "0123456789 pt") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["1 pt", "3 pt", "6 pt", "9 pt", "12 pt", "15 pt", "18 pt"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } lineWidthSelect.updateMenuItemsArr(opacityItems) } lineWidthSelect.delegate = self lineDashSlider.properties = ComponentSliderProperty(size: .m, percent: 1) lineDashSlider.delegate = self lineDashSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "2", textUnit: " pt", regexString: "0123456789 pt") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["1 pt", "3 pt", "6 pt", "9 pt", "12 pt", "15 pt", "18 pt"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } lineDashSelect.updateMenuItemsArr(opacityItems) } lineDashSelect.delegate = self } func reloadData() { guard let pdfView = self.pdfView else { return } self.annotations.removeAll() let allAnnotations: [CPDFAnnotation] = pdfView.activeAnnotations as? [CPDFAnnotation] ?? [] for annotation in allAnnotations { if annotation is CPDFFreeTextAnnotation { annotations.append((annotation as! CPDFFreeTextAnnotation)) } } if annotations.count == 0 { return } guard let firstAnnotation = annotations.first else { return } if annotations.count == 1 { //Font if true { let styleNames = CPDFFont.fontNames(forFamilyName: firstAnnotation.cFont.familyName) var menuItemArr: [ComponentMenuitemProperty] = [] for string in styleNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontStyleSelect.updateMenuItemsArr(menuItemArr) if firstAnnotation.cFont.styleName == nil { fontStyleSelect.selectItemAtIndex(0) } else { fontStyleSelect.properties.text = firstAnnotation.cFont.styleName fontStyleSelect.reloadData() } } fontSizeSelect.properties.text = String(format: "%.0f", firstAnnotation.fontSize) fontSizeSelect.reloadData() //Color colorGroup.currentColor = firstAnnotation.fontColor colorGroup.refreshUI() borderColorGroup.currentColor = firstAnnotation.borderColor borderColorGroup.refreshUI() fillColorGroup.currentColor = firstAnnotation.color fillColorGroup.refreshUI() fillColorGroup.refreshUI() let opacity = firstAnnotation.opacity opacitySlider.properties.percent = opacity opacitySlider.reloadData() opacitySelect.properties.text = String(format: "%.0f%@", opacity*100, "%") opacitySelect.reloadData() let border: CPDFBorder = firstAnnotation.border dashProperty.state = .normal solidProperty.state = .normal if border.style == .dashed { dashProperty.state = .pressed } else if border.style == .solid { solidProperty.state = .pressed } lineTypeSelector.reloadData() let percent = (border.lineWidth - 1)/17 lineWidthSlider.properties.percent = percent lineWidthSlider.reloadData() lineWidthSelect.properties.text = String(format: "%.0f%@", border.lineWidth, " pt") lineWidthSelect.reloadData() linedashInfoView.isHidden = true if border.style == .dashed { linedashInfoView.isHidden = false var dash = 1.0 for dashPattern in border.dashPattern { if let value = dashPattern as? CGFloat { dash = value break } } let percent: CGFloat = (CGFloat(dash) - 1)/17 lineDashSlider.properties.percent = percent lineDashSlider.reloadData() lineDashSelect.properties.text = String(format: "%.0f%@", CGFloat(dash), " pt") lineDashSelect.reloadData() } } else { // if true { // var multiColor: Bool = false // for annotationA in annotations { // for annotationB in annotations { // if annotationA != annotationB { // if annotationA.color != annotationB.color { // multiColor = true // break // } // } // } // if multiColor == true { // break // } // } // if multiColor == true { // colorGroup.currentColor = NSColor.clear // } else { // colorGroup.currentColor = firstAnnotation.color // } // colorGroup.refreshUI() // } // // if true { // var multiOpacity: Bool = false // for annotationA in annotations { // for annotationB in annotations { // if annotationA != annotationB { // if annotationA.opacity != annotationB.opacity { // multiOpacity = true // break // } // } // } // if multiOpacity == true { // break // } // } // // if multiOpacity { // colorSlider.properties.percent = 0 // colorSlider.reloadData() // // colorOpacitySelect.resetText("-") // } else { // let opacity = firstAnnotation.opacity // // colorSlider.properties.percent = opacity // colorSlider.reloadData() // // colorOpacitySelect.properties.text = String(format: "%.0f%@", opacity*100, "%") // colorOpacitySelect.reloadData() // } // } // // if true { // var multiStyle: Bool = false // for annotationA in annotations { // for annotationB in annotations { // if annotationA != annotationB { // if annotationA.borderStyle() != annotationB.borderStyle() { // multiStyle = true // break // } // } // } // if multiStyle == true { // break // } // } // // linedashInfoView.isHidden = true // if multiStyle { // dashProperty.state = .normal // solidProperty.state = .normal // // lineTypeSelector.reloadData() // } else { // let style = firstAnnotation.border.style // dashProperty.state = .normal // solidProperty.state = .normal // if style == .dashed { // dashProperty.state = .pressed // } else if style == .solid { // solidProperty.state = .pressed // } // lineTypeSelector.reloadData() // // if style == .dashed { // linedashInfoView.isHidden = false // } // } // } // // if true { // var multiLineWidth: Bool = false // for annotationA in annotations { // for annotationB in annotations { // if annotationA != annotationB { // if annotationA.borderStyle() != annotationB.borderStyle() { // multiLineWidth = true // break // } // } // } // if multiLineWidth == true { // break // } // } // if multiLineWidth { // lineWidthSlider.properties.percent = 0 // lineWidthSlider.reloadData() // // lineWidthSelect.resetText("-") // } else { // let border: CPDFBorder = firstAnnotation.border // // let percent = (border.lineWidth - 1)/17 // lineWidthSlider.properties.percent = percent // lineWidthSlider.reloadData() // // lineWidthSelect.properties.text = String(format: "%.0f%@", border.lineWidth, " pt") // lineWidthSelect.reloadData() // } // } // // if true { // var multiLineDash: Bool = false // for annotationA in annotations { // var dashA = 1.0 // for dashPattern in annotationA.border.dashPattern { // if let value = dashPattern as? CGFloat { // dashA = value // break // } // } // for annotationB in annotations { // if annotationA != annotationB { // var dashB = 1.0 // for dashPattern in annotationB.border.dashPattern { // if let value = dashPattern as? CGFloat { // dashB = value // break // } // } // if dashA != dashB { // multiLineDash = true // break // } // } // } // if multiLineDash == true { // break // } // } // if multiLineDash { // lineDashSlider.properties.percent = 0 // lineDashSlider.reloadData() // // lineDashSelect.resetText("-") // } else { // var dashA = 1.0 // for dashPattern in firstAnnotation.border.dashPattern { // if let value = dashPattern as? CGFloat { // dashA = value // break // } // } // // let percent = (dashA - 1)/17 // lineDashSlider.properties.percent = percent // lineDashSlider.reloadData() // // lineDashSelect.properties.text = String(format: "%.0f%@", dashA, " pt") // lineDashSelect.reloadData() // } // } } } //MARK: - Mouse override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) view.window?.makeFirstResponder(nil) } } //MARK: - ComponentCColorDelegate extension KMTextBoxController: ComponentCColorDelegate { func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) { if view == colorGroup { for annotation in self.annotations { annotation.updateFontColor(color, withPDFView: pdfView) } } else if view == borderColorGroup { for annotation in self.annotations { annotation.updateBorderColor(color, withPDFView: pdfView) } } else if view == fillColorGroup { for annotation in self.annotations { annotation.updateColor(color, withPDFView: pdfView) } } reloadData() } } //MARK: - ComponentSliderDelegate extension KMTextBoxController: ComponentSliderDelegate { func componentSliderDidUpdate(_ view: ComponentSlider) { if view == opacitySlider { let percent = view.properties.percent for annotation in self.annotations { annotation.updateOpacity(percent, withPDFView: pdfView) } } else if view == lineWidthSlider { let percent = view.properties.percent * 17 + 1 for annotation in self.annotations { annotation.updateLineWidth(percent, withPDFView: pdfView) } } else if view == lineDashSlider { let percent = view.properties.percent * 17 + 1 for annotation in self.annotations { annotation.updateDashPattern(percent, withPDFView: pdfView) } } reloadData() } } //MARK: - ComponentSelectDelegate extension KMTextBoxController: ComponentSelectDelegate { func componentSelectTextDidEndEditing(_ view: ComponentSelect, removeUnit text: String?) { if let result = text { if view == opacitySelect { let opacity = max(0, min(1, result.stringToCGFloat()/100)) for annotation in self.annotations { annotation.updateOpacity(opacity, withPDFView: pdfView) } } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } for annotation in self.annotations { annotation.updateLineWidth(value, withPDFView: pdfView) } } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } for annotation in self.annotations { annotation.updateDashPattern(value, withPDFView: pdfView) } } reloadData() } } func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) { if var result = menuItemProperty?.text { if let textUnit = view?.properties.textUnit { result = result.stringByDeleteCharString(textUnit) } if view == fontNameSelect { var styleName = "Regular" let styleNames = CPDFFont.fontNames(forFamilyName: result) if let first = styleNames.first { styleName = first } for annotation in self.annotations { annotation.updateFont(CPDFFont(familyName: result, fontStyle: styleName), withPDFView: pdfView) } } else if view == fontStyleSelect { let fontName = fontNameSelect.properties.text ?? "Helvetica" for annotation in self.annotations { annotation.updateFont(CPDFFont(familyName: fontName, fontStyle: result), withPDFView: pdfView) } } else if view == fontSizeSelect { let size = result.stringToCGFloat() for annotation in self.annotations { annotation.updateFontSize(size, withPDFView: pdfView) } } else if view == opacitySelect { let opacity = max(0, min(1, result.stringToCGFloat()/100)) for annotation in self.annotations { annotation.updateOpacity(opacity, withPDFView: pdfView) } } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } for annotation in self.annotations { annotation.updateLineWidth(value, withPDFView: pdfView) } } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } for annotation in self.annotations { annotation.updateDashPattern(value, withPDFView: pdfView) } } reloadData() } } } //MARK: - ComponentCSelectorGroupDelegate extension KMTextBoxController: ComponentCSelectorGroupDelegate { func componentCSelectorGroupDidChoose(_ view: ComponentCSelectorGroup, _ item: ComponentCSelectorItem) { if item.properties == solidProperty { for annotation in self.annotations { annotation.updateStyle(.solid, withPDFView: pdfView) } } else if item.properties == dashProperty { for annotation in self.annotations { annotation.updateStyle(.dashed, withPDFView: pdfView) } } reloadData() } }