@mlightcad/mtext-renderer 0.4.10 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,491 @@
1
+ # MText Renderer for Three.js
2
+
3
+ A flexible and extensible AutoCAD MText renderer implementation using Three.js. This package provides a modular architecture to render AutoCAD MText content with different rendering engines, with a primary focus on Three.js rendering.
4
+
5
+ ## Features
6
+
7
+ - Render AutoCAD MText content using Three.js
8
+ - Modular font loading system
9
+ - Font management and dynamic font loading
10
+ - Cache parsed fonts to improve rendering performance
11
+
12
+ ## Core Components
13
+
14
+ ### FontManager
15
+
16
+ The central manager for font operations. It's a singleton class that handles font loading, caching, and text rendering.
17
+
18
+ **Public Properties:**
19
+ - `unsupportedChars`: Record of characters not supported by any loaded font
20
+ - `missedFonts`: Record of fonts that were requested but not found
21
+ - `enableFontCache`: Flag to enable/disable font caching. If it is true, parsed fonts
22
+ will be stored in local IndexedDB to improve performance. Default value is true.
23
+ - `defaultFont`: Default font to use when a requested font is not found
24
+ - `events`: Event managers for font-related events
25
+ - `fontNotFound`: Triggered when a font cannot be found
26
+ - `fontLoaded`: Triggered when a font is successfully loaded
27
+
28
+ **Public Methods:**
29
+ - `getAvaiableFonts()`: Retrieve metadata of available fonts from the configured loader
30
+ - `loadFontsByNames(names)`: Loads fonts by logical names (e.g., 'simsun', 'arial')
31
+ - `getCharShape(char, fontName, size)`: Gets text shape for a character
32
+ - `getFontScaleFactor(fontName)`: Gets scale factor for a font
33
+ - `getNotFoundTextShape(size)`: Gets shape for not found indicator
34
+ - `getUnsupportedChar()`: Gets record of unsupported characters
35
+ - `release()`: Releases all loaded fonts
36
+
37
+ ### FontLoader & DefaultFontLoader
38
+
39
+ Interface for font loading operations. The default implementation [DefaultFontLoader](./src/font/defaultFontLoader.ts) uses a [CDN-based font repository](https://cdn.jsdelivr.net/gh/mlight-lee/cad-data/fonts/). It loads font metadata from a JSON file and provides access to available fonts.
40
+
41
+ You do NOT need to create `DefaultFontLoader` yourself anymore. `FontManager` manages a loader instance internally. If you want to customize font loading, implement `FontLoader` and set it via `FontManager.instance.setFontLoader(customLoader)`.
42
+
43
+ **Public Methods:**
44
+ - `load(fontNames)`: Loads specified fonts into the system
45
+ - `getAvaiableFonts()`: Retrieves information about available fonts
46
+
47
+ ### BaseFont
48
+
49
+ Abstract base class for font implementations. Provides common functionality for font handling.
50
+
51
+ **Public Properties:**
52
+ - `type`: Type of font ('shx' or 'mesh')
53
+ - `data`: Parsed font data
54
+ - `unsupportedChars`: Record of unsupported characters
55
+
56
+ **Public Methods:**
57
+ - `getCharShape(char, size)`: Gets shape for a character
58
+ - `getScaleFactor()`: Gets font scale factor
59
+ - `getNotFoundTextShape(size)`: Gets shape for not found indicator
60
+
61
+ ### BaseTextShape
62
+
63
+ Abstract base class for text shape implementations. Provides common functionality for text shape handling.
64
+
65
+ **Public Properties:**
66
+ - `char`: Character this shape represents
67
+ - `size`: Size of the text shape
68
+
69
+ **Public Methods:**
70
+ - `getWidth()`: Gets width of text shape
71
+ - `getHeight()`: Gets height of text shape
72
+ - `toGeometry()`: Converts shape to THREE.BufferGeometry
73
+
74
+ ### FontFactory
75
+
76
+ Singleton factory class for creating font instances. Handles creation of appropriate font objects based on type and data format.
77
+
78
+ **Public Methods:**
79
+ - `createFont(data)`: Creates font from font data
80
+ - `createFontFromBuffer(fileName, buffer)`: Creates font from file data
81
+
82
+ ### FontCacheManager
83
+ Manages font data caching using IndexedDB. Provides persistent storage for font data.
84
+
85
+ **Public Methods:**
86
+ - `get(fileName)`: Retrieves font data from cache
87
+ - `set(fileName, fontData)`: Stores font data in cache
88
+ - `getAll()`: Retrieves all cached font data
89
+ - `clear()`: Clears all cached font data
90
+
91
+ ## Worker-Based Rendering System
92
+
93
+ The package provides a sophisticated worker-based rendering system that allows MText rendering to be performed in Web Workers, preventing blocking of the main UI thread. This is particularly beneficial for rendering large amounts of text or complex MText content.
94
+
95
+ ### MTextBaseRenderer Interface
96
+
97
+ Defines the common rendering contract for producing Three.js objects from MText content. All renderer implementations must conform to this interface.
98
+
99
+ **Public Methods:**
100
+ - `renderMText(mtextContent, textStyle, colorSettings?)`: Render MText content into a Three.js object hierarchy
101
+ - `destroy()`: Release any resources owned by the renderer
102
+
103
+ ### MTextObject Interface
104
+
105
+ Represents a rendered MText object that extends THREE.Object3D with additional MText-specific properties.
106
+
107
+ **Public Properties:**
108
+ - `box`: The bounding box of the MText object in local coordinates
109
+
110
+ ### MainThreadRenderer
111
+
112
+ Renders MText content directly in the main thread. This is the simplest renderer implementation that provides the same interface as worker-based renderers but runs synchronously in the main thread.
113
+
114
+ **Public Methods:**
115
+ - `renderMText(mtextContent, textStyle, colorSettings?)`: Render MText directly in the main thread
116
+ - `destroy()`: Cleanup resources
117
+
118
+ ### WebWorkerRenderer (MTextWorkerManager)
119
+
120
+ Manages communication with MText Web Workers for parallel text rendering. This renderer uses a pool of Web Workers to distribute rendering tasks, improving performance for large text content.
121
+
122
+ **Public Properties:**
123
+ - `poolSize`: Number of workers in the pool (defaults to optimal size based on hardware concurrency)
124
+
125
+ **Public Methods:**
126
+ - `renderMText(mtextContent, textStyle, colorSettings?)`: Render MText using worker pool
127
+ - `terminate()`: Terminate all workers
128
+ - `destroy()`: Cleanup all resources
129
+
130
+ **Features:**
131
+ - Automatic worker pool management
132
+ - Load balancing across workers
133
+ - Efficient serialization/deserialization of Three.js objects
134
+ - Transferable object support for optimal performance
135
+ - Error handling and timeout management
136
+
137
+ ### UnifiedRenderer
138
+
139
+ A flexible renderer that can switch between main thread and Web Worker rendering modes at runtime. This allows applications to dynamically choose the best rendering strategy based on current conditions.
140
+
141
+ **Public Properties:**
142
+ - `currentMode`: Current rendering mode ('main' or 'worker')
143
+
144
+ **Public Methods:**
145
+ - `switchMode(mode)`: Switch between main thread and worker rendering modes
146
+ - `getMode()`: Get current rendering mode
147
+ - `renderMText(mtextContent, textStyle, colorSettings?)`: Render using current mode
148
+ - `destroy()`: Clean up all resources
149
+
150
+ ### MTextWorker
151
+
152
+ The actual Web Worker implementation that handles MText rendering tasks. This worker contains its own FontManager, StyleManager, and FontLoader instances, allowing it to work independently from the main thread.
153
+
154
+ **Features:**
155
+ - Independent font and style management
156
+ - Can preload fonts on demand via a `loadFonts` message to avoid redundant concurrent loads
157
+ - Efficient object serialization for transfer to main thread
158
+ - Support for transferable objects to minimize memory copying
159
+ - Error handling and response management
160
+
161
+ ### MText
162
+ Main class for rendering AutoCAD MText content. Extends THREE.Object3D to integrate with Three.js scene graph.
163
+
164
+ **Public Properties:**
165
+ - `fontManager`: Reference to FontManager instance for font operations
166
+ - `styleManager`: Reference to StyleManager instance for style operations
167
+
168
+ **Public Methods:**
169
+ - `draw()`: Asynchronously builds geometry and loads required fonts on demand
170
+
171
+ ## Class Diagram
172
+
173
+ ```mermaid
174
+ classDiagram
175
+ class MText {
176
+ +content: MTextContent
177
+ +style: MTextStyle
178
+ +fontManager: FontManager
179
+ +styleManager: StyleManager
180
+ +update()
181
+ +setContent(content)
182
+ +setStyle(style)
183
+ +dispose()
184
+ }
185
+
186
+ class FontManager {
187
+ -_instance: FontManager
188
+ +unsupportedChars: Record
189
+ +missedFonts: Record
190
+ +enableFontCache: boolean
191
+ +defaultFont: string
192
+ +events: EventManagers
193
+ +loadFonts(urls)
194
+ +getCharShape(char, fontName, size)
195
+ +getFontScaleFactor(fontName)
196
+ +getNotFoundTextShape(size)
197
+ +getUnsupportedChar()
198
+ +release()
199
+ }
200
+
201
+ class FontLoader {
202
+ <<interface>>
203
+ +load(fontNames)
204
+ +getAvaiableFonts()
205
+ }
206
+
207
+ class DefaultFontLoader {
208
+ -_avaiableFonts: FontInfo[]
209
+ +load(fontNames)
210
+ +getAvaiableFonts()
211
+ }
212
+
213
+ class BaseFont {
214
+ <<abstract>>
215
+ +type: FontType
216
+ +data: unknown
217
+ +unsupportedChars: Record
218
+ +getCharShape(char, size)
219
+ +getScaleFactor()
220
+ +getNotFoundTextShape(size)
221
+ }
222
+
223
+ class BaseTextShape {
224
+ <<abstract>>
225
+ +char: string
226
+ +size: number
227
+ +getWidth()
228
+ +getHeight()
229
+ +toGeometry()
230
+ }
231
+
232
+ class FontFactory {
233
+ -_instance: FontFactory
234
+ +createFont(data)
235
+ +createFontFromBuffer(fileName, buffer)
236
+ }
237
+
238
+ class FontCacheManager {
239
+ -_instance: FontCacheManager
240
+ +get(fileName)
241
+ +set(fileName, fontData)
242
+ +getAll()
243
+ +clear()
244
+ }
245
+
246
+ class MTextBaseRenderer {
247
+ <<interface>>
248
+ +renderMText(mtextContent, textStyle, colorSettings?)
249
+ +loadFonts(fonts)
250
+ +getAvailableFonts()
251
+ +destroy()
252
+ }
253
+
254
+ class MTextObject {
255
+ <<interface>>
256
+ +box: Box3
257
+ }
258
+
259
+ class MainThreadRenderer {
260
+ -fontManager: FontManager
261
+ -styleManager: StyleManager
262
+ -fontLoader: DefaultFontLoader
263
+ +renderMText(mtextContent, textStyle, colorSettings?)
264
+ +loadFonts(fonts)
265
+ +getAvailableFonts()
266
+ +destroy()
267
+ }
268
+
269
+ class WebWorkerRenderer {
270
+ -workers: Worker[]
271
+ -inFlightPerWorker: number[]
272
+ -pendingRequests: Map
273
+ -poolSize: number
274
+ +renderMText(mtextContent, textStyle, colorSettings?)
275
+ +loadFonts(fonts)
276
+ +getAvailableFonts()
277
+ +terminate()
278
+ +destroy()
279
+ }
280
+
281
+ class UnifiedRenderer {
282
+ -workerManager: WebWorkerRenderer
283
+ -mainThreadRenderer: MainThreadRenderer
284
+ -adapter: MTextBaseRenderer
285
+ -currentMode: RenderMode
286
+ +switchMode(mode)
287
+ +getMode()
288
+ +renderMText(mtextContent, textStyle, colorSettings?)
289
+ +loadFonts(fonts)
290
+ +getAvailableFonts()
291
+ +destroy()
292
+ }
293
+
294
+ class MTextWorker {
295
+ +fontManager: FontManager
296
+ +styleManager: StyleManager
297
+ +fontLoader: DefaultFontLoader
298
+ +serializeMText(mtext)
299
+ +serializeChildren(mtext)
300
+ }
301
+
302
+ MText --> FontManager
303
+ MText --> StyleManager
304
+ FontManager --> FontFactory
305
+ FontManager --> FontCacheManager
306
+ FontManager --> BaseFont
307
+ DefaultFontLoader ..|> FontLoader
308
+ BaseFont <|-- MeshFont
309
+ BaseFont <|-- ShxFont
310
+ BaseTextShape <|-- MeshTextShape
311
+ BaseTextShape <|-- ShxTextShape
312
+ MainThreadRenderer ..|> MTextBaseRenderer
313
+ WebWorkerRenderer ..|> MTextBaseRenderer
314
+ UnifiedRenderer --> WebWorkerRenderer
315
+ UnifiedRenderer --> MainThreadRenderer
316
+ UnifiedRenderer ..|> MTextBaseRenderer
317
+ MTextWorker --> FontManager
318
+ MTextWorker --> StyleManager
319
+ MTextWorker --> DefaultFontLoader
320
+ WebWorkerRenderer --> MTextWorker
321
+ MTextBaseRenderer --> MTextObject
322
+ ```
323
+
324
+ ## Usage
325
+
326
+ ```typescript
327
+ import * as THREE from 'three';
328
+ import { FontManager, MText, StyleManager } from '@mlightcad/mtext-renderer';
329
+
330
+ // Initialize core components
331
+ const fontManager = FontManager.instance;
332
+ const styleManager = new StyleManager();
333
+
334
+ // Optionally preload a font (otherwise MText will load on demand in draw())
335
+ await fontManager.loadFontsByNames(['simsun']);
336
+
337
+ // Create MText content
338
+ const mtextContent = {
339
+ text: '{\\fArial|b0|i0|c0|p34;Hello World}',
340
+ height: 0.1,
341
+ width: 0,
342
+ position: new THREE.Vector3(0, 0, 0),
343
+ };
344
+
345
+ // Create MText instance with style
346
+ const mtext = new MText(
347
+ mtextContent,
348
+ {
349
+ name: 'Standard',
350
+ standardFlag: 0,
351
+ fixedTextHeight: 0.1,
352
+ widthFactor: 1,
353
+ obliqueAngle: 0,
354
+ textGenerationFlag: 0,
355
+ lastHeight: 0.1,
356
+ font: 'Standard',
357
+ bigFont: '',
358
+ color: 0xffffff,
359
+ },
360
+ styleManager,
361
+ fontManager
362
+ );
363
+
364
+ // Build geometry and load fonts on demand
365
+ await mtext.draw();
366
+
367
+ // Add to Three.js scene
368
+ scene.add(mtext);
369
+ ```
370
+
371
+ ## Worker-Based Rendering Usage
372
+
373
+ ### Using MainThreadRenderer
374
+
375
+ ```typescript
376
+ import { MainThreadRenderer } from '@mlightcad/mtext-renderer';
377
+
378
+ // Create main thread renderer
379
+ const renderer = new MainThreadRenderer();
380
+
381
+ // Render MText content (fonts are loaded on demand during rendering)
382
+ const mtextObject = await renderer.renderMText(
383
+ mtextContent,
384
+ textStyle,
385
+ { byLayerColor: 0xffffff, byBlockColor: 0xffffff }
386
+ );
387
+
388
+ // Add to scene
389
+ scene.add(mtextObject);
390
+ ```
391
+
392
+ ### Using WebWorkerRenderer
393
+
394
+ ```typescript
395
+ import { WebWorkerRenderer } from '@mlightcad/mtext-renderer';
396
+
397
+ // Create worker renderer with custom pool size
398
+ const workerRenderer = new WebWorkerRenderer({ poolSize: 4 }); // 4 workers
399
+
400
+ // Optionally preload fonts once via a coordinator to avoid duplicate concurrent loads
401
+ // await workerRenderer.loadFonts(['simsun', 'arial']);
402
+
403
+ // Render MText content using workers (fonts will be loaded on demand if not preloaded)
404
+ const mtextObject = await workerRenderer.renderMText(
405
+ mtextContent,
406
+ textStyle,
407
+ { byLayerColor: 0xffffff, byBlockColor: 0xffffff }
408
+ );
409
+
410
+ // Add to scene
411
+ scene.add(mtextObject);
412
+
413
+ // Clean up when done
414
+ workerRenderer.destroy();
415
+ ```
416
+
417
+ ### Using UnifiedRenderer
418
+
419
+ ```typescript
420
+ import { UnifiedRenderer } from '@mlightcad/mtext-renderer';
421
+
422
+ // Create unified renderer starting in main thread mode
423
+ const unifiedRenderer = new UnifiedRenderer('main');
424
+
425
+ // Render using main thread (fonts loaded on demand)
426
+ let mtextObject = await unifiedRenderer.renderMText(
427
+ mtextContent,
428
+ textStyle,
429
+ { byLayerColor: 0xffffff, byBlockColor: 0xffffff }
430
+ );
431
+
432
+ scene.add(mtextObject);
433
+
434
+ // Switch to worker mode for heavy rendering tasks
435
+ unifiedRenderer.switchMode('worker');
436
+
437
+ // Optionally preload fonts in workers to avoid duplicates
438
+ // await unifiedRenderer.loadFonts(['simsun', 'arial']);
439
+
440
+ // Render using workers
441
+ mtextObject = await unifiedRenderer.renderMText(
442
+ heavyMtextContent,
443
+ textStyle,
444
+ { byLayerColor: 0xffffff, byBlockColor: 0xffffff }
445
+ );
446
+
447
+ scene.add(mtextObject);
448
+
449
+ // Clean up
450
+ unifiedRenderer.destroy();
451
+ ```
452
+
453
+ ### Performance Considerations
454
+
455
+ **When to use MainThreadRenderer:**
456
+ - Simple MText content with few characters
457
+ - When you need immediate synchronous results
458
+ - When worker overhead would be greater than rendering time
459
+
460
+ **When to use WebWorkerRenderer:**
461
+ - Large amounts of MText content
462
+ - Complex text with many formatting codes
463
+ - When you want to keep the main thread responsive
464
+ - Batch processing of multiple MText objects
465
+
466
+ **When to use UnifiedRenderer:**
467
+ - Applications that need to switch rendering strategies dynamically
468
+ - When rendering requirements vary based on content complexity
469
+ - Development environments where you want to test both approaches
470
+
471
+ If all of fonts or certain fonts are not needed any more after rendering, you can call method `release` of class `FontManager` to free memory occupied by them. Based on testing, one Chinese mesh font file may take 40M memory.
472
+
473
+ ```typescript
474
+ // ---
475
+ // FontManager: Releasing Fonts
476
+ // ---
477
+ // To release all loaded fonts and free memory:
478
+ fontManager.release();
479
+
480
+ // To release a specific font by name (e.g., 'simsun'):
481
+ fontManager.release('simsun');
482
+ // Returns true if the font was found and released, false otherwise.
483
+ ```
484
+
485
+ ## License
486
+
487
+ MIT
488
+
489
+ ## Contributing
490
+
491
+ Contributions are welcome! Please read our contributing guidelines for details.