// // KMTextBoxController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/11/26. // import Cocoa import KMComponentLibrary class KMTextBoxController: NSViewController { //Font @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 fontColorGroup: 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 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 fontLabel.stringValue = KMLocalizedString("Font") fontLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") fontLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") 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] 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") fontColorGroup.delegate = self borderColorGroup.delegate = self 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", "2 pt", "4 pt", "6 pt", "8 pt", "10 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", "2 pt", "4 pt", "6 pt", "8 pt", "10 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 } if true { let colors: [NSColor] = KMAnnotationPropertiesColorManager.manager.freeTextColors if colors.count > 4 { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) fontColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) } } if true { let colors: [NSColor] = KMAnnotationPropertiesColorManager.manager.freeText_BorderColors if colors.count > 4 { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) borderColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) } } if true { let colors: [NSColor] = KMAnnotationPropertiesColorManager.manager.freeText_FillColors if colors.count > 4 { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) fillColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) } } self.annotations.removeAll() let allAnnotations: [CPDFAnnotation] = pdfView.activeAnnotations as? [CPDFAnnotation] ?? [] for annotation in allAnnotations { if annotation is CPDFFreeTextAnnotation { annotations.append((annotation as! CPDFFreeTextAnnotation)) } } var firstAnnotation: CPDFFreeTextAnnotation? if annotations.count > 0 { firstAnnotation = annotations.first } //Font var fontNameString: String? var fontStyleString: String? var fontSize: CGFloat? var alignment: NSTextAlignment? //Color var fontColor: NSColor? var borderColor: NSColor? var fillColor: NSColor? var opacity: CGFloat? //Line var lineStyle: CPDFBorderStyle? var lineWidth: CGFloat? var dashPattern: CGFloat? if annotations.count == 0 { //Font fontNameString = CPDFFreeTextAnnotation.defaultFontName() fontStyleString = CPDFFreeTextAnnotation.defaultFontStyle() fontSize = CPDFFreeTextAnnotation.defaultFontSize() alignment = CPDFFreeTextAnnotation.defaultFontAlignment() //Color fontColor = CPDFFreeTextAnnotation.defaultFontColor() borderColor = CPDFFreeTextAnnotation.defaultBorderColor() fillColor = CPDFFreeTextAnnotation.defaultColor() opacity = CPDFFreeTextAnnotation.defaultOpacity() //Line lineStyle = CPDFFreeTextAnnotation.defaultLineStyle() lineWidth = CPDFFreeTextAnnotation.defaultLineWidth() var dash = 1.0 for dashPattern in CPDFFreeTextAnnotation.defaultDashPattern() { if let value = dashPattern as? CGFloat { dash = value break } } dashPattern = dash } else if annotations.count == 1, let annotation = firstAnnotation { //Font fontNameString = annotation.cFont.familyName fontStyleString = annotation.cFont.styleName fontSize = annotation.fontSize alignment = annotation.alignment //Color fontColor = annotation.fontColor borderColor = annotation.borderColor fillColor = annotation.color opacity = annotation.opacity //Line lineStyle = annotation.border?.style lineWidth = annotation.border?.lineWidth var dash = 1.0 for dashPattern in annotation.dashPattern() { if let value = dashPattern as? CGFloat { dash = value break } } dashPattern = dash } else { guard let annotation = firstAnnotation else { return } //Font if true { let multiFontName = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .font_Name) if multiFontName { fontNameString = nil } else { fontNameString = annotation.cFont.familyName } let multiFontStyle = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .font_Style) if multiFontStyle { fontStyleString = nil } else { fontStyleString = annotation.cFont.styleName } let multifontSize = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .font_Size) if multifontSize { fontSize = nil } else { fontSize = annotation.fontSize } let multiAlignment = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .font_Alignment) if multiAlignment { alignment = nil } else { alignment = annotation.alignment } } //Color if true { let multiFontColor: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .font_Color) if multiFontColor == true { fontColor = nil } else { fontColor = annotation.fontColor } let multiBorderColor: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .border_Color) if multiBorderColor == true { borderColor = nil } else { borderColor = annotation.borderColor } let multiFillColor: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .color) if multiFillColor == true { fillColor = nil } else { fillColor = annotation.color } let multiOpacity: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .opacity) if multiOpacity == true { opacity = nil } else { opacity = annotation.opacity } } //Line if true { let multiStyle: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .border_Style) if multiStyle == true { lineStyle = nil } else { lineStyle = annotation.border?.style } let multiLineWidth: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .line_Width) if multiLineWidth == true { lineWidth = nil } else { lineWidth = annotation.border?.lineWidth } let multiDashPattern: Bool = CPDFListView.isAnnotationsContainMultiType(annotations, withType: .line_Width) if multiDashPattern == true { dashPattern = nil } else { if let firstValue = annotation.border?.dashPattern.first { dashPattern = firstValue as? CGFloat } } } } //Font if let value = fontNameString { fontNameSelect.properties.text = value } else { fontNameSelect.properties.text = "-" } fontNameSelect.reloadData() if let familyName = fontNameSelect.properties.text, familyName != "-" { var styleNames = CPDFFont.fontNames(forFamilyName: familyName) var menuItemArr: [ComponentMenuitemProperty] = [] if styleNames.count == 0 { styleNames.append("Regular") } for string in styleNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontStyleSelect.updateMenuItemsArr(menuItemArr) } else { fontStyleSelect.updateMenuItemsArr([]) } if let value = fontStyleString { fontStyleSelect.properties.text = value } else { fontStyleSelect.properties.text = "-" } fontStyleSelect.reloadData() if let value = fontSize { fontSizeSelect.properties.text = String(format: "%.0f", value) } else { fontSizeSelect.properties.text = "-" } fontSizeSelect.reloadData() if let value = alignment { fontAlign_leftItem.state = value == .left ? .pressed : .normal fontAlign_centerItem.state = value == .center ? .pressed : .normal fontAlign_rightItem.state = value == .right ? .pressed : .normal } else { fontAlign_leftItem.state = .normal fontAlign_centerItem.state = .normal fontAlign_rightItem.state = .normal } fontAlignmentGroup.reloadData() //Color fontColorGroup.currentColor = fontColor fontColorGroup.refreshUI() borderColorGroup.currentColor = borderColor borderColorGroup.refreshUI() fillColorGroup.currentColor = fillColor fillColorGroup.refreshUI() if let value = opacity { opacitySlider.properties.percent = value opacitySelect.properties.text = String(format: "%.0f%@", value*100, "%") } else { opacitySlider.properties.percent = 0 opacitySelect.properties.text = "-" } opacitySlider.reloadData() opacitySelect.reloadData() //Line linedashInfoView.isHidden = true if let value = lineStyle { let style = value dashProperty.state = .normal solidProperty.state = .normal if style == .dashed { dashProperty.state = .pressed } else if style == .solid { solidProperty.state = .pressed } if style == .dashed { linedashInfoView.isHidden = false } } else { dashProperty.state = .normal solidProperty.state = .normal } if let value = lineWidth { let percent = value/18 lineWidthSlider.properties.percent = percent lineWidthSlider.reloadData() lineWidthSelect.properties.text = String(format: "%.0f%@", value, " pt") lineWidthSelect.reloadData() } else { lineWidthSlider.properties.percent = 0 lineWidthSlider.reloadData() lineWidthSelect.resetText("-") } if let value = dashPattern { let percent = value/18 lineDashSlider.properties.percent = percent lineDashSlider.reloadData() lineDashSelect.properties.text = String(format: "%.0f%@", value, " pt") lineDashSelect.reloadData() } else { lineDashSlider.properties.percent = 0 lineDashSlider.reloadData() lineDashSelect.resetText("-") } } //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 == fontColorGroup { CPDFAnnotation.updateAnnotations(annotations, newFontColor: color, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontColor_Info(color) } else if view == borderColorGroup { CPDFAnnotation.updateAnnotations(annotations, newBorderColor: color, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultBorderColor_Info(color) } else if view == fillColorGroup { CPDFAnnotation.updateAnnotations(annotations, newColor: color, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultColor_Info(color) } reloadData() } } //MARK: - ComponentSliderDelegate extension KMTextBoxController: ComponentSliderDelegate { func componentSliderDidUpdate(_ view: ComponentSlider) { if view == opacitySlider { let opacity = view.properties.percent CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultOpacity_Info(opacity) } else if view == lineWidthSlider { let value = view.properties.percent * 18 CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultLineWidth_Info(value) } else if view == lineDashSlider { let value = view.properties.percent * 18 CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultDashPattern_Info(value) } 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)) CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultOpacity_Info(opacity) } else if view == fontSizeSelect { let value = result.stringToCGFloat() CPDFAnnotation.updateAnnotations(annotations, newFontSize: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontSize_Info(value) } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultLineWidth_Info(value) } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultDashPattern_Info(value) } 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 } CPDFAnnotation.updateAnnotations(annotations, newCFont: CPDFFont(familyName: result, fontStyle: styleName), withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFont_Info(CPDFFont(familyName: result, fontStyle: styleName)) } else if view == fontStyleSelect { let fontName = fontNameSelect.properties.text ?? "Helvetica" CPDFAnnotation.updateAnnotations(annotations, newCFont: CPDFFont(familyName: fontName, fontStyle: result), withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFont_Info(CPDFFont(familyName: fontName, fontStyle: result)) } else if view == fontSizeSelect { let size = result.stringToCGFloat() CPDFAnnotation.updateAnnotations(annotations, newFontSize: size, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontSize_Info(size) } else if view == opacitySelect { let opacity = max(0, min(1, result.stringToCGFloat()/100)) CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultOpacity_Info(opacity) } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultLineWidth_Info(value) } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 1 { value = 1 } CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultDashPattern_Info(value) } reloadData() } } } //MARK: - ComponentCSelectorGroupDelegate extension KMTextBoxController: ComponentCSelectorGroupDelegate { func componentCSelectorGroupDidChoose(_ view: ComponentCSelectorGroup, _ item: ComponentCSelectorItem) { if view == fontAlignmentGroup { if item.properties == fontAlign_leftItem { CPDFAnnotation.updateAnnotations(annotations, newAlignment: .left, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontAlignment_Info(.left) } else if item.properties == fontAlign_centerItem { CPDFAnnotation.updateAnnotations(annotations, newAlignment: .center, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontAlignment_Info(.center) } else if item.properties == fontAlign_rightItem { CPDFAnnotation.updateAnnotations(annotations, newAlignment: .right, withPDFView: pdfView) CPDFFreeTextAnnotation.update_DefaultFontAlignment_Info(.right) } } else if view == lineTypeSelector { if item.properties == solidProperty { CPDFAnnotation.updateAnnotations(annotations, newBorderStyle: .solid, withPDFView: pdfView) CPDFFreeTextAnnotation.updateBorder_DefaultStyle_Info(.solid) } else if item.properties == dashProperty { CPDFAnnotation.updateAnnotations(annotations, newBorderStyle: .dashed, withPDFView: pdfView) CPDFFreeTextAnnotation.updateBorder_DefaultStyle_Info(.dashed) } } reloadData() } }