Skip to content

Instantly share code, notes, and snippets.

@amake
Last active June 30, 2020 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amake/fea416e4ece4faeeedbbdc5bc50808ed to your computer and use it in GitHub Desktop.
Save amake/fea416e4ece4faeeedbbdc5bc50808ed to your computer and use it in GitHub Desktop.
Generate math PNGs on iOS with KaTeX (Xcode Playground)
import UIKit
import WebKit
import PlaygroundSupport
var html = """
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js" integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
<style type="text/css">
body { background: transparent; margin: 0; font-size: 300%; }
.katex-display, .katex, .katex-html { display: inline !important; }
#math { float: left; }
</style>
</head>
<body>
<span id="math"></span>
</body>
<script>
function getMathElement() {
return document.getElementById('math');
}
function render(math) {
try {
katex.render(math, getMathElement());
return true;
} catch {
return false;
}
}
function bounds() {
return getMathElement().getBoundingClientRect().toJSON();
}
</script>
</html>
"""
class NavDelegate : NSObject, WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
renderMath(webView, math: "c = \\pm\\sqrt{a^2 + b^2}")
}
}
func renderMath(_ webView: WKWebView, math: String) {
let escaped = math.replacingOccurrences(of: "\\", with: "\\\\")
web.evaluateJavaScript("render('\(escaped)')") { result, error in
guard let result = result as? Bool else {
dump(error)
return
}
if result {
takeSnapshot(webView)
} else {
print("KaTeX error")
}
}
}
func takeSnapshot(_ webView: WKWebView) {
webView.evaluateJavaScript("bounds()") { bbox, error in
guard let bbox = bbox as? [String:Any] else {
dump(error)
return
}
let x = (bbox["left"] as! NSNumber).intValue
let y = (bbox["top"] as! NSNumber).intValue
let w = (bbox["width"] as! NSNumber).intValue
let h = (bbox["height"] as! NSNumber).intValue
let rect = CGRect(x: x, y: y, width: w, height: h)
print("Crop rect: \(rect)")
let snapConfig = WKSnapshotConfiguration()
snapConfig.rect = rect
snapConfig.afterScreenUpdates = true
webView.takeSnapshot(with: snapConfig) { image, error in
guard let image = image else {
dump(error)
return
}
saveImage(image)
}
}
}
func saveImage(_ image: UIImage) {
guard let data = image.pngData() else {
print("Couldn't get data")
return
}
let url = URL(fileURLWithPath: "math.png")
print("Saving to \(url.absoluteString)")
try! data.write(to: url)
}
let frame = CGRect(x: 0, y: 0, width: 600, height: 800)
let web = WKWebView(frame: frame)
web.isOpaque = false
let delegate = NavDelegate()
web.navigationDelegate = delegate
web.loadHTMLString(html, baseURL: nil)
PlaygroundPage.current.liveView = web
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment