220 lines
6.0 KiB
Swift
220 lines
6.0 KiB
Swift
import CoreGraphics
|
|
import CoreText
|
|
import Foundation
|
|
|
|
extension CGContext {
|
|
func draw(_ displayNode: Math.DisplayNode, foregroundColor: CGColor) {
|
|
let foregroundColor =
|
|
displayNode.localTextColor?.cgColor
|
|
?? displayNode.textColor?.cgColor
|
|
?? foregroundColor
|
|
|
|
switch displayNode {
|
|
case let list as Math.DisplayList:
|
|
draw(list, foregroundColor: foregroundColor)
|
|
case let textRun as Math.DisplayTextRun:
|
|
draw(textRun, foregroundColor: foregroundColor)
|
|
case let glyph as Math.DisplayGlyph:
|
|
draw(glyph, foregroundColor: foregroundColor)
|
|
case let glyphRun as Math.DisplayGlyphRun:
|
|
draw(glyphRun, foregroundColor: foregroundColor)
|
|
case let fraction as Math.DisplayFraction:
|
|
draw(fraction, foregroundColor: foregroundColor)
|
|
case let radical as Math.DisplayRadical:
|
|
draw(radical, foregroundColor: foregroundColor)
|
|
case let line as Math.DisplayLine:
|
|
draw(line, foregroundColor: foregroundColor)
|
|
case let largeOperator as Math.DisplayLargeOperator:
|
|
draw(largeOperator, foregroundColor: foregroundColor)
|
|
case let accent as Math.DisplayAccent:
|
|
draw(accent, foregroundColor: foregroundColor)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CGContext {
|
|
private func draw(_ list: Math.DisplayList, foregroundColor: CGColor) {
|
|
saveGState()
|
|
|
|
translateBy(x: list.position.x, y: list.position.y)
|
|
textPosition = .zero
|
|
|
|
for child in list.children {
|
|
draw(child, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ textRun: Math.DisplayTextRun, foregroundColor: CGColor) {
|
|
let line = CTLineCreateWithAttributedString(textRun.attributedString)
|
|
|
|
saveGState()
|
|
setFillColor(foregroundColor)
|
|
textPosition = textRun.position
|
|
CTLineDraw(line, self)
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ glyph: Math.DisplayGlyph, foregroundColor: CGColor) {
|
|
guard let platformFont = Math.PlatformFont(font: glyph.font) else {
|
|
return
|
|
}
|
|
|
|
saveGState()
|
|
|
|
translateBy(x: glyph.position.x, y: glyph.position.y - glyph.shiftDown)
|
|
textPosition = .zero
|
|
|
|
setFillColor(foregroundColor)
|
|
|
|
var cgGlyph = CGGlyph(glyph.glyph)
|
|
var pos = CGPoint.zero
|
|
CTFontDrawGlyphs(platformFont.ctFont, &cgGlyph, &pos, 1, self)
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ glyphRun: Math.DisplayGlyphRun, foregroundColor: CGColor) {
|
|
guard let platformFont = Math.PlatformFont(font: glyphRun.font) else {
|
|
return
|
|
}
|
|
|
|
saveGState()
|
|
|
|
translateBy(x: glyphRun.position.x, y: glyphRun.position.y - glyphRun.shiftDown)
|
|
textPosition = .zero
|
|
|
|
setFillColor(foregroundColor)
|
|
|
|
var glyphs = glyphRun.glyphs.map { CGGlyph($0) }
|
|
var positions = glyphRun.offsets.map { CGPoint(x: 0, y: $0) }
|
|
CTFontDrawGlyphs(platformFont.ctFont, &glyphs, &positions, glyphs.count, self)
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ fraction: Math.DisplayFraction, foregroundColor: CGColor) {
|
|
if let numerator = fraction.numerator {
|
|
draw(numerator, foregroundColor: foregroundColor)
|
|
}
|
|
if let denominator = fraction.denominator {
|
|
draw(denominator, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
guard fraction.lineThickness > 0 else {
|
|
return
|
|
}
|
|
|
|
saveGState()
|
|
|
|
setStrokeColor(foregroundColor)
|
|
setLineWidth(fraction.lineThickness)
|
|
|
|
let lineStart = CGPoint(
|
|
x: fraction.position.x,
|
|
y: fraction.position.y + fraction.linePosition
|
|
)
|
|
let lineEnd = CGPoint(
|
|
x: fraction.position.x + fraction.width,
|
|
y: lineStart.y
|
|
)
|
|
|
|
move(to: lineStart)
|
|
addLine(to: lineEnd)
|
|
strokePath()
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ radical: Math.DisplayRadical, foregroundColor: CGColor) {
|
|
if let radicand = radical.radicand {
|
|
draw(radicand, foregroundColor: foregroundColor)
|
|
}
|
|
if let degree = radical.degree {
|
|
draw(degree, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
saveGState()
|
|
|
|
setStrokeColor(foregroundColor)
|
|
setFillColor(foregroundColor)
|
|
|
|
translateBy(x: radical.position.x + radical.radicalShift, y: radical.position.y)
|
|
textPosition = .zero
|
|
|
|
if let radicalGlyph = radical.radicalGlyph {
|
|
draw(radicalGlyph, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
let heightFromTop = radical.topKern
|
|
let glyphWidth = radical.radicalGlyph?.width ?? 0
|
|
let radicandWidth = radical.radicand?.width ?? 0
|
|
let lineStart = CGPoint(
|
|
x: glyphWidth,
|
|
y: radical.ascent - heightFromTop - radical.lineThickness / 2
|
|
)
|
|
let lineEnd = CGPoint(x: lineStart.x + radicandWidth, y: lineStart.y)
|
|
|
|
setLineWidth(radical.lineThickness)
|
|
setLineCap(.round)
|
|
move(to: lineStart)
|
|
addLine(to: lineEnd)
|
|
strokePath()
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ line: Math.DisplayLine, foregroundColor: CGColor) {
|
|
if let inner = line.inner {
|
|
draw(inner, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
saveGState()
|
|
|
|
setStrokeColor(foregroundColor)
|
|
setLineWidth(line.lineThickness)
|
|
|
|
let lineStart = CGPoint(x: line.position.x, y: line.position.y + line.lineShiftUp)
|
|
let lineEnd = CGPoint(x: lineStart.x + (line.inner?.width ?? 0), y: lineStart.y)
|
|
|
|
move(to: lineStart)
|
|
addLine(to: lineEnd)
|
|
strokePath()
|
|
|
|
restoreGState()
|
|
}
|
|
|
|
private func draw(_ largeOperator: Math.DisplayLargeOperator, foregroundColor: CGColor) {
|
|
if let upperLimit = largeOperator.upperLimit {
|
|
draw(upperLimit, foregroundColor: foregroundColor)
|
|
}
|
|
if let lowerLimit = largeOperator.lowerLimit {
|
|
draw(lowerLimit, foregroundColor: foregroundColor)
|
|
}
|
|
if let nucleus = largeOperator.nucleus {
|
|
draw(nucleus, foregroundColor: foregroundColor)
|
|
}
|
|
}
|
|
|
|
private func draw(_ accent: Math.DisplayAccent, foregroundColor: CGColor) {
|
|
if let accentee = accent.accentee {
|
|
draw(accentee, foregroundColor: foregroundColor)
|
|
}
|
|
|
|
guard let accentGlyph = accent.accent else {
|
|
return
|
|
}
|
|
|
|
saveGState()
|
|
|
|
translateBy(x: accent.position.x, y: accent.position.y)
|
|
textPosition = .zero
|
|
draw(accentGlyph, foregroundColor: foregroundColor)
|
|
|
|
restoreGState()
|
|
}
|
|
}
|