KMEditImageController.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. //
  2. // KMEditImageController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by Niehaoyu on 2024/11/14.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. class KMEditImageController: NSViewController {
  10. @IBOutlet var scrollView: NSScrollView!
  11. @IBOutlet var contendView: NSView!
  12. @IBOutlet var sizeBGView: NSView!
  13. @IBOutlet var sizeLabel: NSTextField!
  14. @IBOutlet var sizeSyncButton: ComponentButton!
  15. @IBOutlet var sizeWidthInput: ComponentInputNumber!
  16. @IBOutlet var sizeHeightInput: ComponentInputNumber!
  17. @IBOutlet var rotateBGView: NSView!
  18. @IBOutlet var rotateLabel: NSTextField!
  19. @IBOutlet var rotateSelect: ComponentSelect!
  20. @IBOutlet var rotateLeftButton: ComponentButton!
  21. @IBOutlet var rotateRightButton: ComponentButton!
  22. @IBOutlet var flipVerticalButton: ComponentButton!
  23. @IBOutlet var flipHorizontalButton: ComponentButton!
  24. @IBOutlet var opacityBGView: NSView!
  25. @IBOutlet var opacityLabel: NSTextField!
  26. @IBOutlet var opacitySlider: ComponentSlider!
  27. @IBOutlet var opacitySelect: ComponentSelect!
  28. @IBOutlet var cropButton: ComponentButton!
  29. @IBOutlet var replaceButton: ComponentButton!
  30. @IBOutlet var extractButton: ComponentButton!
  31. @IBOutlet var extractBtnTopConst: NSLayoutConstraint!
  32. @IBOutlet var alignmentBGView: NSView!
  33. private var syncChangeBounds: Bool = true //同步修改宽高
  34. private var groupView: ComponentGroup!
  35. private var alignmentController: KMNAlignmentController?
  36. private var areas: [CPDFEditImageArea] = []
  37. var pdfView: CPDFListView? {
  38. didSet {
  39. reloadData()
  40. }
  41. }
  42. //MARK: - func
  43. override func viewDidAppear() {
  44. super.viewDidAppear()
  45. opacitySlider.reloadData()
  46. }
  47. override func viewDidLoad() {
  48. super.viewDidLoad()
  49. setupProperty()
  50. }
  51. func setupProperty() {
  52. //Size
  53. sizeLabel.stringValue = KMLocalizedString("Size")
  54. sizeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  55. sizeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  56. sizeSyncButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "sync_Change_unlock"), keepPressState: true)
  57. sizeSyncButton.properties.propertyInfo.leftIcon_press = NSImage(named: "sync_Change_lock")
  58. sizeSyncButton.setTarget(self, action: #selector(sizeSyncButtonClicked(_:)))
  59. sizeSyncButton.reloadData()
  60. sizeWidthInput.properties = ComponentInputNumberProperty(alignment: .center,
  61. size: .s,
  62. state: .normal,
  63. showPrefix: true,
  64. leftIcon: NSImage(named: "w_icon"),
  65. showSuffix: false,
  66. minSize: 1,
  67. maxSize: 1000,
  68. text:"100",
  69. valueType: .floatType)
  70. sizeWidthInput.delegate = self
  71. sizeHeightInput.properties = ComponentInputNumberProperty(alignment: .center,
  72. size: .s,
  73. state: .normal,
  74. showPrefix: true,
  75. leftIcon: NSImage(named: "h_icon"),
  76. showSuffix: false,
  77. minSize: 1,
  78. maxSize: 1000,
  79. text:"100",
  80. valueType: .floatType)
  81. sizeHeightInput.delegate = self
  82. //Rotate
  83. rotateLabel.stringValue = KMLocalizedString("Rotate & Flip")
  84. rotateLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  85. rotateLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  86. rotateSelect.properties = ComponentSelectProperties(size: .s,
  87. state: .normal,
  88. creatable: true,
  89. text: "100",
  90. textUnit: "°",
  91. regexString: "0123456789-")
  92. if true {
  93. var opacityItems: [ComponentMenuitemProperty] = []
  94. for string in ["0°", "45°", "-45°", "90°", "-90°"] {
  95. let item = ComponentMenuitemProperty(type: .normal, text: string)
  96. opacityItems.append(item)
  97. }
  98. rotateSelect.updateMenuItemsArr(opacityItems)
  99. }
  100. rotateSelect.delegate = self
  101. rotateLeftButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, onlyIcon: true, icon: NSImage(named: "pageEdit_rotateRight"), keepPressState: false)
  102. rotateLeftButton.setTarget(self, action: #selector(buttonClicked(_:)))
  103. rotateRightButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, onlyIcon: true, icon: NSImage(named: "pageEdit_rotateLeft"), keepPressState: false)
  104. rotateRightButton.setTarget(self, action: #selector(buttonClicked(_:)))
  105. flipVerticalButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, onlyIcon: true, icon: NSImage(named: "flipVertical"), keepPressState: false)
  106. flipVerticalButton.setTarget(self, action: #selector(buttonClicked(_:)))
  107. flipHorizontalButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, onlyIcon: true, icon: NSImage(named: "flipHorizontal"), keepPressState: false)
  108. flipHorizontalButton.setTarget(self, action: #selector(buttonClicked(_:)))
  109. //Opacity
  110. opacityLabel.stringValue = KMLocalizedString("Opacity")
  111. opacityLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  112. opacityLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  113. opacitySlider.properties = ComponentSliderProperty(size: .m, percent: 1)
  114. opacitySlider.delegate = self
  115. opacitySelect.properties = ComponentSelectProperties(size: .s,
  116. state: .normal,
  117. creatable: true,
  118. text: "100",
  119. textUnit: "%",
  120. regexString: "0123456789%")
  121. if true {
  122. var opacityItems: [ComponentMenuitemProperty] = []
  123. for string in ["25%", "50%", "75%", "100%"] {
  124. let item = ComponentMenuitemProperty(type: .normal, text: string)
  125. opacityItems.append(item)
  126. }
  127. opacitySelect.updateMenuItemsArr(opacityItems)
  128. }
  129. opacitySelect.delegate = self
  130. cropButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .s, buttonText: KMLocalizedString("Crop"), keepPressState: false)
  131. cropButton.setTarget(self, action: #selector(buttonClicked(_:)))
  132. replaceButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .s, buttonText: KMLocalizedString("Replace"), keepPressState: false)
  133. replaceButton.setTarget(self, action: #selector(buttonClicked(_:)))
  134. extractButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .s, buttonText: KMLocalizedString("Extract"), keepPressState: true)
  135. extractButton.setTarget(self, action: #selector(buttonClicked(_:)))
  136. if alignmentController == nil {
  137. alignmentController = KMNAlignmentController.init()
  138. }
  139. alignmentController?.view.frame = alignmentBGView.bounds
  140. alignmentController?.view.autoresizingMask = [.width, .height]
  141. alignmentController?.delegate = self
  142. alignmentBGView.addSubview(alignmentController!.view)
  143. }
  144. func reloadData() {
  145. areas = pdfView?.km_editingImageAreas() ?? []
  146. if areas.count == 0 {
  147. } else if areas.count == 1 {
  148. cropButton.isHidden = false
  149. replaceButton.isHidden = false
  150. extractBtnTopConst.constant = 96
  151. alignmentBGView.isHidden = true
  152. } else if areas.count > 1 {
  153. cropButton.isHidden = true
  154. replaceButton.isHidden = true
  155. extractBtnTopConst.constant = 16
  156. alignmentBGView.isHidden = false
  157. if areas.count == 2 {
  158. alignmentController?.updateMulti(false)
  159. } else {
  160. alignmentController?.updateMulti(true)
  161. }
  162. }
  163. var first_area: CPDFEditImageArea?
  164. if areas.count > 0 {
  165. first_area = areas.first
  166. }
  167. //refreshData
  168. var boundsWidth: CGFloat?
  169. var boundsHeight: CGFloat?
  170. var rotateAngle: CGFloat?
  171. var opacity_value: CGFloat?
  172. if areas.count == 0 {
  173. } else if areas.count == 1, let area = first_area {
  174. boundsWidth = area.bounds.size.width
  175. boundsHeight = area.bounds.size.height
  176. if let rotates = pdfView?.km_editAreasRotates([area]) {
  177. rotateAngle = rotates.first ?? 1
  178. }
  179. if let opacitys = pdfView?.km_editAreasOpacitys([area]) {
  180. var opacity = opacitys.first ?? 1
  181. opacity = min(1, opacity)
  182. opacity = max(0, opacity)
  183. opacity_value = opacity
  184. }
  185. } else if areas.count > 1, let area = first_area {
  186. if CPDFEditArea.isMultiBoundsWidth(areas) == true {
  187. boundsWidth = nil
  188. } else {
  189. boundsWidth = area.bounds.size.width
  190. }
  191. if CPDFEditArea.isMultiBoundsHeight(areas) == true {
  192. boundsHeight = nil
  193. } else {
  194. boundsHeight = area.bounds.size.height
  195. }
  196. if CPDFEditArea.isMultiAngle(areas, inPDFView: pdfView) == true {
  197. rotateAngle = nil
  198. } else {
  199. rotateAngle = pdfView?.getRotateWith(area)
  200. }
  201. if CPDFEditArea.isMultiOpacity(areas, inPDFView: pdfView) == true {
  202. opacity_value = nil
  203. } else {
  204. var firstValue = pdfView?.opacityByRange(for: area) ?? 0
  205. if firstValue < 0 {
  206. firstValue = pdfView?.opacity(for: area) ?? 0
  207. }
  208. opacity_value = firstValue
  209. }
  210. }
  211. if let value = boundsWidth {
  212. sizeWidthInput.properties.text = String(format: "%.1f", value)
  213. } else {
  214. sizeWidthInput.properties.text = "-"
  215. }
  216. if let value = boundsHeight {
  217. sizeHeightInput.properties.text = String(format: "%.1f", value)
  218. } else {
  219. sizeHeightInput.properties.text = "-"
  220. }
  221. sizeWidthInput.properties.isDisabled = pdfView?.isEditImage == true
  222. sizeHeightInput.properties.isDisabled = pdfView?.isEditImage == true
  223. sizeWidthInput.reloadData()
  224. sizeHeightInput.reloadData()
  225. sizeSyncButton.properties.isDisabled = pdfView?.isEditImage == true
  226. if syncChangeBounds {
  227. sizeSyncButton.properties.state = .pressed
  228. } else {
  229. sizeSyncButton.properties.state = .normal
  230. }
  231. sizeSyncButton.reloadData()
  232. rotateSelect.properties.isDisabled = pdfView?.isEditImage == true
  233. rotateLeftButton.properties.isDisabled = pdfView?.isEditImage == true
  234. rotateLeftButton.reloadData()
  235. rotateRightButton.properties.isDisabled = pdfView?.isEditImage == true
  236. rotateRightButton.reloadData()
  237. flipVerticalButton.properties.isDisabled = pdfView?.isEditImage == true
  238. flipVerticalButton.reloadData()
  239. flipHorizontalButton.properties.isDisabled = pdfView?.isEditImage == true
  240. flipHorizontalButton.reloadData()
  241. opacitySlider.properties.isDisabled = pdfView?.isEditImage == true
  242. opacitySlider.reloadData()
  243. opacitySelect.properties.isDisabled = pdfView?.isEditImage == true
  244. replaceButton.properties.isDisabled = pdfView?.isEditImage == true
  245. replaceButton.reloadData()
  246. extractButton.properties.isDisabled = pdfView?.isEditImage == true
  247. extractButton.reloadData()
  248. if let value = rotateAngle {
  249. rotateSelect.properties.text = String(format: "%.0f", value)
  250. } else {
  251. rotateSelect.properties.text = "-"
  252. }
  253. rotateSelect.reloadData()
  254. if let value = opacity_value {
  255. opacitySelect.properties.text = String(format: "%.0f", value*100)
  256. opacitySlider.properties.percent = value
  257. } else {
  258. opacitySelect.properties.text = "-"
  259. opacitySlider.properties.percent = 0
  260. }
  261. opacitySelect.reloadData()
  262. opacitySlider.reloadData()
  263. if pdfView?.isEditImage == true {
  264. cropButton.properties.buttonText = KMLocalizedString("Cancel Crop")
  265. } else {
  266. cropButton.properties.buttonText = KMLocalizedString("Crop")
  267. }
  268. cropButton.reloadData()
  269. }
  270. //MARK: - Action
  271. @objc func sizeSyncButtonClicked(_ sender: ComponentButton) {
  272. syncChangeBounds = !syncChangeBounds
  273. if syncChangeBounds {
  274. sizeSyncButton.properties.state = .pressed
  275. } else {
  276. sizeSyncButton.properties.state = .normal
  277. }
  278. sizeSyncButton.reloadData()
  279. }
  280. @objc func buttonClicked(_ sender: ComponentButton) {
  281. if sender == rotateLeftButton {
  282. self.pdfView?.leftRotateAction()
  283. } else if sender == rotateRightButton {
  284. self.pdfView?.rightRotateAction()
  285. } else if sender == flipVerticalButton {
  286. self.pdfView?.reverseYAction()
  287. } else if sender == flipHorizontalButton {
  288. self.pdfView?.reverseXAction()
  289. } else if sender == cropButton {
  290. if pdfView?.isEditImage == true {
  291. pdfView?.cropCancelAction()
  292. } else {
  293. pdfView?.cropAction()
  294. }
  295. } else if sender == replaceButton {
  296. if let areas = self.pdfView?.km_editingImageAreas() {
  297. let panel = NSOpenPanel()
  298. panel.allowsMultipleSelection = false
  299. panel.allowedFileTypes = ["png","jpg"]
  300. panel.beginSheetModal(for: NSApp.mainWindow!) { response in
  301. if response == .OK {
  302. let openPath = panel.url?.path
  303. for area in areas {
  304. self.pdfView?.replace(area, imagePath: openPath!)
  305. }
  306. }
  307. }
  308. }
  309. } else if sender == extractButton {
  310. extractAction()
  311. }
  312. reloadData()
  313. }
  314. func extractAction() {
  315. var menuItemArr: [ComponentMenuitemProperty] = []
  316. let items: [(String, String)] = [("jpg", "export_jpg_Key"),
  317. ("png", "export_png_Key"),
  318. ("pdf", "export_pdf_Key")]
  319. for (i, value) in items {
  320. let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  321. itemSelected: false,
  322. keyEquivalent: nil,
  323. text: KMLocalizedString(i),
  324. identifier: value)
  325. menuItemArr.append(properties_Menuitem)
  326. }
  327. if groupView == nil {
  328. groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
  329. }
  330. groupView.groupDelegate = self
  331. groupView?.frame = CGRectMake(0, 0, extractButton.frame.size.width, 36*3+8)
  332. groupView.updateGroupInfo(menuItemArr)
  333. if let point: CGPoint = extractButton.superview?.convert(extractButton.frame.origin, to: self.view.window?.contentView) {
  334. groupView.showWithPoint(CGPoint(x: point.x, y: point.y - CGRectGetHeight(groupView.frame)-4), relativeTo: self.view)
  335. }
  336. }
  337. //MARK: - MouseEvent
  338. override func mouseDown(with event: NSEvent) {
  339. super.mouseDown(with: event)
  340. view.window?.makeFirstResponder(nil)
  341. }
  342. }
  343. //MARK: - ComponentInputNumberDelegate
  344. extension KMEditImageController: ComponentInputNumberDelegate {
  345. func componentInputNumberDidFinishEditing(inputNumber: ComponentInputNumber?) {
  346. if areas.count == 0 {
  347. return
  348. }
  349. var textValue = inputNumber?.properties.text.stringToCGFloat() ?? 0
  350. if inputNumber == sizeWidthInput {
  351. let areas = self.pdfView?.km_editingImageAreas() ?? []
  352. for area in areas {
  353. pdfView?.updateArea(area, newWidth: textValue, syncChangeBounds)
  354. }
  355. } else if inputNumber == sizeHeightInput {
  356. let areas = self.pdfView?.km_editingImageAreas() ?? []
  357. for area in areas {
  358. pdfView?.updateArea(area, newHeight: textValue, syncChangeBounds)
  359. }
  360. }
  361. reloadData()
  362. }
  363. func componentInputNumberDidIncrease(inputNumber: ComponentInputNumber?) {
  364. if areas.count == 0 {
  365. return
  366. }
  367. //只处理多选状态
  368. var textValue = inputNumber?.properties.text.stringToCGFloat() ?? 0
  369. if inputNumber == sizeWidthInput {
  370. let areas = self.pdfView?.km_editingImageAreas() ?? []
  371. for area in areas {
  372. textValue = area.bounds.size.width + 1
  373. pdfView?.updateArea(area, newWidth: textValue, syncChangeBounds)
  374. }
  375. } else if inputNumber == sizeHeightInput {
  376. let areas = self.pdfView?.km_editingImageAreas() ?? []
  377. for area in areas {
  378. textValue = area.bounds.size.height + 1
  379. pdfView?.updateArea(area, newHeight: textValue, syncChangeBounds)
  380. }
  381. }
  382. reloadData()
  383. }
  384. func componentInputNumberDidDecrease(inputNumber: ComponentInputNumber?) {
  385. if areas.count == 0 {
  386. return
  387. }
  388. //只处理多选状态
  389. var textValue = inputNumber?.properties.text.stringToCGFloat() ?? 0
  390. if inputNumber == sizeWidthInput {
  391. let areas = self.pdfView?.km_editingImageAreas() ?? []
  392. for area in areas {
  393. textValue = area.bounds.size.width - 1
  394. pdfView?.updateArea(area, newWidth: textValue, syncChangeBounds)
  395. }
  396. } else if inputNumber == sizeHeightInput {
  397. let areas = self.pdfView?.km_editingImageAreas() ?? []
  398. for area in areas {
  399. textValue = area.bounds.size.height - 1
  400. pdfView?.updateArea(area, newHeight: textValue, syncChangeBounds)
  401. }
  402. }
  403. reloadData()
  404. }
  405. }
  406. //MARK: - ComponentSelectDelegate
  407. extension KMEditImageController: ComponentSelectDelegate {
  408. func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) {
  409. if view == opacitySelect {
  410. if let text = opacitySelect.properties.text {
  411. let result = text.stringByDeleteCharString("%")
  412. let opacity = result.stringToCGFloat()/100
  413. pdfView?.setEditingAreasOpacity(opacity)
  414. reloadData()
  415. }
  416. } else if view == rotateSelect {
  417. if let text = rotateSelect.properties.text {
  418. let result = text.stringByDeleteCharString("°")
  419. let value = result.stringToCGFloat()
  420. pdfView?.rotateEditingAreas(value)
  421. reloadData()
  422. }
  423. }
  424. }
  425. func componentSelectTextDidEndEditing(_ view: ComponentSelect) {
  426. if view == opacitySelect {
  427. if let text = opacitySelect.properties.text {
  428. let result = text.stringByDeleteCharString("%")
  429. if result != "-" {
  430. let opacity = result.stringToCGFloat()/100
  431. pdfView?.setEditingAreasOpacity(opacity)
  432. }
  433. reloadData()
  434. }
  435. } else if view == rotateSelect {
  436. if let text = rotateSelect.properties.text {
  437. let result = text.stringByDeleteCharString("°")
  438. if result != "-" {
  439. let value = result.stringToCGFloat()
  440. pdfView?.rotateEditingAreas(value)
  441. }
  442. reloadData()
  443. }
  444. }
  445. }
  446. }
  447. //MARK: - ComponentCColorDelegate
  448. extension KMEditImageController: ComponentCColorDelegate {
  449. }
  450. //MARK: - ComponentSliderDelegate
  451. extension KMEditImageController: ComponentSliderDelegate {
  452. func componentSliderDidUpdate(_ view: ComponentSlider) {
  453. let percent = view.properties.percent
  454. self.pdfView?.setEditingAreasOpacity(percent)
  455. reloadData()
  456. }
  457. }
  458. //MARK: - ComponentGroupDelegate
  459. extension KMEditImageController: ComponentGroupDelegate {
  460. func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) {
  461. if menuItemProperty?.identifier == "export_jpg_Key" {
  462. pdfView?.exportEditingImageAreasAction(format: "jpg")
  463. } else if menuItemProperty?.identifier == "export_png_Key" {
  464. pdfView?.exportEditingImageAreasAction(format: "png")
  465. } else if menuItemProperty?.identifier == "export_pdf_Key" {
  466. pdfView?.exportEditingImageAreasAction(format: "pdf")
  467. }
  468. }
  469. func componentGroupDidDismiss(group: ComponentGroup?) {
  470. extractButton.properties.state = .normal
  471. extractButton.reloadData()
  472. }
  473. }
  474. //MARK: - KMNAlignmentControllerDelegate
  475. extension KMEditImageController: KMNAlignmentControllerDelegate {
  476. func alignmentControllerDidClick(_ controller: KMNAlignmentController, _ alignmentType: KMPDFActiveFormsAlignType) {
  477. pdfView?.changeEditingAreas(alignmentType)
  478. }
  479. }