1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
#if SwiftMathSupport
import SwiftMath
import SwiftUI
/// ``NorgMathRenderer`` backed by SwiftMath. Enabled by the
/// `SwiftMathSupport` package trait.
struct SwiftMathRenderer: NorgMathRenderer {
@MainActor
func render(
latex: String, mode: NorgMathMode, fontSize: CGFloat, color: Color?
) async -> NorgMathImage? {
let label = MTMathUILabel()
label.latex = latex
label.labelMode = mode == .block ? .display : .text
label.fontSize = mode == .block ? fontSize * 1.4 : fontSize
if let color {
#if canImport(UIKit)
label.textColor = UIColor(color)
#elseif canImport(AppKit)
label.textColor = NSColor(color)
#endif
}
let size = label.intrinsicContentSize
guard size.width > 0, size.height > 0, !latex.isEmpty else { return nil }
guard let image = rasterize(label, size: size) else { return nil }
// SwiftMath does not expose the layout descent publicly, so approximate
// it by centring the formula on the text line. Tune if inline math sits
// too high or low against surrounding text.
let descent = max(0, (size.height - fontSize) / 2)
return NorgMathImage(image: image, size: size, descent: descent)
}
@MainActor
private func rasterize(_ label: MTMathUILabel, size: CGSize) -> PlatformImage? {
label.frame = CGRect(origin: .zero, size: size)
#if canImport(UIKit)
label.layoutIfNeeded()
let format = UIGraphicsImageRendererFormat()
format.scale = UIScreen.main.scale
format.opaque = false
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { context in
let cg = context.cgContext
cg.translateBy(x: 0, y: size.height)
cg.scaleBy(x: 1, y: -1)
label.layer.render(in: cg)
}
#elseif canImport(AppKit)
label.layoutSubtreeIfNeeded()
guard let rep = label.bitmapImageRepForCachingDisplay(in: label.bounds) else { return nil }
label.cacheDisplay(in: label.bounds, to: rep)
let image = NSImage(size: size)
image.addRepresentation(rep)
return image
#endif
}
}
#endif
|