# LaTeXSwiftUI **Repository Path**: curryluya/LaTeXSwiftUI ## Basic Information - **Project Name**: LaTeXSwiftUI - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-17 - **Last Updated**: 2026-04-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # LaTeXSwiftUI โจ Beautifully rendered LaTeX equations in SwiftUI โ powered by MathJax. [](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [](https://github.com/colinc86/LaTeXSwiftUI/actions/workflows/swift.yml)

That's it! The `LaTeX` view's body is built from `Text` views, so standard SwiftUI modifiers work out of the box.
```swift
LaTeX("Hello, $\\LaTeX$!")
.fontDesign(.serif)
.foregroundColor(.blue)
```
>
## โจ๏ธ Usage
### ๐ค Fonts
The view measures the current font's x-height to correctly size rendered equations. It converts SwiftUI's `Font` to `UIFont`/`NSFont` internally, which currently works with SwiftUI's preferred fonts (`largeTitle`, `title`, `headline`, `caption`, etc.) and platform font types passed directly.
```swift
// SwiftUI preferred fonts
LaTeX("Hello, $\\LaTeX$!")
.font(.title)
// UIFont/NSFont passed directly
LaTeX("Hello, $\\LaTeX$!")
.font(UIFont.systemFont(ofSize: 30))
LaTeX("Hello, $\\LaTeX$!")
.font(UIFont(name: "Avenir", size: 25)!)
```
> โ ๏ธ Custom SwiftUI fonts (`.custom(name:size:)`, `.system(size:)`) and `UIFont`/`NSFont` wrapped in `Font()` will not size equations correctly. Use preferred fonts or pass platform font types directly.
### ๐ง Parsing & Input
#### Parsing Mode
The view can search for top-level equations delimited by the following terminators, or render the entire input as math.
| Terminators |
|-------------|
| `$...$` |
| `$$...$$` |
| `\(...\)` |
| `\[...\]` |
| `\begin{equation}...\end{equation}` |
| `\begin{equation*}...\end{equation*}` |
| `\begin{...}...\end{...}` |
##### Generic Environments
Any `\begin{name}...\end{name}` environment is automatically recognized as a block equation โ including `align`, `gather`, `cases`, `array`, `matrix`, `pmatrix`, and more. There's no need to wrap them in `$$...$$`.
```swift
LaTeX("The function is \\begin{cases} x & \\text{if } x \\geq 0 \\\\ -x & \\text{if } x < 0 \\end{cases}")
```
```swift
// Only parse equations (default)
LaTeX("Euler's identity is $e^{i\\pi}+1=0$.")
.parsingMode(.onlyEquations)
// Parse the entire input
LaTeX("\\text{Euler's identity is } e^{i\\pi}+1=0\\text{.}")
.parsingMode(.all)
```
>
#### Unencode HTML
Input may contain HTML entities such as `<` which LaTeX won't parse. Use the `unencoded` modifier to decode them.
```swift
LaTeX("$x^2<1$")
.errorMode(.error)
// Replace "<" with "<"
LaTeX("$x^2<1$")
.unencoded()
```
>
#### Block Rendering Mode
Block equations can be rendered centered on their own line (`blockViews`, the default), forced inline (`alwaysInline`), or as text with newlines (`blockText`). Block equations are placed in horizontal scroll views when they exceed the view width.
```swift
LaTeX("The quadratic formula is $$x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$$ and it has zeros at the roots of $f(x)=ax^2+bx+c$.")
.blockMode(.blockViews)
Divider()
LaTeX("The quadratic formula is $$x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$$ and it has zeros at the roots of $f(x)=ax^2+bx+c$.")
.blockMode(.alwaysInline)
Divider()
LaTeX("The quadratic formula is $$x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$$ and it has zeros at the roots of $f(x)=ax^2+bx+c$.")
.blockMode(.blockText)
```
>
#### Numbered Block Equations
The view supports simple numbering of block equations when using `blockViews` mode.
| Modifier | Description |
|:---------|:------------|
| `.equationNumberMode(_:)` | Position: `.left`, `.right`, or `.none` (default) |
| `.equationNumberStart(_:)` | Starting number (default: `1`) |
| `.equationNumberOffset(_:)` | Left or right offset in points |
| `.formatEquationNumber(_:)` | Custom formatting closure `(Int) -> String` |
```swift
LaTeX("$$E = mc^2$$")
.equationNumberMode(.right)
.equationNumberOffset(10)
.padding([.bottom])
LaTeX("$$E = mc^2$$ $$E = mc^2$$")
.equationNumberMode(.right)
.equationNumberOffset(10)
.equationNumberStart(2)
.formatEquationNumber { n in
return "~[\(n)]~"
}
```
>
#### Rendering Style
All rendering (MathJax conversion and SVG rasterization) is performed off the main thread. Choose a rendering style to control loading behavior.
| Style | Async | Description |
|:-----------|:------|:-------------------------------------------------------------------------|
| `empty` | Yes | The view remains empty until rendering completes. |
| `original` | Yes | The view displays the input text until rendering completes. |
| `redactedOriginal` | Yes | The view displays a redacted placeholder until rendering completes. ๐ |
| `progress` | Yes | The view displays a progress indicator until rendering completes. |
| `wait` | No | *(default)* The view blocks until rendering completes. |
When using an asynchronous style, use `renderingAnimation` to animate the transition.
```swift
LaTeX(input)
.renderingStyle(.original)
.renderingAnimation(.easeIn)
```
> **Note:** The `LaTeX` view automatically re-renders when its input string changes, so you can bind it to `@State` variables without needing `.id()`:
> ```swift
> @State var text: String = ""
>
> var body: some View {
> VStack {
> TextField("LaTeX", text: $text)
> LaTeX("$\(text)$")
> }
> }
> ```
#### Script Mode
๐ When displaying equations inline with non-Latin scripts such as Korean, Japanese, or Chinese, equations may appear undersized or misaligned. The `script` modifier adjusts equation scaling to match the surrounding text.
```swift
// Korean
LaTeX("๋ฐฉ์ ์ $x^2 + y^2 = z^2$ ์ ์ ์๋ ค์ ธ ์์ต๋๋ค.")
.script(.cjk)
// Japanese
LaTeX("ๆน็จๅผ $E = mc^2$ ใฏๆๅใงใใ")
.script(.cjk)
// Custom scale factor
LaTeX("Scaled equation: $\\int_0^1 x^2 dx$")
.script(.custom(1.3))
```
| Script | Description |
|:-------|:------------|
| `.latin` | *(default)* Uses the font's x-height. Suitable for Latin, Cyrillic, and similar scripts. |
| `.cjk` | Uses the font's cap-height. Suitable for Korean, Japanese, and Chinese. |
| `.custom(CGFloat)` | Multiplies the font's x-height by the given factor. |
#### Line Spacing
๐ On iOS 18+ / macOS 15+, the view automatically normalizes line spacing when inline equations cause uneven line gaps. This uses a custom `TextRenderer` and requires no configuration.
#### Arrays & Tables
๐ The view renders LaTeX array and table environments including `array`, `matrix`, `pmatrix`, `vmatrix`, and `cases`. Horizontal rules (`\hline`) and vertical column borders are supported.
```swift
LaTeX("$$\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}$$")
LaTeX("$$\\begin{cases} x & \\text{if } x \\geq 0 \\\\ -x & \\text{if } x < 0 \\end{cases}$$")
```
### โฟ Accessibility
๐ Rendered equations are images that need accessibility labels for VoiceOver. By default, LaTeXSwiftUI uses MathJax's Speech Rule Engine (SRE) to generate natural language descriptions automatically.
```swift
// Default (.sre) โ VoiceOver reads "x squared plus y squared equals z squared"
LaTeX("$x^2 + y^2 = z^2$")
// Use the raw TeX input as the label
LaTeX("$x^2 + y^2 = z^2$")
.imageAccessibility(.input)
// No accessibility label
LaTeX("$x^2 + y^2 = z^2$")
.imageAccessibility(.none)
// Custom label
LaTeX("$E = mc^2$")
.imageAccessibility(.custom("Einstein's mass-energy equivalence"))
```
| Mode | Description |
|:-----|:------------|
| `.sre` | *(default)* Uses the Speech Rule Engine to generate natural language. Falls back to raw TeX on failure. |
| `.input` | Uses the raw TeX input as the accessibility label. |
| `.none` | No accessibility label (default SwiftUI behavior). |
| `.custom(String)` | Uses a custom string as the accessibility label. |
### ๐ผ๏ธ Rendering to Images
๐ You can render LaTeX equations directly to `UIImage` (iOS/visionOS) or `NSImage` (macOS) without using the `LaTeX` SwiftUI view. This is useful for UIKit integration, image export, or custom rendering pipelines.
```swift
// Render all equations to images
let images = LaTeX.renderToImages("$x^2 + y^2 = z^2$")
// With custom options
let images = LaTeX.renderToImages(
"Euler's identity: $e^{i\\pi}+1=0$ and $\\int_0^1 x\\,dx$",
displayScale: 3.0,
processEscapes: true
)
// Each equation produces one image
for image in images {
imageView.image = image
}
```
### โก Performance & Caching
All rendering is performed off the main thread. The package caches both SVG data from MathJax and the rasterized images. You can control the caches directly.
```swift
// Clear the SVG data cache
LaTeX.dataCache.removeAllObjects()
// Clear the rendered image cache
LaTeX.imageCache.removeAllObjects()
```
#### Preloading
SVGs and images are rendered on demand, but you can preload them to minimize lag when the view appears. Call `preload` **last** in the modifier chain.
```swift
VStack {
ForEach(expressions, id: \.self) { expression in
LaTeX(expression)
.font(.caption2)
.foregroundColor(.green)
.unencoded()
.errorMode(.error)
.processEscapes()
.preload()
}
}
```