Skip to content

Commit 9a760ec

Browse files
authored
Merge pull request #65 from colinc86/develop
Develop
2 parents f75b32d + eb09a3d commit 9a760ec

File tree

10 files changed

+160
-31
lines changed

10 files changed

+160
-31
lines changed

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,40 @@ struct MyView: View {
7373
7474
### Fonts
7575

76-
The view needs to be able to measure the current font's x-height to correctly size the characters inside of the rendered LaTeX SVG. To do that, the view must use the `UIFont`/`NSFont` classes and do its best to convert SwiftUI's `Font` structure into the correct `UIFont` instance. Currently, the view's functionality is limited to SwiftUI's static largeTitle, title, title1, headline, etc fonts.
76+
The view needs to be able to measure the current font's x-height to correctly size the characters inside of the rendered LaTeX SVG. To do that, the view must use the `UIFont`/`NSFont` classes and do its best to convert SwiftUI's `Font` structure into the correct `UIFont` instance. Currently, the view's functionality is limited to SwiftUI's static largeTitle, title, title1, headline, etc fonts, or by using `UIFont`/`NSFont` types directly.
77+
78+
The `font` modifier has been overloaded so that you can give the `LaTeX` view `UIFont` and `NSFont` types directly. The following examples will render the size of the LaTeX correctly.
79+
80+
```swift
81+
// SwiftUI perferred fonts
82+
LaTeX("Hello, $\\LaTeX$!")
83+
.font(.title)
84+
85+
LaTeX("Hello, $\\LaTeX$!")
86+
.font(.caption)
87+
88+
// Any UIFont/NSFont - note that they should be passed directly to the view
89+
LaTeX("Hello, $\\LaTeX$!")
90+
.font(UIFont.systemFont(ofSize: 30))
91+
92+
LaTeX("Hello, $\\LaTeX$!")
93+
.font(UIFont(name: "Avenir", size: 25)!)
94+
```
95+
96+
The following examples will _not_ render the size of the LaTeX correctly.
97+
98+
```swift
99+
// SwiftUI Font type, but not a preferred font
100+
LaTeX("Hello, $\\LaTeX$!")
101+
.font(.custom(name: "Avenir", size: 25))
102+
103+
LaTeX("Hello, $\\LaTeX$!")
104+
.font(.system(size: 25))
105+
106+
// Wrapping UIFont/NSFont in a SwiftUI Font type
107+
LaTeX("Hello, $\\LaTeX$!")
108+
.font(Font(UIFont.systemFont(ofSize: 30)))
109+
```
77110

78111
### Modifiers
79112

Sources/LaTeXSwiftUI/Extensions/EnvironmentValues+Extensions.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ private struct RenderingAnimationKey: EnvironmentKey {
7474
static let defaultValue: Animation? = .none
7575
}
7676

77-
private struct IgnoreSringFormatting: EnvironmentKey {
77+
private struct IgnoreSringFormattingKey: EnvironmentKey {
7878
static let defaultValue: Bool = false
7979
}
8080

81+
private struct PlatformFontKey: EnvironmentKey {
82+
static let defaultValue: _Font? = nil
83+
}
84+
8185
extension EnvironmentValues {
8286

8387
/// The image rendering mode of this environment.
@@ -154,8 +158,14 @@ extension EnvironmentValues {
154158

155159
/// Whether string formatting such as markdown should be ignored or rendered.
156160
var ignoreStringFormatting: Bool {
157-
get { self[IgnoreSringFormatting.self] }
158-
set { self[IgnoreSringFormatting.self] = newValue }
161+
get { self[IgnoreSringFormattingKey.self] }
162+
set { self[IgnoreSringFormattingKey.self] = newValue }
163+
}
164+
165+
/// The specified UI/NSFont font to use, if any.
166+
var platformFont: _Font? {
167+
get { self[PlatformFontKey.self] }
168+
set { self[PlatformFontKey.self] = newValue }
159169
}
160170

161171
}

Sources/LaTeXSwiftUI/Extensions/View+Extensions.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,11 @@ public extension View {
170170
environment(\.ignoreStringFormatting, ignore)
171171
}
172172

173+
/// Sets the view's UI/NSFont font, if any.
174+
/// - Parameter font: The UI/NSFont font to use.
175+
/// - Returns: A view that uses the provided font.
176+
internal func platformFont(_ font: _Font? = nil) -> some View {
177+
environment(\.platformFont, font)
178+
}
179+
173180
}

Sources/LaTeXSwiftUI/LaTeX.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ public struct LaTeX: View {
166166
/// The view's font.
167167
@Environment(\.font) private var font
168168

169+
/// The view's UI/NSFont font.
170+
@Environment(\.platformFont) private var platformFont
171+
169172
// MARK: Private properties
170173

171174
/// The view's renderer.
@@ -235,6 +238,20 @@ extension LaTeX {
235238
style.makeBody(content: self)
236239
}
237240

241+
#if os(iOS) || os(visionOS)
242+
public func font(_ font: UIFont) -> some View {
243+
self
244+
.platformFont(font)
245+
.font(Font(font))
246+
}
247+
#else
248+
public func font(_ font: NSFont) -> some View {
249+
self
250+
.platformFont(font)
251+
.font(Font(font))
252+
}
253+
#endif
254+
238255
}
239256

240257
// MARK: Private methods
@@ -255,7 +272,7 @@ extension LaTeX {
255272
parsingMode: parsingMode,
256273
processEscapes: processEscapes,
257274
errorMode: errorMode,
258-
font: font ?? .body,
275+
xHeight: (platformFont?.xHeight ?? font?.xHeight) ?? Font.body.xHeight,
259276
displayScale: displayScale)
260277
}
261278

@@ -267,7 +284,7 @@ extension LaTeX {
267284
parsingMode: parsingMode,
268285
processEscapes: processEscapes,
269286
errorMode: errorMode,
270-
font: font ?? .body,
287+
xHeight: (platformFont?.xHeight ?? font?.xHeight) ?? Font.body.xHeight,
271288
displayScale: displayScale,
272289
renderingMode: imageRenderingMode)
273290
}
@@ -282,7 +299,7 @@ extension LaTeX {
282299
parsingMode: parsingMode,
283300
processEscapes: processEscapes,
284301
errorMode: errorMode,
285-
font: font ?? .body,
302+
xHeight: (platformFont?.xHeight ?? font?.xHeight) ?? Font.body.xHeight,
286303
displayScale: displayScale,
287304
renderingMode: imageRenderingMode)
288305
}

