KMNWatermarkPropertyController.swift 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. //
  2. // KMNWatermarkPropertyController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by Niehaoyu on 2024/11/1.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. @objc protocol KMNWatermarkPropertyControllerDelegate: AnyObject {
  10. @objc optional func watermarkPropertyControllerDidRemoveWatermark(_ controller: KMNWatermarkPropertyController, _ type: CPDFWatermarkType)
  11. //水印数据有更新
  12. @objc optional func watermarkPropertyControllerDidUpdate(_ controller: KMNWatermarkPropertyController)
  13. //切换到模板界面
  14. @objc optional func watermarkPropertyControllerDidChangetoTemplate(_ controller: KMNWatermarkPropertyController)
  15. //成功保存到模板
  16. @objc optional func watermarkPropertyControllerSaveTemplateSuccess(_ controller: KMNWatermarkPropertyController, _ data: KMPDFWatermarkData)
  17. //取消修改模板信息
  18. @objc optional func watermarkPropertyControllerCancelTemplateEdit(_ controller: KMNWatermarkPropertyController)
  19. }
  20. class KMNWatermarkPropertyController: NSViewController {
  21. @IBOutlet var contendView: NSView!
  22. @IBOutlet var leftTopButton: ComponentButton!
  23. @IBOutlet var titleLabel: NSTextField!
  24. @IBOutlet var templateButton: ComponentButton!
  25. @IBOutlet var typeTabsBGView: NSView!
  26. @IBOutlet var typeTabs: ComponentTabs!
  27. @IBOutlet var infoContendView: NSView!
  28. @IBOutlet var infoScrollView: KMScrollView!
  29. @IBOutlet var infoScrollTopConst: NSLayoutConstraint!
  30. //Text
  31. @IBOutlet var textBGView: NSView!
  32. @IBOutlet var text_textareaView: ComponentTextarea!
  33. @IBOutlet var fontNameSelect: ComponentSelect!
  34. @IBOutlet var fontStyleSelect: ComponentSelect!
  35. @IBOutlet var fontSizeSelect: ComponentSelect!
  36. @IBOutlet var fontColorGroup: ComponentCColorGroup!
  37. //File
  38. @IBOutlet var fileBGView: NSView!
  39. @IBOutlet var fileInputView: ComponentInput!
  40. @IBOutlet var fileInputAddonView: ComponentInputAddon!
  41. //Appearance
  42. @IBOutlet var appearanceBGView: NSView!
  43. @IBOutlet var appearanceLabel: NSTextField!
  44. @IBOutlet var appearance_RotateSelect: ComponentSelect!
  45. @IBOutlet var appearance_OpacitySelect: ComponentSelect!
  46. @IBOutlet var appearanceScaleCheckbox: ComponentCheckBox!
  47. @IBOutlet var appearanceScaleSelect: ComponentSelect!
  48. @IBOutlet var appearanceTopPageRadio: ComponentRadio!
  49. @IBOutlet var appearanceBottomPageRadio: ComponentRadio!
  50. @IBOutlet var appearanceBGTopConst: NSLayoutConstraint!
  51. //Position
  52. @IBOutlet var positionBGView: NSView!
  53. @IBOutlet var positionLabel: NSTextField!
  54. @IBOutlet var positionItemView: ComponentCPosition!
  55. @IBOutlet var positionXInput: ComponentInputNumber!
  56. @IBOutlet var positionYInput: ComponentInputNumber!
  57. @IBOutlet var positionTileCheckbox: ComponentCheckBox!
  58. @IBOutlet var positionTileHorImage: NSImageView!
  59. @IBOutlet var positionTileVertImage: NSImageView!
  60. @IBOutlet var positionTileHoriInput: ComponentInputNumber!
  61. @IBOutlet var positionTileVertInput: ComponentInputNumber!
  62. //Save
  63. @IBOutlet var saveTemplateBGView: NSView!
  64. @IBOutlet var saveButton: ComponentButton!
  65. private var textTabProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: KMLocalizedString("Text"))
  66. private var fileTabProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: KMLocalizedString("File"))
  67. private var familyNames = CPDFFont.familyNames
  68. var watermarkData: KMPDFWatermarkData = KMWatermarkManager.defaultManager.defaultWatermarkData
  69. public var editSubType: KMPDFEditSubModeType = .add
  70. var originalDataDict: Dictionary<String, Any>?
  71. weak open var delegate: KMNWatermarkPropertyControllerDelegate?
  72. //MARK: - func
  73. override func viewDidLoad() {
  74. super.viewDidLoad()
  75. // Do view setup here.
  76. infoScrollView.documentView = infoContendView
  77. infoScrollView.scrollerStyle = .overlay
  78. infoScrollView.layout()
  79. setupProperty()
  80. reloadData()
  81. refreshScrollView()
  82. }
  83. func setupProperty() {
  84. contendView.wantsLayer = true
  85. contendView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor
  86. titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  87. titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold")
  88. leftTopButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "watermark_arrowLeft"), keepPressState: false)
  89. leftTopButton.setTarget(self, action: #selector(leftTopButtonClicked(_:)))
  90. templateButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "icon_wm_template"), keepPressState: false)
  91. templateButton.setTarget(self, action: #selector(templateButtonClicked(_:)))
  92. typeTabs.updateItemProperty([textTabProperty, fileTabProperty])
  93. typeTabs.delegate = self
  94. //Text
  95. text_textareaView.properties = ComponentTextareaProperty(size: .s, placeholderString: KMLocalizedString("Please enter..."), totalCount: -1, text: "")
  96. text_textareaView.delegate = self
  97. fontNameSelect.properties = ComponentSelectProperties(size: .s,
  98. state: .normal,
  99. text: "")
  100. var menuItemArr: [ComponentMenuitemProperty] = []
  101. for string in familyNames {
  102. let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string)
  103. menuItemArr.append(item)
  104. }
  105. fontNameSelect.updateMenuItemsArr(menuItemArr)
  106. fontNameSelect.selectItemAtIndex(0)
  107. fontNameSelect.delegate = self
  108. fontStyleSelect.properties = ComponentSelectProperties(size: .s,
  109. state: .normal,
  110. text: "")
  111. fontStyleSelect.delegate = self
  112. fontSizeSelect.properties = ComponentSelectProperties(size: .s,
  113. state: .normal,
  114. creatable: true,
  115. text: "12 pt",
  116. textUnit: " pt",
  117. regexString: "0123456789 pt")
  118. var sizeItemArr: [ComponentMenuitemProperty] = []
  119. for string in ["6","8","10","12","14",
  120. "16","18","20","22","24",
  121. "26","28","30","32","34",
  122. "36","40","48","64","80",
  123. "96","112"] {
  124. let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string)
  125. sizeItemArr.append(item)
  126. }
  127. fontSizeSelect.updateMenuItemsArr(sizeItemArr)
  128. fontSizeSelect.selectItemAtIndex(0)
  129. fontSizeSelect.delegate = self
  130. let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[0])
  131. let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[1])
  132. let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[2])
  133. let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[3])
  134. let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: KMAnnotationPropertiesColorManager.manager.watermark_Colors[4])
  135. fontColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty)
  136. fontColorGroup.delegate = self
  137. //File
  138. fileInputView.properties = ComponentInputProperty(size: .s,
  139. state:.pressed ,
  140. placeholder: "Select Source File...",
  141. text: "",
  142. creatable: false)
  143. fileInputView.properties.propertyInfo.cornerRadius_topLeft = 0
  144. fileInputView.properties.propertyInfo.cornerRadius_topRight = 0
  145. fileInputView.properties.propertyInfo.cornerRadius_bottomLeft = 0
  146. fileInputView.properties.propertyInfo.cornerRadius_bottomRight = 0
  147. fileInputView.reloadData()
  148. fileInputView.delegate = self
  149. fileInputAddonView.properties = ComponentInputAddonProperty(size: .s,
  150. state: .normal,
  151. addOnBefore: false,
  152. onlyRead: false,
  153. addonType: .imageWithColor,
  154. iconImage: NSImage(named: "icon_folder"))
  155. fileInputAddonView.setTarget(self, action: #selector(chooseURLAction(_ :)))
  156. //Appearance
  157. appearanceLabel.stringValue = KMLocalizedString("Appearance")
  158. appearanceLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  159. appearanceLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  160. appearance_RotateSelect.properties = ComponentSelectProperties(size: .s,
  161. state: .normal,
  162. creatable: true,
  163. text: "0°",
  164. textUnit: "°",
  165. regexString: "0123456789°-")
  166. var rotateItems: [ComponentMenuitemProperty] = []
  167. for string in ["0°", "45°", "-45°", "90°", "-90°"] {
  168. let item = ComponentMenuitemProperty(type: .normal, text: string)
  169. rotateItems.append(item)
  170. }
  171. appearance_RotateSelect.updateMenuItemsArr(rotateItems)
  172. appearance_RotateSelect.delegate = self
  173. appearance_OpacitySelect.properties = ComponentSelectProperties(size: .s,
  174. state: .normal,
  175. creatable: true,
  176. text: "100%",
  177. textUnit: "%",
  178. regexString: "0123456789%")
  179. var opacityItems: [ComponentMenuitemProperty] = []
  180. for string in ["25%", "50%", "75%", "100%"] {
  181. let item = ComponentMenuitemProperty(type: .normal, text: string)
  182. opacityItems.append(item)
  183. }
  184. appearance_OpacitySelect.updateMenuItemsArr(opacityItems)
  185. appearance_OpacitySelect.delegate = self
  186. appearanceScaleCheckbox.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("Scale relative to target page"), checkboxType: .normal)
  187. appearanceScaleCheckbox.setTarget(self, action: #selector(checkBoxClicked(_:)))
  188. appearanceScaleSelect.properties = ComponentSelectProperties(size: .s,
  189. state: .normal,
  190. creatable: true,
  191. text: "100%",
  192. textUnit: "%",
  193. regexString: "0123456789%")
  194. var appearanceScaleItems: [ComponentMenuitemProperty] = []
  195. for string in ["25%", "50%", "75%", "100%", "125%", "150%","200%"] {
  196. let item = ComponentMenuitemProperty(type: .normal, text: string)
  197. appearanceScaleItems.append(item)
  198. }
  199. appearanceScaleSelect.updateMenuItemsArr(appearanceScaleItems)
  200. appearanceScaleSelect.delegate = self
  201. appearanceTopPageRadio.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("At the top of the page"), checkboxType: .normal)
  202. appearanceTopPageRadio.setTarget(self, action: #selector(radioClicked(_:)))
  203. appearanceBottomPageRadio.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("At the bottom of the page"), checkboxType: .normal)
  204. appearanceBottomPageRadio.setTarget(self, action: #selector(radioClicked(_:)))
  205. //Position
  206. positionLabel.stringValue = KMLocalizedString("Position (mm)")
  207. positionLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  208. positionLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  209. positionItemView.properties = ComponentCPositionProperty(rowCount: 3, columnCount: 3, dash: false, selRow: 1, selColumn: 1)
  210. positionItemView.delegate = self
  211. positionXInput.properties = ComponentInputNumberProperty(alignment: .left,
  212. size: .s,
  213. minSize: -1000,
  214. maxSize: 1000,
  215. text: "0")
  216. positionXInput.delegate = self
  217. positionYInput.properties = ComponentInputNumberProperty(alignment: .left,
  218. size: .s,
  219. minSize: -1000,
  220. maxSize: 1000,
  221. text: "0")
  222. positionYInput.delegate = self
  223. positionTileCheckbox.properties = ComponentCheckBoxProperty(size: .s, text: KMLocalizedString("Tile"), checkboxType: .normal)
  224. positionTileCheckbox.setTarget(self, action: #selector(checkBoxClicked(_:)))
  225. positionTileHoriInput.properties = ComponentInputNumberProperty(alignment: .left,
  226. size: .s,
  227. minSize: 0,
  228. maxSize: 1000,
  229. text: "0")
  230. positionTileHoriInput.delegate = self
  231. positionTileVertInput.properties = ComponentInputNumberProperty(alignment: .left,
  232. size: .s,
  233. minSize: 0,
  234. maxSize: 1000,
  235. text: "0")
  236. positionTileVertInput.delegate = self
  237. //Save
  238. saveTemplateBGView.wantsLayer = true
  239. saveTemplateBGView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor
  240. saveButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .m, showLeftIcon: true, buttonText: KMLocalizedString("Save as Template"), icon: NSImage(named: "icon_wm_template_save"), keepPressState: false)
  241. saveButton.setTarget(self, action: #selector(saveButtonClicked(_:)))
  242. }
  243. func refreshScrollView() {
  244. infoScrollView.contentViewSize = CGSizeMake(264, 672)
  245. if watermarkData.watermarkType == .text {
  246. infoScrollView.contentViewSize = CGSizeMake(264, 672)
  247. } else if watermarkData.watermarkType == .image {
  248. infoScrollView.contentViewSize = CGSizeMake(264, 512)
  249. }
  250. infoScrollView.scrollToTop()
  251. }
  252. func reloadData() {
  253. titleLabel.stringValue = KMLocalizedString("Add Watermark")
  254. leftTopButton.isHidden = true
  255. saveTemplateBGView.isHidden = false
  256. templateButton.properties.icon = NSImage(named: "icon_wm_template")
  257. typeTabsBGView.isHidden = false
  258. infoScrollTopConst.constant = 56
  259. if editSubType == .edit {
  260. typeTabsBGView.isHidden = true
  261. infoScrollTopConst.constant = 8
  262. titleLabel.stringValue = KMLocalizedString("Edit Template")
  263. leftTopButton.isHidden = false
  264. saveTemplateBGView.isHidden = true
  265. templateButton.properties.icon = NSImage(named: "edit_save")
  266. }
  267. templateButton.reloadData()
  268. fileBGView.isHidden = true
  269. textBGView.isHidden = true
  270. textTabProperty.state = .normal
  271. fileTabProperty.state = .normal
  272. if watermarkData.watermarkType == .text {
  273. textBGView.isHidden = false
  274. appearanceBGTopConst.constant = 200
  275. textTabProperty.state = .pressed
  276. } else if watermarkData.watermarkType == .image {
  277. fileBGView.isHidden = false
  278. appearanceBGTopConst.constant = 48
  279. fileTabProperty.state = .pressed
  280. }
  281. typeTabs.refreshItems()
  282. //Text
  283. text_textareaView.properties.text = watermarkData.text ?? ""
  284. text_textareaView.reloadData()
  285. if let index = familyNames.firstIndex(of: watermarkData.fontName) {
  286. fontNameSelect.selectItemAtIndex(index)
  287. }
  288. fontNameSelect.properties.text = watermarkData.fontName
  289. fontNameSelect.reloadData()
  290. let styleNames = CPDFFont.fontNames(forFamilyName: watermarkData.fontName)
  291. var menuItemArr: [ComponentMenuitemProperty] = []
  292. for string in styleNames {
  293. let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string)
  294. menuItemArr.append(item)
  295. }
  296. fontStyleSelect.updateMenuItemsArr(menuItemArr)
  297. if watermarkData.fontStyle == nil {
  298. fontStyleSelect.selectItemAtIndex(0)
  299. watermarkData.fontStyle = styleNames.first
  300. } else {
  301. fontStyleSelect.properties.text = watermarkData.fontStyle
  302. fontStyleSelect.reloadData()
  303. }
  304. positionTileCheckbox.properties.isDisabled = false
  305. let fontSizeStr = String(format: "%.0f", watermarkData.fontSize)
  306. if let index = KMWatermarkManager.getFontSize().firstIndex(of: fontSizeStr) {
  307. fontSizeSelect.selectItemAtIndex(index)
  308. }
  309. fontSizeSelect.properties.text = String(format: "%.0f", watermarkData.fontSize) + " pt"
  310. fontSizeSelect.properties.isDisabled = false
  311. if watermarkData.isScale {
  312. fontSizeSelect.properties.isDisabled = true
  313. positionTileCheckbox.properties.isDisabled = true
  314. watermarkData.isTilePage = false
  315. }
  316. fontSizeSelect.reloadData()
  317. fontColorGroup.currentColor = watermarkData.textColor
  318. fontColorGroup.refreshUI()
  319. //File
  320. fileInputView.properties.text = watermarkData.imagePath ?? ""
  321. fileInputView.reloadData()
  322. //Appearance
  323. appearance_RotateSelect.properties.text = String(format: "%.0f", watermarkData.rotation) + "°"
  324. appearance_RotateSelect.reloadData()
  325. if watermarkData.opacity > 1 {
  326. watermarkData.opacity = 1
  327. }
  328. appearance_OpacitySelect.properties.text = String(format: "%.0f", watermarkData.opacity*100) + "%"
  329. appearance_OpacitySelect.reloadData()
  330. appearanceScaleCheckbox.properties.checkboxType = watermarkData.isScale ? .selected : .normal
  331. appearanceScaleCheckbox.reloadData()
  332. appearanceScaleSelect.properties.isDisabled = watermarkData.isScale ? false : true
  333. appearanceScaleSelect.properties.text = String(format: "%.0f", watermarkData.scale*100) + "%"
  334. appearanceScaleSelect.reloadData()
  335. appearanceTopPageRadio.properties.checkboxType = watermarkData.isFront ? .selected : .normal
  336. appearanceTopPageRadio.reloadData()
  337. appearanceBottomPageRadio.properties.checkboxType = watermarkData.isFront ? .normal : .selected
  338. appearanceBottomPageRadio.reloadData()
  339. //Position
  340. positionXInput.properties.text = String(format: "%.0f", watermarkData.tx)
  341. positionXInput.reloadData()
  342. positionYInput.properties.text = String(format: "%.0f", watermarkData.ty)
  343. positionYInput.reloadData()
  344. positionTileHoriInput.properties.text = String(format: "%.0f", watermarkData.horizontalSpacing)
  345. positionTileVertInput.properties.text = String(format: "%.0f", watermarkData.verticalSpacing)
  346. if watermarkData.isTilePage {
  347. positionTileHorImage.image = NSImage(named: "tile_spacing_horiz")
  348. positionTileVertImage.image = NSImage(named: "tile_spacing_vert")
  349. positionTileHoriInput.properties.isDisabled = false
  350. positionTileVertInput.properties.isDisabled = false
  351. } else {
  352. positionTileHorImage.image = NSImage(named: "tile_spacing_horiz_dis")
  353. positionTileVertImage.image = NSImage(named: "tile_spacing_vert_dis")
  354. positionTileHoriInput.properties.isDisabled = true
  355. positionTileVertInput.properties.isDisabled = true
  356. }
  357. positionTileHoriInput.reloadData()
  358. positionTileVertInput.reloadData()
  359. positionTileCheckbox.properties.checkboxType = watermarkData.isTilePage ? .selected : .normal
  360. positionTileCheckbox.reloadData()
  361. saveButton.properties.isDisabled = false
  362. if watermarkData.watermarkType == .text {
  363. if watermarkData.text?.count == 0 {
  364. saveButton.properties.isDisabled = true
  365. }
  366. } else if watermarkData.watermarkType == .image {
  367. if watermarkData.imagePath == nil {
  368. saveButton.properties.isDisabled = true
  369. }
  370. }
  371. saveButton.reloadData()
  372. }
  373. func clearData() {
  374. watermarkData = KMPDFWatermarkData()
  375. reloadData()
  376. }
  377. //MARK: - Action
  378. @objc func leftTopButtonClicked(_ sender: ComponentButton) {
  379. if sender == leftTopButton {
  380. var isChanged = false
  381. if let dict = self.originalDataDict {
  382. isChanged = KMWatermarkManager.compareIsChangedModel(watermarkData, withDict: dict as NSDictionary)
  383. }
  384. if isChanged == true {
  385. let alert = NSAlert()
  386. alert.alertStyle = .critical
  387. alert.messageText = KMLocalizedString("Save template changes?")
  388. alert.informativeText = KMLocalizedString("Cancel and they will not be saved.")
  389. alert.addButton(withTitle: KMLocalizedString("Save"))
  390. alert.addButton(withTitle: KMLocalizedString("Cancel"))
  391. alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in
  392. if response == NSApplication.ModalResponse.alertFirstButtonReturn {
  393. self.templateButtonClicked(self.templateButton)
  394. } else {
  395. if let dict = self.originalDataDict {
  396. KMWatermarkManager.defaultManager.setDictToWatermarK(dict: dict as NSDictionary, self.watermarkData)
  397. let _ = KMWatermarkManager.defaultManager.updateWatermark(watermark: self.watermarkData)
  398. }
  399. self.delegate?.watermarkPropertyControllerCancelTemplateEdit?(self)
  400. }
  401. }
  402. } else {
  403. delegate?.watermarkPropertyControllerCancelTemplateEdit?(self)
  404. }
  405. }
  406. }
  407. @objc func templateButtonClicked(_ sender: ComponentButton) {
  408. if editSubType == .edit {
  409. let _ = KMWatermarkManager.defaultManager.updateWatermark(watermark: watermarkData)
  410. delegate?.watermarkPropertyControllerSaveTemplateSuccess?(self, self.watermarkData)
  411. } else {
  412. if watermarkData.watermarkType == .image, let _ = watermarkData.imagePath {
  413. watermarkData.text = nil
  414. watermarkData.watermarkType = .image
  415. } else {
  416. watermarkData.watermarkType = .text
  417. watermarkData.imagePath = nil
  418. }
  419. delegate?.watermarkPropertyControllerDidChangetoTemplate?(self)
  420. }
  421. }
  422. @objc func checkBoxClicked(_ sender: ComponentCheckBox) {
  423. if sender == appearanceScaleCheckbox {
  424. watermarkData.isScale = appearanceScaleCheckbox.properties.checkboxType == .selected ? true : false
  425. } else if sender == positionTileCheckbox {
  426. watermarkData.isTilePage = positionTileCheckbox.properties.checkboxType == .selected ? true : false
  427. }
  428. reloadData()
  429. delegate?.watermarkPropertyControllerDidUpdate?(self)
  430. }
  431. @objc func radioClicked(_ sender: ComponentRadio) {
  432. if sender == appearanceTopPageRadio {
  433. watermarkData.isFront = true
  434. } else if sender == appearanceBottomPageRadio {
  435. watermarkData.isFront = false
  436. }
  437. reloadData()
  438. delegate?.watermarkPropertyControllerDidUpdate?(self)
  439. }
  440. @objc func saveButtonClicked(_ sender: ComponentButton) {
  441. let saveWindow: KMWatermarkSaveWindow = KMWatermarkSaveWindow(windowNibName: "KMWatermarkSaveWindow")
  442. if watermarkData.watermarkType == .image {
  443. saveWindow.nameValue = watermarkData.imagePath?.lastPathComponent ?? ""
  444. } else if watermarkData.watermarkType == .text {
  445. saveWindow.nameValue = watermarkData.text ?? ""
  446. }
  447. saveWindow.saveHandler = {[weak self] string in
  448. guard let weakSelf = self else { return }
  449. DispatchQueue.main.async {
  450. weakSelf.watermarkData.watermarkName = string ?? ""
  451. if KMWatermarkManager.defaultManager.addWatermark(watermark: weakSelf.watermarkData) == true {
  452. weakSelf.delegate?.watermarkPropertyControllerSaveTemplateSuccess?(weakSelf, weakSelf.watermarkData)
  453. }
  454. }
  455. }
  456. saveWindow.own_beginSheetModal(for: view.window) { string in
  457. }
  458. }
  459. @objc func chooseURLAction(_ sender: NSView) {
  460. self.chooseURLAction(sender, nil)
  461. }
  462. func chooseURLAction(_ sender: NSView, _ filePath: String? = nil) {
  463. let openPanel = NSOpenPanel()
  464. openPanel.canChooseDirectories = false
  465. openPanel.canChooseFiles = true
  466. openPanel.allowsMultipleSelection = false
  467. openPanel.allowedFileTypes = ["jpg", "jpeg", "png", "pdf"]
  468. openPanel.beginSheetModal(for: self.view.window!) { (result) in
  469. if result == NSApplication.ModalResponse.OK {
  470. guard let url = openPanel.url else { return }
  471. let filePath = url.path
  472. let outFolder = KMWatermarkManager.defaultManager.watermarkImageFolder
  473. let fileName = filePath.getLastComponentDeleteExtension
  474. if filePath.extension.lowercased() == ".pdf" {
  475. let pdf = CPDFDocument(url: url)
  476. guard !pdf!.isEncrypted else { return }
  477. if let image = self.generateThumbnail(for: url) {
  478. if let outFolderPath = outFolder?.stringByAppendingPathComponent(fileName + ".png") {
  479. try?image.pngData()?.write(to: URL(fileURLWithPath: outFolderPath))
  480. if FileManager.default.fileExists(atPath: outFolderPath) {
  481. self.watermarkData.imagePath = outFolderPath
  482. }
  483. self.delegate?.watermarkPropertyControllerDidUpdate?(self)
  484. self.reloadData()
  485. }
  486. }
  487. } else {
  488. let image = NSImage(contentsOfFile: url.path)
  489. if image == nil || NSImage.isDamageImage(image, imagePath: url.path) {
  490. let alert = NSAlert()
  491. alert.alertStyle = .critical
  492. alert.messageText = "The file \"\(url.lastPathComponent)\" could not be opened."
  493. alert.informativeText = "It may be damaged or use a file format that PDF Reader Pro doesn’t recognize."
  494. alert.addButton(withTitle: "Cancel")
  495. alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in
  496. if response == NSApplication.ModalResponse.alertFirstButtonReturn {
  497. // Cancel button clicked
  498. }
  499. }
  500. return
  501. }
  502. if let resultPath = outFolder?.stringByAppendingPathComponent(fileName + ".png") {
  503. try?FileManager.default.copyItem(atPath: url.path, toPath: resultPath)
  504. self.watermarkData.imagePath = resultPath
  505. }
  506. self.delegate?.watermarkPropertyControllerDidUpdate?(self)
  507. self.reloadData()
  508. }
  509. }
  510. }
  511. }
  512. func generateThumbnail(for pdfURL: URL) -> NSImage? {
  513. // 创建PDF文档
  514. guard let pdfDocument = CGPDFDocument(pdfURL as CFURL),
  515. let pdfPage = pdfDocument.page(at: 1) else {
  516. print("打开PDF文件失败")
  517. return nil
  518. }
  519. // 获取页面尺寸
  520. let pageRect = pdfPage.getBoxRect(.mediaBox)
  521. // 创建图形上下文
  522. let size = NSSize(width: 200, height: 300)
  523. let bitsPerComponent = 8
  524. let bytesPerPixel = 4
  525. let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
  526. let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: bitsPerComponent, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo)
  527. // 绘制PDF页面
  528. context?.saveGState()
  529. context?.translateBy(x: 0, y: size.height)
  530. context?.scaleBy(x: 1.0, y: -1.0) // 反转坐标系
  531. context?.scaleBy(x: size.width / pageRect.width, y: size.height / pageRect.height) // 缩放
  532. context?.drawPDFPage(pdfPage)
  533. context?.restoreGState()
  534. // 创建NSImage
  535. if let cgImage = context?.makeImage() {
  536. return NSImage(cgImage: cgImage, size: NSSize(width: size.width, height: size.height))
  537. } else {
  538. return nil
  539. }
  540. }
  541. //MARK: - Mouse
  542. override func mouseDown(with event: NSEvent) {
  543. super.mouseDown(with: event)
  544. view.window?.makeFirstResponder(nil)
  545. }
  546. }
  547. //MARK: - ComponentTabsDelegate
  548. extension KMNWatermarkPropertyController: ComponentTabsDelegate {
  549. func componentTabsDidSelected(_ view: ComponentTabs, _ property: ComponentTabsProperty) {
  550. if property == textTabProperty {
  551. if watermarkData.watermarkType != .text {
  552. delegate?.watermarkPropertyControllerDidRemoveWatermark?(self, .text)
  553. }
  554. watermarkData.watermarkType = .text
  555. } else if property == fileTabProperty {
  556. if watermarkData.watermarkType != .image {
  557. delegate?.watermarkPropertyControllerDidRemoveWatermark?(self, .image)
  558. }
  559. watermarkData.watermarkType = .image
  560. }
  561. reloadData()
  562. delegate?.watermarkPropertyControllerDidUpdate?(self)
  563. refreshScrollView()
  564. }
  565. }
  566. //MARK: - ComponentTextareaDelegate
  567. extension KMNWatermarkPropertyController: ComponentTextareaDelegate {
  568. func componentTextareaTextDidChange(_ view: ComponentTextarea) {
  569. watermarkData.text = view.properties.text
  570. delegate?.watermarkPropertyControllerDidUpdate?(self)
  571. }
  572. func componentTextareaTextDidEndEditing(_ view: ComponentTextarea) {
  573. watermarkData.text = view.properties.text
  574. delegate?.watermarkPropertyControllerDidUpdate?(self)
  575. }
  576. }
  577. //MARK: - ComponentCColorDelegate
  578. extension KMNWatermarkPropertyController: ComponentCColorDelegate {
  579. func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) {
  580. if view == fontColorGroup {
  581. watermarkData.textColor = color ?? NSColor.clear
  582. }
  583. reloadData()
  584. delegate?.watermarkPropertyControllerDidUpdate?(self)
  585. }
  586. }
  587. //MARK: - ComponentSelectDelegate
  588. extension KMNWatermarkPropertyController: ComponentSelectDelegate {
  589. func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) {
  590. if view == fontNameSelect {
  591. watermarkData.fontName = menuItemProperty?.text ?? "Helvetica"
  592. watermarkData.fontStyle = nil
  593. } else if view == fontStyleSelect {
  594. watermarkData.fontStyle = menuItemProperty?.text
  595. } else if view == fontSizeSelect {
  596. if let text = menuItemProperty?.text {
  597. let result = text.stringByDeleteCharString(" pt")
  598. watermarkData.fontSize = result.stringToCGFloat()
  599. }
  600. } else if view == appearance_OpacitySelect {
  601. if let text = menuItemProperty?.text {
  602. let result = text.stringByDeleteCharString("%")
  603. watermarkData.opacity = result.stringToCGFloat()/100
  604. }
  605. } else if view == appearance_RotateSelect {
  606. if let text = menuItemProperty?.text {
  607. let result = text.stringByDeleteCharString("°")
  608. watermarkData.rotation = result.stringToCGFloat()
  609. }
  610. } else if view == appearanceScaleSelect {
  611. if let text = menuItemProperty?.text {
  612. let result = text.stringByDeleteCharString("%")
  613. watermarkData.scale = result.stringToCGFloat()/100
  614. }
  615. }
  616. reloadData()
  617. delegate?.watermarkPropertyControllerDidUpdate?(self)
  618. }
  619. func componentSelectTextDidChange(_ view: ComponentSelect) {
  620. if view == fontSizeSelect {
  621. if let string = view.properties.text {
  622. let size = string.stringToCGFloat()
  623. watermarkData.fontSize = size
  624. delegate?.watermarkPropertyControllerDidUpdate?(self)
  625. }
  626. }
  627. }
  628. func componentSelectTextDidEndEditing(_ view: ComponentSelect) {
  629. if view == fontSizeSelect {
  630. if let string = view.properties.text {
  631. let result = string.stringByDeleteCharString(" pt")
  632. let size = result.stringToCGFloat()
  633. watermarkData.fontSize = size
  634. }
  635. } else if view == appearance_OpacitySelect {
  636. if let text = appearance_OpacitySelect.properties.text {
  637. let result = text.stringByDeleteCharString("%")
  638. watermarkData.opacity = result.stringToCGFloat()/100
  639. }
  640. } else if view == appearance_RotateSelect {
  641. if let text = appearance_RotateSelect.properties.text {
  642. let result = text.stringByDeleteCharString("°")
  643. watermarkData.rotation = result.stringToCGFloat()
  644. }
  645. } else if view == appearanceScaleSelect {
  646. if let text = appearanceScaleSelect.properties.text {
  647. let result = text.stringByDeleteCharString("%")
  648. watermarkData.scale = result.stringToCGFloat()/100
  649. }
  650. }
  651. reloadData()
  652. delegate?.watermarkPropertyControllerDidUpdate?(self)
  653. }
  654. }
  655. //MARK: - ComponentCPositionDelegate
  656. extension KMNWatermarkPropertyController: ComponentCPositionDelegate {
  657. func componentCPositionDidChoose(_ view: NSView, _ row: Int, _ column: Int) {
  658. if view == positionItemView {
  659. if row == 0 {
  660. watermarkData.verticalPosition = .bottom
  661. } else if row == 1 {
  662. watermarkData.verticalPosition = .center
  663. } else if row == 2 {
  664. watermarkData.verticalPosition = .top
  665. }
  666. if column == 0 {
  667. watermarkData.horizontalPosition = .left
  668. } else if column == 1 {
  669. watermarkData.horizontalPosition = .center
  670. } else if column == 2 {
  671. watermarkData.horizontalPosition = .right
  672. }
  673. }
  674. reloadData()
  675. delegate?.watermarkPropertyControllerDidUpdate?(self)
  676. }
  677. }
  678. //MARK: - ComponentInputNumberDelegate
  679. extension KMNWatermarkPropertyController: ComponentInputNumberDelegate {
  680. func componentInputNumberDidValueChanged(inputNumber: ComponentInputNumber?) {
  681. if inputNumber == positionXInput {
  682. if let text = inputNumber?.properties.text {
  683. watermarkData.tx = text.stringToCGFloat()
  684. }
  685. } else if inputNumber == positionYInput {
  686. if let text = inputNumber?.properties.text {
  687. watermarkData.ty = text.stringToCGFloat()
  688. }
  689. } else if inputNumber == positionTileHoriInput {
  690. if let text = inputNumber?.properties.text {
  691. watermarkData.horizontalSpacing = text.stringToCGFloat()
  692. }
  693. } else if inputNumber == positionTileVertInput {
  694. if let text = inputNumber?.properties.text {
  695. watermarkData.verticalSpacing = text.stringToCGFloat()
  696. }
  697. }
  698. reloadData()
  699. delegate?.watermarkPropertyControllerDidUpdate?(self)
  700. }
  701. }
  702. //MARK: - ComponentInputDelegate
  703. extension KMNWatermarkPropertyController: ComponentInputDelegate {
  704. func componentInputDidCoverButtonClicked(inputView: ComponentInput) {
  705. if inputView.properties.text.count > 0 && inputView.properties.creatable == false {
  706. self.chooseURLAction(inputView, inputView.properties.text)
  707. }
  708. }
  709. }