123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- //
- // KMPDFSignatureImageView.swift
- // PDF Reader Pro
- //
- // Created by lizhe on 2023/10/9.
- //
- import Cocoa
- import Quartz
- let KMSignatureMaxWidth = 400.0
- let KMSignatureMaxHeight = 300.0
- @objcMembers class KMPDFSignatureImageView: NSView {
- var picImage: NSImage?
- @IBOutlet var pictureView: NSView!
- // @IBOutlet var emptyTipLbl: NSTextField!
- @IBOutlet var dragButton: NSButton!
- // @IBOutlet var emptyImg: NSImageView!
-
- @IBOutlet var heraLabel: NSTextField!
- @IBOutlet var orLabel: NSTextField!
-
- var clearBackground: Bool = false {
- didSet {
- if let imageURL = imageURL, imageURL.path.count > 0 {
- loadImageViewPath(imageURL, isRemoveBGColor: clearBackground)
- }
- }
- }
-
- var trackingArea: NSTrackingArea?
- var imageURL: URL?
-
- var changeSignatureImageCallback: ((Bool) -> Void)?
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- wantsLayer = true
- layer?.borderWidth = 1.0
- layer?.borderColor = NSColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.05).cgColor
- layer?.backgroundColor = NSColor(red: 1, green: 1, blue: 1, alpha: 0.9).cgColor
- registerForDraggedTypes([.fileURL])
- }
-
- override init(frame frameRect: NSRect) {
- super.init(frame: frameRect)
- registerForDraggedTypes([.fileURL])
- }
- override func awakeFromNib() {
- super.awakeFromNib()
- // emptyTipLbl.stringValue = NSLocalizedString("Select image file", comment: "")
- // emptyTipLbl.textColor = .labelColor
- self.heraLabel.stringValue = NSLocalizedString("Drop image here", comment: "")
- self.orLabel.stringValue = NSLocalizedString("or", comment: "")
- dragButton.title = NSLocalizedString("Select a File", comment: "")
- dragButton.wantsLayer = true
- dragButton.setTitleColor(color: .labelColor, font: nil)
- pictureView.wantsLayer = true
- // emptyImg.image = NSImage(named: "signPicture_nor")
- trackingArea = NSTrackingArea(rect: bounds, options: [.mouseEnteredAndExited, .mouseMoved, .activeAlways], owner: self, userInfo: nil)
- if let trackingArea = trackingArea {
- addTrackingArea(trackingArea)
- }
- }
- func supportedImageTypes() -> [String] {
- return ["jpg", "cur", "bmp", "jpeg", "gif", "png", "tiff", "tif", "ico", "icns", "tga", "psd", "eps", "hdr", "jp2", "jpc", "pict", "sgi", "pdf"]
- }
- override func draw(_ dirtyRect: NSRect) {
- super.draw(dirtyRect)
- if let picImage = picImage {
- let selfSize = self.frame.size
- var rect = NSZeroRect
- if let imageCIImage = CIImage(data: picImage.tiffRepresentation!) {
- let size = imageCIImage.extent.size
- let scale = min((selfSize.width - 30) / size.width, (selfSize.height - 30) / size.height)
- if scale > 1 {
- rect = NSRect(x: 0, y: 0, width: size.width, height: size.height)
- } else {
- rect = NSRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)
- }
- picImage.draw(in: NSRect(x: (selfSize.width - rect.size.width) / 2, y: (selfSize.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height), from: NSZeroRect, operation: .sourceOver, fraction: 1.0)
- }
- }
- }
- func clearImage() {
- picImage = nil
- imageURL = nil
- pictureView.isHidden = false
- // emptyTipLbl.isHidden = false
- needsDisplay = true
- }
- func reSelectImage() {
- buttonItemClick_SelectPhoto(nil)
- }
- func signatureImage() -> NSImage? {
- var rect = CGRect.zero
-
- guard let picImage = self.picImage else {
- return nil
- }
-
- let imageCIImage = CIImage(data: picImage.tiffRepresentation!)
- let size = imageCIImage?.extent.size ?? CGSize.zero
-
- let scale = min(KMSignatureMaxWidth / size.width, KMSignatureMaxHeight / size.height)
-
- if scale > 1 {
- rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
- } else {
- rect = CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)
- }
-
- let image = NSImage(size: rect.size)
- image.lockFocus()
- picImage.draw(in: rect, from: NSZeroRect, operation: .sourceOver, fraction: 1.0)
- image.unlockFocus()
-
- return image
- }
- func loadImageViewPath(_ url: URL, isRemoveBGColor: Bool) {
- self.imageURL = url
- let filePath: NSString = url.path as NSString
-
- if filePath.pathExtension.lowercased() == "pdf" {
- if let pdf = PDFDocument(url: url), pdf.isEncrypted {
- return
- }
- }
-
- if let image = NSImage(contentsOfFile: filePath as String) {
- if isRemoveBGColor {
- // if let imageData = image.tiffRepresentation {
- // if let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil),
- // let imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
- //
- // let imageWidth = image.size.width
- // let imageHeight = image.size.height
- // let bytesPerRow = Int(imageWidth) * 4
- // var rgbImageBuf = [UInt32](repeating: 0, count: Int(bytesPerRow) * Int(imageHeight))
- //
- // let colorSpace = CGColorSpaceCreateDeviceRGB()
- // let imageRect = CGRect(x: 0.0, y: 0.0, width: imageWidth, height: imageHeight)
- //
- // if let context = CGContext(data: &rgbImageBuf,
- // width: Int(imageWidth),
- // height: Int(imageHeight),
- // bitsPerComponent: 8,
- // bytesPerRow: bytesPerRow,
- // space: colorSpace,
- // bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue),
- // let dataProvider = CGDataProvider(dataInfo: nil,
- // data: &rgbImageBuf,
- // size: Int(bytesPerRow) * Int(imageHeight),
- // releaseData: { (info, data, size) in
- // data.deallocate()
- // }),
- // let newImageRef = CGImage(width: Int(imageWidth),
- // height: Int(imageHeight),
- // bitsPerComponent: 8,
- // bitsPerPixel: 32,
- // bytesPerRow: bytesPerRow,
- // space: colorSpace,
- // bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue),
- // provider: dataProvider,
- // decode: nil,
- // shouldInterpolate: true,
- // intent: .defaultIntent) {
- //
- // let newImage = NSImage(size: CGSize(width: imageWidth, height: imageHeight))
- // newImage.lockFocus()
- //
- // let imageContext = NSGraphicsContext(cgContext: context, flipped: false)
- // NSGraphicsContext.current = imageContext
- // context.draw(imageRef, in: imageRect)
- // newImage.unlockFocus()
- // self.picImage = newImage
- // self.pictureView.isHidden = true
- // self.emptyTipLbl.isHidden = true
- // self.setNeedsDisplay(NSRect.zero)
- //
- // }
- // }
- // }
- let resultImage = self.makeImageTransparent(image)
- self.picImage = resultImage
- self.pictureView.isHidden = true
- // self.emptyTipLbl.isHidden = true
- self.layer?.setNeedsDisplay()
- debugPrint("移除成功")
- } else {
- self.picImage = image
- self.pictureView.isHidden = true
- // self.emptyTipLbl.isHidden = true
- self.layer?.setNeedsDisplay()
- }
- }
- }
-
- func makeImageTransparent(_ inputImage: NSImage) -> NSImage? {
- if let cgImage = inputImage.cgImage(forProposedRect: nil, context: nil, hints: nil) {
- // 创建一个位图上下文,与图像大小相同
- let width = cgImage.width
- let height = cgImage.height
-
- let colorSpace = CGColorSpaceCreateDeviceRGB()
- let bytesPerPixel = 4
- let bytesPerRow = bytesPerPixel * width
- let bitsPerComponent = 8
-
- let context = CGContext(data: nil,
- width: width,
- height: height,
- bitsPerComponent: bitsPerComponent,
- bytesPerRow: bytesPerRow,
- space: colorSpace,
- bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
-
- // 绘制图像到上下文
- context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
-
- // 获取上下文中的像素数据
- if let data = context?.data {
- let buffer = data.bindMemory(to: UInt8.self, capacity: width * height * bytesPerPixel)
-
- // 迭代每个像素并将白色像素变为透明
- for y in 0..<height {
- for x in 0..<width {
- let pixelIndex = (y * width + x) * bytesPerPixel
- let red = buffer[pixelIndex]
- let green = buffer[pixelIndex + 1]
- let blue = buffer[pixelIndex + 2]
-
- // 判断是否是白色像素(这里假设白色为红、绿和蓝都为255)
- if red > 240 && green > 240 && blue > 240 {
- // 将白色像素的 alpha 通道设为0,使其变为透明
- buffer[pixelIndex + 3] = 0
- }
- }
- }
-
- // 创建新的 CGImage
- if let newCgImage = context?.makeImage() {
- // 创建新的 NSImage
- let newImage = NSImage(cgImage: newCgImage, size: inputImage.size)
- return newImage
- // 现在,newImage 包含具有白色像素变为透明的图像
- // 可以将其显示在您的应用程序中或保存到磁盘上
- }
- }
- }
- return nil
- }
- func setGradualChanging() -> CAGradientLayer {
- let gradientLayer = CAGradientLayer()
- gradientLayer.frame = dragButton.bounds
- gradientLayer.colors = [NSColor.lightGray.cgColor, NSColor.white.cgColor]
- gradientLayer.startPoint = CGPoint(x: 0, y: 0)
- gradientLayer.endPoint = CGPoint(x: 1, y: 1)
- gradientLayer.locations = [0, 1]
- return gradientLayer
- }
- func isDamageImage(_ image: NSImage, imagePath: String) -> Bool {
- let addImageAnnotation = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last?.appending("/\(Bundle.main.bundleIdentifier!)") ?? ""
- if !FileManager.default.fileExists(atPath: addImageAnnotation) {
- try? FileManager.default.createDirectory(atPath: addImageAnnotation, withIntermediateDirectories: false, attributes: nil)
- }
- if let imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) {
- imageRep.size = image.size
- var imageData: Data?
- if imagePath.lowercased() == "png" {
- imageData = imageRep.representation(using: .png, properties: [:])
- } else {
- imageData = imageRep.representation(using: .jpeg, properties: [:])
- }
- if let imageData = imageData, let tagString = tagString() {
- let rPath = (addImageAnnotation.stringByAppendingPathComponent(tagString)).stringByAppendingPathExtension("png")
- do {
- try imageData.write(to: URL(fileURLWithPath: rPath))
- // NSWorkspace.shared.selectFile(savePanel.url?.path, inFileViewerRootedAtPath: "")
- return false
- } catch {
- return true
- }
- }
- }
- return false
- }
- func tagString() -> String? {
- let dateFormatter = DateFormatter()
- dateFormatter.dateFormat = "yyMMddHHmmss"
- return String(format: "%@%04d", dateFormatter.string(from: Date()), Int(arc4random_uniform(10000)))
- }
- @IBAction func buttonItemClick_SelectPhoto(_ sender: Any?) {
- let openPanel = NSOpenPanel()
- openPanel.allowedFileTypes = supportedImageTypes()
- if let accessoryView = openPanel.accessoryView as? NSButton {
- accessoryView.state = UserDefaults.standard.bool(forKey: "AnnotationSelectPhoto") ? .on : .off
- }
- openPanel.allowsMultipleSelection = false
- openPanel.beginSheetModal(for: window ?? NSApp.mainWindow!) { [weak self] (result) in
- if result == .OK, let url = openPanel.url {
- // Check if the PDF file is damaged
- let filePath: NSString = url.path as NSString
- if filePath.pathExtension.lowercased() == "pdf" {
- if let pdf = PDFDocument(url: url), !pdf.isEncrypted {
- self?.loadImageViewPath(url, isRemoveBGColor: self?.clearBackground ?? false)
- self?.changeSignatureImageCallback?(true)
- return
- } else {
- let alert = NSAlert()
- alert.alertStyle = .critical
- alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
- alert.runModal()
- return
- }
- }
- if let image = NSImage(contentsOfFile: url.path), self?.isDamageImage(image, imagePath: url.path) == true {
- let alert = NSAlert()
- alert.alertStyle = .critical
- alert.messageText = String(format: NSLocalizedString("The file \"%@\" could not be opened.", comment: ""), url.path.lastPathComponent)
- alert.informativeText = NSLocalizedString("It may be damaged or use a file format that PDFTech Editor doesn’t recognize.", comment: "")
- alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
- alert.beginSheetModal(for: NSApp.mainWindow ?? NSApp.keyWindow!) { (returnCode) in
- if returnCode == .alertFirstButtonReturn {
- }
- }
- return
- }
- self?.loadImageViewPath(url, isRemoveBGColor: self?.clearBackground ?? false)
- self?.changeSignatureImageCallback?(true)
- }
- }
- }
- override func mouseEntered(with event: NSEvent) {
- super.mouseEntered(with: event)
- // emptyImg.image = NSImage(named: "signPicture_hover")
- }
- override func mouseMoved(with event: NSEvent) {
- super.mouseMoved(with: event)
- if let windowContentView = window?.contentView, let convertPoint = pictureView?.convert(event.locationInWindow, from: windowContentView) {
- if pictureView.bounds.contains(convertPoint) {
- // emptyImg.image = NSImage(named: "signPicture_hover")
- } else {
- // emptyImg.image = NSImage(named: "signPicture_nor")
- }
- }
- }
- override func mouseExited(with event: NSEvent) {
- super.mouseExited(with: event)
- // emptyImg.image = NSImage(named: "signPicture_nor")
- }
- override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
- let pboard = sender.draggingPasteboard
- if pboard.availableType(from: [.fileURL]) != nil {
- guard let pbItems = pboard.pasteboardItems else {
- return NSDragOperation(rawValue: 0)
- }
- for item in pbItems {
- guard let data = item.string(forType: .fileURL), let _url = URL(string: data) else {
- continue
- }
- if supportedImageTypes().contains(_url.pathExtension.lowercased()) {
- return .every
- }
- }
- }
- return []
- }
- override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
- let pboard = sender.draggingPasteboard
- if pboard.availableType(from: [.fileURL]) != nil {
- guard let pbItems = pboard.pasteboardItems else {
- return false
- }
- for item in pbItems {
- guard let data = item.string(forType: .fileURL), let _url = URL(string: data) else {
- continue
- }
- if supportedImageTypes().contains(_url.pathExtension.lowercased()) {
- loadImageViewPath(_url, isRemoveBGColor: clearBackground)
- changeSignatureImageCallback?(true)
- return true
- }
- }
- }
- return false
- }
- }
- private extension NSImage {
- func resized(to size: NSSize) -> NSImage {
- let image = NSImage(size: size)
- image.lockFocus()
- let ctx = NSGraphicsContext.current?.cgContext
- ctx?.interpolationQuality = .high
- draw(in: NSRect(origin: .zero, size: size), from: NSRect(origin: .zero, size: self.size), operation: .sourceOver, fraction: 1.0)
- image.unlockFocus()
- return image
- }
- }
|