Sources/LaTeXSwiftUI/Models/Component.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ extension Component {
192192
/// Converts the component to a `Text` view.
193193
///
194194
/// - Parameters:
195-
/// - font: The font to use.
195+
/// - xHeight: The font's x-height.
196196
/// - displayScale: The view's display scale.
197197
/// - renderingMode: The image rendering mode.
198198
/// - errorMode: The error handling mode.
@@ -202,7 +202,7 @@ extension Component {
202202
/// should be ignored or rendered.
203203
/// - Returns: A text view.
204204
func convertToText(
205-
font: Font,
205+
xHeight: CGFloat,
206206
displayScale: CGFloat,
207207
renderingMode: SwiftUI.Image.TemplateRenderingMode,
208208
errorMode: LaTeX.ErrorMode,
@@ -227,7 +227,6 @@ extension Component {
227227
}
228228
}
229229
else if let imageContainer {
230-
let xHeight = _Font.preferredFont(from: font).xHeight
231230
let offset = svg.geometry.verticalAlignment.toPoints(xHeight)
232231
text = Text(imageContainer.image).baselineOffset(blockRenderingMode == .alwaysInline || !isInEquationBlock ? offset : 0)
233232
}

Sources/LaTeXSwiftUI/Models/ComponentBlock.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extension ComponentBlock {
6060
/// Converts a component block to a `Text` view.
6161
///
6262
/// - Parameters:
63-
/// - font: The font to use.
63+
/// - xHeight: The font's x-height.
6464
/// - displayScale: The display scale.
6565
/// - renderingMode: The rendering mode.
6666
/// - errorMode: The error mode.
@@ -69,7 +69,7 @@ extension ComponentBlock {
6969
/// should be ignored or rendered.
7070
/// - Returns: A `Text` view.
7171
@MainActor func toText(
72-
font: Font?,
72+
xHeight: CGFloat,
7373
displayScale: CGFloat,
7474
renderingMode: Image.TemplateRenderingMode,
7575
errorMode: LaTeX.ErrorMode,
@@ -78,7 +78,7 @@ extension ComponentBlock {
7878
) -> Text {
7979
components.enumerated().map { i, component in
8080
return component.convertToText(
81-
font: font ?? .body,
81+
xHeight: xHeight,
8282
displayScale: displayScale,
8383
renderingMode: renderingMode,
8484
errorMode: errorMode,

Sources/LaTeXSwiftUI/Models/Renderer.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,21 @@ extension Renderer {
105105
/// - parsingMode: The `parsingMode` environment variable.
106106
/// - processEscapes: The `processEscapes` environment variable.
107107
/// - errorMode: The `errorMode` environment variable.
108-
/// - font: The `font environment` variable.
108+
/// - xHeight: The font's x-height.
109109
/// - displayScale: The `displayScale` environment variable.
110110
func isCached(
111111
latex: String,
112112
unencodeHTML: Bool,
113113
parsingMode: LaTeX.ParsingMode,
114114
processEscapes: Bool,
115115
errorMode: LaTeX.ErrorMode,
116-
font: Font,
116+
xHeight: CGFloat,
117117
displayScale: CGFloat
118118
) -> Bool {
119119
let texOptions = TeXInputProcessorOptions(processEscapes: processEscapes, errorMode: errorMode)
120120
return blocksExistInCache(
121121
parseBlocks(latex: latex, unencodeHTML: unencodeHTML, parsingMode: parsingMode),
122-
font: font,
122+
xHeight: xHeight,
123123
displayScale: displayScale,
124124
texOptions: texOptions)
125125
}
@@ -132,7 +132,7 @@ extension Renderer {
132132
/// - parsingMode: The `parsingMode` environment variable.
133133
/// - processEscapes: The `processEscapes` environment variable.
134134
/// - errorMode: The `errorMode` environment variable.
135-
/// - font: The `font` environment variable.
135+
/// - xHeight: The font's x-height.
136136
/// - displayScale: The `displayScale` environment variable.
137137
/// - renderingMode: The `renderingMode` environment variable.
138138
@MainActor func renderSync(
@@ -141,7 +141,7 @@ extension Renderer {
141141
parsingMode: LaTeX.ParsingMode,
142142
processEscapes: Bool,
143143
errorMode: LaTeX.ErrorMode,
144-
font: Font,
144+
xHeight: CGFloat,
145145
displayScale: CGFloat,
146146
renderingMode: SwiftUI.Image.TemplateRenderingMode
147147
) -> [ComponentBlock] {
@@ -156,7 +156,7 @@ extension Renderer {
156156
let texOptions = TeXInputProcessorOptions(processEscapes: processEscapes, errorMode: errorMode)
157157
blocks = render(
158158
blocks: parseBlocks(latex: latex, unencodeHTML: unencodeHTML, parsingMode: parsingMode),
159-
font: font,
159+
xHeight: xHeight,
160160
displayScale: displayScale,
161161
renderingMode: renderingMode,
162162
texOptions: texOptions)
@@ -174,7 +174,7 @@ extension Renderer {
174174
/// - parsingMode: The `parsingMode` environment variable.
175175
/// - processEscapes: The `processEscapes` environment variable.
176176
/// - errorMode: The `errorMode` environment variable.
177-
/// - font: The `font` environment variable.
177+
/// - xHeight: The font's x-height.
178178
/// - displayScale: The `displayScale` environment variable.
179179
/// - renderingMode: The `renderingMode` environment variable.
180180
func render(
@@ -183,7 +183,7 @@ extension Renderer {
183183
parsingMode: LaTeX.ParsingMode,
184184
processEscapes: Bool,
185185
errorMode: LaTeX.ErrorMode,
186-
font: Font,
186+
xHeight: CGFloat,
187187
displayScale: CGFloat,
188188
renderingMode: SwiftUI.Image.TemplateRenderingMode
189189
) async {
@@ -200,7 +200,7 @@ extension Renderer {
200200
let texOptions = TeXInputProcessorOptions(processEscapes: processEscapes, errorMode: errorMode)
201201
let renderedBlocks = render(
202202
blocks: parseBlocks(latex: latex, unencodeHTML: unencodeHTML, parsingMode: parsingMode),
203-
font: font,
203+
xHeight: xHeight,
204204
displayScale: displayScale,
205205
renderingMode: renderingMode,
206206
texOptions: texOptions)
@@ -249,14 +249,14 @@ extension Renderer {
249249
///
250250
/// - Parameters:
251251
/// - blocks: The component blocks.
252-
/// - font: The view's font.
252+
/// - xHeight: The font's x-height.
253253
/// - displayScale: The display scale to render at.
254254
/// - renderingMode: The image rendering mode.
255255
/// - texOptions: The MathJax Tex input processor options.
256256
/// - Returns: An array of rendered blocks.
257257
func render(
258258
blocks: [ComponentBlock],
259-
font: Font,
259+
xHeight: CGFloat,
260260
displayScale: CGFloat,
261261
renderingMode: SwiftUI.Image.TemplateRenderingMode,
262262
texOptions: TeXInputProcessorOptions
@@ -266,7 +266,7 @@ extension Renderer {
266266
do {
267267
let newComponents = try render(
268268
block.components,
269-
xHeight: font.xHeight,
269+
xHeight: xHeight,
270270
displayScale: displayScale,
271271
renderingMode: renderingMode,
272272
texOptions: texOptions)
@@ -288,7 +288,7 @@ extension Renderer {
288288
///
289289
/// - Parameters:
290290
/// - components: The components to render.
291-
/// - xHeight: The xHeight of the font to use.
291+
/// - xHeight: The font's x-height.
292292
/// - displayScale: The current display scale.
293293
/// - renderingMode: The image rendering mode.
294294
/// - texOptions: The MathJax TeX input processor options.
@@ -454,11 +454,11 @@ extension Renderer {
454454
///
455455
/// - Parameters:
456456
/// - blocks: The blocks.
457-
/// - font: The `font` environment variable.
457+
/// - xHeight: The font's x-height.
458458
/// - displayScale: The `displayScale` environment variable.
459459
/// - texOptions: The `texOptions` environment variable.
460460
/// - Returns: Whether the blocks are in the renderer's cache.
461-
func blocksExistInCache(_ blocks: [ComponentBlock], font: Font, displayScale: CGFloat, texOptions: TeXInputProcessorOptions) -> Bool {
461+
func blocksExistInCache(_ blocks: [ComponentBlock], xHeight: CGFloat, displayScale: CGFloat, texOptions: TeXInputProcessorOptions) -> Bool {
462462
for block in blocks {
463463
for component in block.components where component.type.isEquation {
464464
let dataCacheKey = Cache.SVGCacheKey(
@@ -473,7 +473,6 @@ extension Renderer {
473473
return false
474474
}
475475

476-
let xHeight = _Font.preferredFont(from: font).xHeight
477476
let imageCacheKey = Cache.ImageCacheKey(svg: svg, xHeight: xHeight)
478477
guard Cache.shared.imageCacheValue(for: imageCacheKey) != nil else {
479478
return false
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// LaTeX_Previews+Fonts.swift
3+
// LaTeXSwiftUI
4+
//
5+
// Copyright (c) 2025 Colin Campbell
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23+
// IN THE SOFTWARE.
24+
//
25+
26+
import SwiftUI
27+
28+
#if os(iOS)
29+
import UIKit
30+
typealias PlatformFont = UIFont
31+
#else
32+
import Cocoa
33+
typealias PlatformFont = NSFont
34+
#endif
35+
36+
struct LaTeX_Previews_Fonts: PreviewProvider {
37+
38+
static var previews: some View {
39+
VStack {
40+
LaTeX("Hello, $\\LaTeX$!")
41+
.font(.system(size: 25))
42+
43+
LaTeX("Hello, $\\LaTeX$!")
44+
.font(PlatformFont.preferredFont(forTextStyle: .title1))
45+
46+
LaTeX("Hello, $\\LaTeX$!")
47+
.font(PlatformFont.systemFont(ofSize: 36))
48+
49+
LaTeX("Hello, $\\LaTeX$!")
50+
.font(PlatformFont.boldSystemFont(ofSize: 25))
51+
52+
LaTeX("Hello, $\\LaTeX$!")
53+
.font(PlatformFont(name: "Avenir", size: 25)!)
54+
}
55+
.previewDisplayName("Fonts")
56+
}
57+
58+
}

0 commit comments

Comments
 (0)