# 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://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [![Unit Tests](https://github.com/colinc86/LaTeXSwiftUI/actions/workflows/swift.yml/badge.svg)](https://github.com/colinc86/LaTeXSwiftUI/actions/workflows/swift.yml)
## ๐Ÿ†• What's New in v2 - โ™ฟ **[Accessibility](#-accessibility)** โ€” VoiceOver support via the Speech Rule Engine (SRE) - ๐ŸŒ **[Script Scaling](#script-mode)** โ€” CJK and non-Latin script support with `.script()` - ๐Ÿ“ **[Line Spacing](#line-spacing)** โ€” Automatic normalization on iOS 18+ / macOS 15+ - ๐Ÿ“Š **[Arrays & Tables](#arrays--tables)** โ€” `array`, `matrix`, `cases`, and more with borders and rules - ๐Ÿ”ฒ **[Redacted Placeholder](#rendering-style)** โ€” New `redactedOriginal` async rendering style - ๐Ÿ“ **Dynamic Type** โ€” Equations scale with system text size settings - ๐Ÿ–ผ๏ธ **[Render to Image](#-rendering-to-images)** โ€” Render LaTeX to `UIImage`/`NSImage` without a SwiftUI view - ๐Ÿงฉ **[Generic Environments](#generic-environments)** โ€” `\begin{align}`, `\begin{cases}`, etc. work standalone ## ๐Ÿ“– Contents - [โ„น๏ธ About](#-about) - [๐Ÿ“ฆ Installation](#-installation) - [๐Ÿš€ Quick Start](#-quick-start) - [โŒจ๏ธ Usage](#%EF%B8%8F-usage) - [๐Ÿ”ค Fonts](#-fonts) - [๐Ÿ”ง Parsing & Input](#-parsing--input) - [๐ŸŽจ Visual & Layout](#-visual--layout) - [โ™ฟ Accessibility](#-accessibility) - [๐Ÿ–ผ๏ธ Rendering to Images](#-rendering-to-images) - [โšก Performance & Caching](#-performance--caching) ## โ„น๏ธ About `LaTeXSwiftUI` is a package that exposes a `LaTeX` view capable of parsing and rendering TeX and LaTeX equations containing math-mode macros. It uses the [MathJaxSwift](https://www.github.com/colinc86/MathJaxSwift) package to render equations with [MathJax](https://www.mathjax.org), so the view's capabilities are influenced by MathJax's [supported features](https://docs.mathjax.org/en/latest/input/tex/extensions.html). The view renders math-mode equations (inline and block), `\text{}` within equations, numbered block equations, any `\begin{...}...\end{...}` environment (including `align`, `gather`, `cases`, `array`, and more), and provides VoiceOver accessibility via the Speech Rule Engine. It scales equations for non-Latin scripts and normalizes line spacing on iOS 18+ / macOS 15+. You can also render equations directly to `UIImage`/`NSImage` without a SwiftUI view. Equations automatically scale with Dynamic Type accessibility settings. It does **not** render full LaTeX documents or text-mode macros. Requires Swift 6.0. Supports iOS 15+, macOS 12+, and visionOS 1+. All rendering is performed off the main thread. ## ๐Ÿ“ฆ Installation Add the dependency to your package manifest file. ```swift .package(url: "https://github.com/colinc86/LaTeXSwiftUI", from: "2.0.0") ``` ## ๐Ÿš€ Quick Start ```swift import LaTeXSwiftUI struct MyView: View { var body: some View { LaTeX("Hello, $\\LaTeX$!") } } ``` > 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() ``` > #### String Formatting The view renders the following markdown syntax by default. | Syntax | Description | |-------------|-------------| | `*...*` | Italic | | `**...**` | Bold | | `***...***` | Bold & Italic | | `~~...~~` | Strikethrough | | `` `...` `` | Monospaced | | `[...](...)` | Links | The reserved LaTeX characters `&`, `%`, `$`, `#`, `_`, `{`, `}`, `~`, `^`, and `\` are also unescaped when preceded by a backslash. Use `ignoreStringFormatting()` to disable both markdown rendering and escape replacement. ```swift LaTeX(input) .ignoreStringFormatting() ``` Use `processEscapes()` to allow `\$` for literal dollar signs and `\\` for literal backslashes within your input. ### ๐ŸŽจ Visual & Layout #### Image Rendering Mode Equations can match the surrounding text style or display the original MathJax-rendered colors. ```swift // Match surrounding text (default) LaTeX("Hello, $\\color{red}\\LaTeX$!") .imageRenderingMode(.template) // Display original rendered colors LaTeX("Hello, ${\\color{red} \\LaTeX}$!") .imageRenderingMode(.original) ``` > #### Error Mode Control how the view handles rendering errors. > When `rendered` mode is used, MathJax loads the `noerrors` and `noundefined` packages. In the other modes, errors are either displayed or replaced with the original text. ```swift LaTeX("$\\asdf$") .errorMode(.original) // Show original text LaTeX("$\\asdf$") .errorMode(.error) // Show error message LaTeX("$\\asdf$") .errorMode(.rendered) // Show rendered image if available ``` > #### 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() } } ```