KMImageToolTipContext.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //
  2. // KMImageToolTipContext.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/23.
  6. //
  7. import Foundation
  8. @objc protocol KMImageToolTipContext: NSObjectProtocol {
  9. func toolTipImage() -> NSImage?
  10. }
  11. extension NSAttributedString: KMImageToolTipContext {
  12. func toolTipImage() -> NSImage? {
  13. return nil
  14. }
  15. }
  16. var _km_dest_labelAttributes: [NSAttributedString.Key : Any]?
  17. var _km_dest_labelColor: NSColor?
  18. var _km_text_margin_x: CGFloat = 2
  19. var _km_text_margin_y: CGFloat = 2
  20. extension CPDFDestination: KMImageToolTipContext {
  21. func toolTipImage() -> NSImage? {
  22. return self._toolTipImage(offset: NSMakePoint(-50.0, 20.0))
  23. }
  24. @objc internal func _toolTipImage(offset: NSPoint) -> NSImage? {
  25. guard let page = self.page() else {
  26. return nil
  27. }
  28. if _km_dest_labelAttributes == nil {
  29. let style = NSMutableParagraphStyle()
  30. style.lineBreakMode = .byClipping
  31. _km_dest_labelAttributes = [.font : NSFont.boldSystemFont(ofSize: 11), .foregroundColor : NSColor.white, .paragraphStyle : style]
  32. }
  33. let attri = _km_dest_labelAttributes ?? [:]
  34. if _km_dest_labelColor == nil {
  35. _km_dest_labelColor = NSColor(calibratedWhite: 0.5, alpha: 0.8)
  36. }
  37. let labelColor = _km_dest_labelColor ?? NSColor(calibratedWhite: 0.5, alpha: 0.8)
  38. // NSImage *pageImage = [page thumbnailWithSize:0.0 forBox:kPDFDisplayBoxCropBox shadowBlurRadius:0.0 readingBar:nil];
  39. let pageImage = page.thumbnail(of: page.bounds(for: .cropBox).size)
  40. let pageSize = page.bounds(for: .cropBox)
  41. let pageImageRect = NSMakeRect(0, 0, pageSize.width, pageSize.height)
  42. let bounds = page.bounds(for: .cropBox)
  43. var sourceRect: NSRect = .zero
  44. let selection = page.selection(for: bounds)
  45. let transform = page.km_affineTransform(for: .cropBox)
  46. // sourceRect.size.width = [[NSUserDefaults standardUserDefaults] doubleForKey:SKToolTipWidthKey];
  47. sourceRect.size.width = 400
  48. // sourceRect.size.height = [[NSUserDefaults standardUserDefaults] doubleForKey:SKToolTipHeightKey];
  49. sourceRect.size.height = 120
  50. sourceRect.origin = KMAddPoints(transform.transform(self.point), offset)
  51. sourceRect.origin.y -= NSHeight(sourceRect)
  52. if let data = selection?.hasCharacters(), data {
  53. var selBounds = selection?.bounds(for: page) ?? .zero
  54. let blPoint = KMBottomLeftPoint(selBounds)
  55. let trPoint = KMTopRightPoint(selBounds)
  56. selBounds = KMRectFromPoints(aPoint: blPoint, bPoint: trPoint)
  57. let top = ceil(fmax(NSMaxY(selBounds), NSMinY(selBounds) + NSHeight(sourceRect)))
  58. let left = floor(fmin(NSMinX(selBounds), NSMaxX(selBounds) - NSWidth(sourceRect)))
  59. if (top < NSMaxY(sourceRect)) {
  60. sourceRect.origin.y = top - NSHeight(sourceRect)
  61. }
  62. if (left > NSMinX(sourceRect)) {
  63. sourceRect.origin.x = left
  64. }
  65. }
  66. sourceRect = KMConstrainRect(rect: sourceRect, boundary: pageImageRect)
  67. let labelString = NSAttributedString(string: String(format: KMLocalizedString("Page %@", "Tool tip label format"), page.km_displayLabel), attributes: attri)
  68. var labelRect = labelString.boundingRect(with: .zero, options: [.usesLineFragmentOrigin])
  69. labelRect.size.width = floor(NSWidth(labelRect))
  70. labelRect.size.height = 2.0 * floor(0.5 * NSHeight(labelRect)) // make sure the cap radius is integral
  71. labelRect.origin.x = NSWidth(sourceRect) - NSWidth(labelRect) - 0.5 * NSHeight(labelRect) - _km_text_margin_x
  72. labelRect.origin.y = _km_text_margin_y
  73. labelRect = NSIntegralRect(labelRect)
  74. let image = NSImage.bitmapImage(with: sourceRect.size) { rect in
  75. pageImage?.draw(in: rect, from: sourceRect, operation: .copy, fraction: 1)
  76. let radius = 0.5 * NSHeight(labelRect)
  77. let path = NSBezierPath()
  78. path.move(to: KMTopLeftPoint(labelRect))
  79. path.appendArc(withCenter: NSMakePoint(NSMinX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: 90.0, endAngle: 270.0)
  80. path.appendArc(withCenter: NSMakePoint(NSMaxX(labelRect), NSMidY(labelRect)), radius: radius, startAngle: -90, endAngle: 90)
  81. path.close()
  82. labelColor.setFill()
  83. path.fill()
  84. labelString.draw(with: labelRect, options: [.usesLineFragmentOrigin])
  85. }
  86. return image
  87. }
  88. }
  89. extension CPDFAnnotation: KMImageToolTipContext {
  90. func toolTipImage() -> NSImage? {
  91. if self.isLink() {
  92. if let destination: CPDFDestination = self.linkDestination() {
  93. var image = destination._toolTipImage(offset: .zero)
  94. if image == nil{
  95. let url = self.linkURL()
  96. if url != nil{
  97. let attrString = toolTipAttributedString(url?.absoluteString ?? "")
  98. if attrString.length > 0 {
  99. image = attrString.toolTipImage()
  100. }
  101. }
  102. }
  103. if image != nil {
  104. return image
  105. }
  106. }else{
  107. return nil
  108. }
  109. }
  110. var attrString = self.text()
  111. var string = attrString?.string
  112. var i = 0
  113. var l = string?.count
  114. if l == 0 || ((string?.rangeOfCharacter(from: CharacterSet.whitespacesAndNewlines)) == nil) {
  115. string = self.string()
  116. l = string?.count
  117. if l ?? 0 > 0 {
  118. attrString = toolTipAttributedString(string ?? "")
  119. }else {
  120. attrString = nil
  121. }
  122. }
  123. attrString = KMOCTool.transformAttr(attrString ?? NSAttributedString(string: ""), with: string ?? "")
  124. if attrString?.length ?? 0 > 0 {
  125. return attrString?.toolTipImage()
  126. }else{
  127. return nil
  128. }
  129. }
  130. }
  131. func toolTipAttributedString(_ string: String) -> NSAttributedString {
  132. var attributes: [NSAttributedString.Key: Any]? = nil
  133. if attributes == nil {
  134. attributes = [NSAttributedString.Key.font: NSFont.toolTipsFont(ofSize: 11.0), NSAttributedString.Key.paragraphStyle: NSParagraphStyle.defaultClippingParagraphStyle]
  135. }
  136. return NSAttributedString(string: string, attributes: attributes)
  137. }
  138. extension CPDFPage: KMImageToolTipContext {
  139. func toolTipImage() -> NSImage? {
  140. return nil
  141. }
  142. }
  143. extension NSParagraphStyle{
  144. static var defaultClippingParagraphStyle: NSParagraphStyle {
  145. var style: NSParagraphStyle? = nil
  146. if style == nil {
  147. let tmpParagraphStyle = (NSParagraphStyle.default as! NSMutableParagraphStyle).mutableCopy() as! NSMutableParagraphStyle
  148. tmpParagraphStyle.lineBreakMode = .byClipping
  149. style = tmpParagraphStyle.copy() as? NSParagraphStyle
  150. }
  151. return style!
  152. }
  153. static var defaultTruncatingTailParagraphStyle: NSParagraphStyle {
  154. var style: NSParagraphStyle? = nil
  155. if style == nil {
  156. let tmpParagraphStyle = (NSParagraphStyle.default as! NSMutableParagraphStyle).mutableCopy() as! NSMutableParagraphStyle
  157. tmpParagraphStyle.lineBreakMode = .byTruncatingTail
  158. style = tmpParagraphStyle.copy() as? NSParagraphStyle
  159. }
  160. return style!
  161. }
  162. }