Skip to content

Commit b33507b

Browse files
committed
Refactor renderer.
1 parent 5c8747a commit b33507b

File tree

9 files changed

+383
-415
lines changed

9 files changed

+383
-415
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// MathJax+Extensions.swift
3+
// LaTeXSwiftUI
4+
//
5+
// Copyright (c) 2023 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 Foundation
27+
import MathJaxSwift
28+
29+
internal extension MathJax {
30+
31+
static var svgRenderer: MathJax? = {
32+
do {
33+
return try MathJax(preferredOutputFormat: .svg)
34+
}
35+
catch {
36+
NSLog("Error creating MathJax instance: \(error)")
37+
return nil
38+
}
39+
}()
40+
41+
}

Sources/LaTeXSwiftUI/LaTeX.swift

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,18 @@ public struct LaTeX: View {
104104

105105
/// The package's shared data cache.
106106
public static var dataCache: NSCache<NSString, NSData> {
107-
Renderer.shared.cache.dataCache
107+
Cache.shared.dataCache
108108
}
109109

110110
#if os(macOS)
111111
/// The package's shared image cache.
112112
public static var imageCache: NSCache<NSString, NSImage> {
113-
Renderer.shared.cache.imageCache
113+
Cache.shared.imageCache
114114
}
115115
#else
116116
/// The package's shared image cache.
117117
public static var imageCache: NSCache<NSString, UIImage> {
118-
Renderer.shared.cache.imageCache
118+
Cache.shared.imageCache
119119
}
120120
#endif
121121

@@ -156,8 +156,8 @@ public struct LaTeX: View {
156156

157157
// MARK: Private properties
158158

159-
/// The view's render state.
160-
@StateObject private var renderState: LaTeXRenderState
159+
/// The view's renderer.
160+
@StateObject private var renderer: Renderer
161161

162162
// MARK: Initializers
163163

@@ -166,16 +166,16 @@ public struct LaTeX: View {
166166
/// - Parameter latex: The LaTeX input.
167167
public init(_ latex: String) {
168168
self.latex = latex
169-
_renderState = StateObject(wrappedValue: LaTeXRenderState(latex: latex))
169+
_renderer = StateObject(wrappedValue: Renderer(latex: latex))
170170
}
171171

172172
// MARK: View body
173173

174174
public var body: some View {
175175
VStack(spacing: 0) {
176-
if renderState.rendered {
176+
if renderer.rendered {
177177
// If our blocks have been rendered, display them
178-
bodyWithBlocks(renderState.blocks)
178+
bodyWithBlocks(renderer.blocks)
179179
}
180180
else if isCached() {
181181
// If our blocks are cached, display them
@@ -193,7 +193,8 @@ public struct LaTeX: View {
193193
}
194194
}
195195
}
196-
.animation(renderingAnimation, value: renderState.rendered)
196+
.animation(renderingAnimation, value: renderer.rendered)
197+
.environmentObject(renderer)
197198
}
198199

199200
}
@@ -222,7 +223,7 @@ extension LaTeX {
222223
/// - Returns: A boolean indicating whether the components to the view are
223224
/// cached.
224225
private func isCached() -> Bool {
225-
renderState.isCached(
226+
renderer.isCached(
226227
unencodeHTML: unencodeHTML,
227228
parsingMode: parsingMode,
228229
processEscapes: processEscapes,
@@ -233,7 +234,7 @@ extension LaTeX {
233234

234235
/// Renders the view's components.
235236
@Sendable private func renderAsync() async {
236-
await renderState.render(
237+
await renderer.render(
237238
unencodeHTML: unencodeHTML,
238239
parsingMode: parsingMode,
239240
processEscapes: processEscapes,
@@ -246,7 +247,7 @@ extension LaTeX {
246247
///
247248
/// - Returns: The rendered components.
248249
private func renderSync() -> [ComponentBlock] {
249-
renderState.renderSync(
250+
renderer.renderSync(
250251
unencodeHTML: unencodeHTML,
251252
parsingMode: parsingMode,
252253
processEscapes: processEscapes,
@@ -274,13 +275,10 @@ extension LaTeX {
274275
switch renderingStyle {
275276
case .empty:
276277
Text("")
277-
.task(renderAsync)
278278
case .original:
279279
Text(latex)
280-
.task(renderAsync)
281280
case .progress:
282281
ProgressView()
283-
.task(renderAsync)
284282
default:
285283
EmptyView()
286284
}

Sources/LaTeXSwiftUI/Models/Cache.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,24 @@ internal class Cache {
7676

7777
// MARK: Static properties
7878

79-
/// The renderer's data cache.
80-
internal let dataCache: NSCache<NSString, NSData> = NSCache()
79+
/// The shared cache.
80+
static let shared = Cache()
8181

82-
/// Semaphore for thread-safe access to `dataCache`.
83-
internal let dataCacheSemaphore = DispatchSemaphore(value: 1)
82+
// MARK: Public properties
83+
84+
/// The renderer's data cache.
85+
let dataCache: NSCache<NSString, NSData> = NSCache()
8486

8587
/// The renderer's image cache.
86-
internal let imageCache: NSCache<NSString, _Image> = NSCache()
88+
let imageCache: NSCache<NSString, _Image> = NSCache()
89+
90+
// MARK: Private properties
91+
92+
/// Semaphore for thread-safe access to `dataCache`.
93+
let dataCacheSemaphore = DispatchSemaphore(value: 1)
8794

8895
/// Semaphore for thread-safe access to `imageCache`.
89-
internal let imageCacheSemaphore = DispatchSemaphore(value: 1)
96+
let imageCacheSemaphore = DispatchSemaphore(value: 1)
9097

9198
}
9299

Sources/LaTeXSwiftUI/Models/Component.swift

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,6 @@ internal struct ComponentBlock: Hashable, Identifiable {
4444
components.count == 1 && !components[0].type.inline
4545
}
4646

47-
/// Creates the image view and its size for the given block.
48-
///
49-
/// If the block isn't an equation block, then this method returns `nil`.
50-
///
51-
/// - Parameter block: The block.
52-
/// - Returns: The image, its size, and any associated error text.
53-
@MainActor func image(
54-
font: Font,
55-
displayScale: CGFloat,
56-
renderingMode: Image.TemplateRenderingMode
57-
) -> (Image, CGSize, String?)? {
58-
guard isEquationBlock, let component = components.first else {
59-
return nil
60-
}
61-
return component.convertToImage(
62-
font: font,
63-
displayScale: displayScale,
64-
renderingMode: renderingMode)
65-
}
66-
6747
}
6848

6949
/// A LaTeX component.
@@ -203,91 +183,3 @@ internal struct Component: CustomStringConvertible, Equatable, Hashable {
203183
}
204184

205185
}
206-
207-
// MARK: Methods
208-
209-
extension Component {
210-
211-
/// Converts the component to a `Text` view.
212-
///
213-
/// - Parameters:
214-
/// - font: The font to use.
215-
/// - displayScale: The view's display scale.
216-
/// - renderingMode: The image rendering mode.
217-
/// - errorMode: The error handling mode.
218-
/// - isLastComponentInBlock: Whether or not this is the last component in
219-
/// the block that contains it.
220-
/// - Returns: A text view.
221-
@MainActor func convertToText(
222-
font: Font,
223-
displayScale: CGFloat,
224-
renderingMode: Image.TemplateRenderingMode,
225-
errorMode: LaTeX.ErrorMode,
226-
blockRenderingMode: LaTeX.BlockMode,
227-
isInEquationBlock: Bool
228-
) -> Text {
229-
// Get the component's text
230-
let text: Text
231-
if let svg = svg {
232-
// Do we have an error?
233-
if let errorText = svg.errorText, errorMode != .rendered {
234-
switch errorMode {
235-
case .original:
236-
// Use the original tex input
237-
text = Text(blockRenderingMode == .alwaysInline ? originalTextTrimmingNewlines : originalText)
238-
case .error:
239-
// Use the error text
240-
text = Text(errorText)
241-
default:
242-
text = Text("")
243-
}
244-
}
245-
else if let (image, _, _) = convertToImage(
246-
font: font,
247-
displayScale: displayScale,
248-
renderingMode: renderingMode
249-
) {
250-
let xHeight = _Font.preferredFont(from: font).xHeight
251-
let offset = svg.geometry.verticalAlignment.toPoints(xHeight)
252-
text = Text(image).baselineOffset(blockRenderingMode == .alwaysInline || !isInEquationBlock ? offset : 0)
253-
}
254-
else {
255-
text = Text("")
256-
}
257-
}
258-
else if blockRenderingMode == .alwaysInline {
259-
text = Text(originalTextTrimmingNewlines)
260-
}
261-
else {
262-
text = Text(originalText)
263-
}
264-
265-
return text
266-
}
267-
268-
/// Converts the component to an image.
269-
///
270-
/// - Parameters:
271-
/// - font: The font to use.
272-
/// - displayScale: The current display scale.
273-
/// - renderingMode: The current rendering mode.
274-
/// - Returns: Image details.
275-
@MainActor func convertToImage(
276-
font: Font,
277-
displayScale: CGFloat,
278-
renderingMode: Image.TemplateRenderingMode
279-
) -> (Image, CGSize, String?)? {
280-
guard let svg = svg else {
281-
return nil
282-
}
283-
guard let imageData = Renderer.shared.convertToImage(
284-
svg: svg,
285-
font: font,
286-
displayScale: displayScale,
287-
renderingMode: renderingMode) else {
288-
return nil
289-
}
290-
return (imageData.0, imageData.1, svg.errorText)
291-
}
292-
293-
}

0 commit comments

Comments
 (0)