#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