KMNWatermarkPropertyController.swift 45 KB

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