// // KMImageToolTipContext.swift // PDF Reader Pro // // Created by tangchao on 2023/11/23. // import Foundation protocol KMImageToolTipContext: NSObjectProtocol { func toolTipImage() -> NSImage? } extension NSAttributedString: KMImageToolTipContext { func toolTipImage() -> NSImage? { return nil } } 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 private 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 = 260 // 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 %@", "Tool tip label format"), 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? { return nil } } extension CPDFPage: KMImageToolTipContext { func toolTipImage() -> NSImage? { return nil } }