123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- //
- // KMImageToolTipContext.swift
- // PDF Reader Pro
- //
- // Created by tangchao on 2023/11/23.
- //
- import Foundation
- let TEXT_MARGIN_X = 2.0
- let TEXT_MARGIN_Y = 2.0
- let SKToolTipWidthKey = "SKToolTipWidth"
- let SKToolTipHeightKey = "SKToolTipHeight"
- @objc protocol KMImageToolTipContext: NSObjectProtocol {
- func toolTipImage() -> NSImage?
- }
- extension NSAttributedString: KMImageToolTipContext {
- // func toolTipImage() -> NSImage? {
- // return nil
- // }
-
- func toolTipImage() -> NSImage? {
- let backgroundColor: NSColor = {
- if #available(OSX 10.9, *) {
- return NSColor(calibratedRed: 0.95, green: 0.95, blue: 0.95, alpha: 1.0)
- } else {
- return NSColor(calibratedRed: 1.0, green: 1.0, blue: 0.75, alpha: 1.0)
- }
- }()
-
- let width = UserDefaults.standard.double(forKey: SKToolTipWidthKey) - 2.0 * TEXT_MARGIN_X
- let height = UserDefaults.standard.double(forKey: SKToolTipHeightKey) - 2.0 * TEXT_MARGIN_Y
- var textRect = self.boundingRect(with: NSSize(width: width, height: height), options: .usesLineFragmentOrigin)
-
- textRect.origin = NSPoint(x: TEXT_MARGIN_X, y: TEXT_MARGIN_Y)
- textRect.size.height = min(textRect.height, height)
- textRect = NSInsetRect(NSIntegralRect(textRect), -TEXT_MARGIN_X, -TEXT_MARGIN_Y)
-
- let image = NSImage(size: textRect.size, flipped: false) { rect in
- backgroundColor.setFill()
- rect.fill()
- self.draw(with: NSRect(x: TEXT_MARGIN_X, y: TEXT_MARGIN_Y, width: rect.width - 2 * TEXT_MARGIN_X, height: rect.height - 2 * TEXT_MARGIN_Y), options: .usesLineFragmentOrigin)
- return true
- }
-
- return image
- }
- }
- var _km_dest_labelAttributes: [NSAttributedString.Key : Any]?
- var _km_dest_labelColor: NSColor?
- var _km_text_margin_x: CGFloat = 2
- var _km_text_margin_y: CGFloat = 2
- extension CPDFDestination: KMImageToolTipContext {
- func toolTipImage() -> NSImage? {
- return self._toolTipImage(offset: NSMakePoint(-50.0, 20.0))
- }
- @objc internal func _toolTipImage(offset: NSPoint) -> NSImage? {
- guard let page = self.page() else {
- return nil
- }
-
- if _km_dest_labelAttributes == nil {
- let style = NSMutableParagraphStyle()
- style.lineBreakMode = .byClipping
- _km_dest_labelAttributes = [.font : NSFont.boldSystemFont(ofSize: 11), .foregroundColor : NSColor.white, .paragraphStyle : style]
- }
- let attri = _km_dest_labelAttributes ?? [:]
-
- if _km_dest_labelColor == nil {
- _km_dest_labelColor = NSColor(calibratedWhite: 0.5, alpha: 0.8)
- }
- let labelColor = _km_dest_labelColor ?? NSColor(calibratedWhite: 0.5, alpha: 0.8)
-
- // NSImage *pageImage = [page thumbnailWithSize:0.0 forBox:kPDFDisplayBoxCropBox shadowBlurRadius:0.0 readingBar:nil];
- let pageImage = page.thumbnail(of: page.bounds(for: .cropBox).size)
- let pageSize = page.bounds(for: .cropBox)
- let pageImageRect = NSMakeRect(0, 0, pageSize.width, pageSize.height)
- let bounds = page.bounds(for: .cropBox)
- var sourceRect: NSRect = .zero
- let selection = page.selection(for: bounds)
-
- let transform = page.km_affineTransform(for: .cropBox)
- // sourceRect.size.width = [[NSUserDefaults standardUserDefaults] doubleForKey:SKToolTipWidthKey];
- sourceRect.size.width = 400
- // sourceRect.size.height = [[NSUserDefaults standardUserDefaults] doubleForKey:SKToolTipHeightKey];
- sourceRect.size.height = 120
- sourceRect.origin = KMAddPoints(transform.transform(self.point), offset)
- sourceRect.origin.y -= NSHeight(sourceRect)
-
- if let data = selection?.hasCharacters(), data {
- var selBounds = selection?.bounds(for: page) ?? .zero
- let blPoint = KMBottomLeftPoint(selBounds)
- let trPoint = KMTopRightPoint(selBounds)
- selBounds = KMRectFromPoints(aPoint: blPoint, bPoint: trPoint)
- let top = ceil(fmax(NSMaxY(selBounds), NSMinY(selBounds) + NSHeight(sourceRect)))
- let left = floor(fmin(NSMinX(selBounds), NSMaxX(selBounds) - NSWidth(sourceRect)))
- if (top < NSMaxY(sourceRect)) {
- sourceRect.origin.y = top - NSHeight(sourceRect)
- }
- if (left > NSMinX(sourceRect)) {
- sourceRect.origin.x = left
- }
- }
-
- sourceRect = KMConstrainRect(rect: sourceRect, boundary: pageImageRect)
-
- let labelString = NSAttributedString(string: String(format: KMLocalizedString("Page %@"), page.km_displayLabel), attributes: attri)
- var labelRect = labelString.boundingRect(with: .zero, options: [.usesLineFragmentOrigin])
- labelRect.size.width = floor(NSWidth(labelRect))
- labelRect.size.height = 2.0 * floor(0.5 * NSHeight(labelRect)) // make sure the cap radius is integral
- labelRect.origin.x = NSWidth(sourceRect) - NSWidth(labelRect) - 0.5 * NSHeight(labelRect) - _km_text_margin_x
- labelRect.origin.y = _km_text_margin_y
- labelRect = NSIntegralRect(labelRect)
- let image = NSImage.bitmapImage(with: sourceRect.size) { rect in
- pageImage?.draw(in: rect, from: sourceRect, operation: .copy, fraction: 1)
- let radius = 0.5 * NSHeight(labelRect)
- let path = NSBezierPath()
- path.move(to: KMTopLeftPoint(labelRect))
- path.appendArc(withCenter: NSMakePoint(NSMinX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: 90.0, endAngle: 270.0)
- path.appendArc(withCenter: NSMakePoint(NSMaxX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: -90, endAngle: 90)
- path.close()
- labelColor.setFill()
- path.fill()
- labelString.draw(with: labelRect, options: [.usesLineFragmentOrigin])
- }
- return image
- }
- }
- extension CPDFAnnotation: KMImageToolTipContext {
- func toolTipImage() -> NSImage? {
- if self.isLink() {
- if let destination: CPDFDestination = self.linkDestination() {
- var image = destination._toolTipImage(offset: .zero)
- if image == nil{
- let url = self.linkURL()
- if url != nil{
- let attrString = toolTipAttributedString(url?.absoluteString ?? "")
- if attrString.length > 0 {
- image = attrString.toolTipImage()
- }
- }
- }
- if image != nil {
- return image
- }
- }else{
- return nil
- }
- }
- var attrString = self.text()
- var string = attrString?.string
- var i = 0
- var l = string?.count
- if l == 0 || ((string?.rangeOfCharacter(from: CharacterSet.whitespacesAndNewlines)) == nil) {
- string = self.string()
- l = string?.count
- if l ?? 0 > 0 {
- attrString = toolTipAttributedString(string ?? "")
- }else {
- attrString = nil
- }
- }
- attrString = KMOCTool.transformAttr(attrString ?? NSAttributedString(string: ""), with: string ?? "")
-
- if attrString?.length ?? 0 > 0 {
- return attrString?.toolTipImage()
- }else{
- return nil
- }
- }
- }
- func toolTipAttributedString(_ string: String) -> NSAttributedString {
- var attributes: [NSAttributedString.Key: Any]? = nil
- if attributes == nil {
- attributes = [NSAttributedString.Key.font: NSFont.toolTipsFont(ofSize: 11.0), NSAttributedString.Key.paragraphStyle: NSParagraphStyle.defaultClippingParagraphStyle]
- }
- return NSAttributedString(string: string, attributes: attributes)
- }
- extension CPDFPage: KMImageToolTipContext {
- func toolTipImage() -> NSImage? {
- return nil
- }
- }
- extension CPDFBookmark: KMImageToolTipContext {
- func toolTipImage() -> NSImage? {
- guard let page = self.document?.page(at: UInt(self.pageIndex)) else {
- return nil
- }
- let image = page.thumbnail(of: page.size)
- image?.size = .init(width: 164, height: 224)
-
- let attri = _km_dest_labelAttributes ?? [:]
-
- let sourceRect = NSMakeRect(0, 0, 164, 224)
- let labelString = NSAttributedString(string: String(format: KMLocalizedString("Page %@"), page.km_displayLabel), attributes: attri)
- var labelRect = labelString.boundingRect(with: .zero, options: [.usesLineFragmentOrigin])
- labelRect.size.width = floor(NSWidth(labelRect)) + 10
- // labelRect.size.height = 2.0 * floor(0.5 * NSHeight(labelRect)) // make sure the cap radius is integral
- labelRect.size.height = 20
- // labelRect.origin.x = NSWidth(sourceRect) - NSWidth(labelRect) - 0.5 * NSHeight(labelRect) - _km_text_margin_x
- labelRect.origin.x = 8
- labelRect.origin.y = 8
- labelRect = NSIntegralRect(labelRect)
- let labelColor = _km_dest_labelColor ?? NSColor(calibratedWhite: 0.5, alpha: 0.8)
- let kimage = NSImage.bitmapImage(with: sourceRect.size) { rect in
- image?.draw(in: rect, from: sourceRect, operation: .copy, fraction: 1)
- let radius = 0.5 * NSHeight(labelRect)
- let path = NSBezierPath()
- path.move(to: KMTopLeftPoint(labelRect))
- path.appendArc(withCenter: NSMakePoint(NSMinX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: 90.0, endAngle: 270.0)
- path.appendArc(withCenter: NSMakePoint(NSMaxX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: -90, endAngle: 90)
- path.close()
- labelColor.setFill()
- path.fill()
- labelString.draw(with: labelRect.insetBy(dx: 0, dy: 2), options: [.usesLineFragmentOrigin])
- }
-
- return kimage
- }
- }
- extension NSParagraphStyle {
- static var defaultClippingParagraphStyle: NSParagraphStyle = {
- let tmpParagraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
- tmpParagraphStyle.lineBreakMode = .byClipping
- return tmpParagraphStyle.copy() as! NSParagraphStyle
- }()
-
- static var defaultTruncatingTailParagraphStyle: NSParagraphStyle = {
- let tmpParagraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
- tmpParagraphStyle.lineBreakMode = .byTruncatingTail
- return tmpParagraphStyle.copy() as! NSParagraphStyle
- }()
- }
- //extension NSParagraphStyle{
- // static var defaultClippingParagraphStyle: NSParagraphStyle {
- // var style: NSParagraphStyle? = nil
- // if style == nil {
- // let tmpParagraphStyle = (NSParagraphStyle.default as! NSMutableParagraphStyle).mutableCopy() as! NSMutableParagraphStyle
- // tmpParagraphStyle.lineBreakMode = .byClipping
- // style = tmpParagraphStyle.copy() as? NSParagraphStyle
- // }
- // return style!
- // }
- //
- // static var defaultTruncatingTailParagraphStyle: NSParagraphStyle {
- // var style: NSParagraphStyle? = nil
- // if style == nil {
- // let tmpParagraphStyle = (NSParagraphStyle.default as! NSMutableParagraphStyle).mutableCopy() as! NSMutableParagraphStyle
- // tmpParagraphStyle.lineBreakMode = .byTruncatingTail
- // style = tmpParagraphStyle.copy() as? NSParagraphStyle
- // }
- // return style!
- // }
- //}
|