@mlightcad/mtext-renderer 0.12.0 → 0.12.2

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.
@@ -48,6 +48,12 @@ export declare class FontManager {
48
48
  missedFonts: Record<string, number>;
49
49
  /** Flag to enable/disable font caching */
50
50
  enableFontCache: boolean;
51
+ /**
52
+ * When true (default), missing fonts are fetched/parsed in the background
53
+ * via {@link requestFont} instead of requiring an open-time preload.
54
+ * Drawing continues with temporary fallbacks until {@link events.fontLoaded}.
55
+ */
56
+ lazyFontLoading: boolean;
51
57
  /**
52
58
  * Default fonts to use when a requested font is not found or lacks a glyph.
53
59
  * Insertion order is preserved; earlier entries are tried first.
@@ -66,6 +72,18 @@ export declare class FontManager {
66
72
  /** Event triggered when a font is successfully loaded */
67
73
  fontLoaded: EventManager<FontManagerEventArgs>;
68
74
  };
75
+ /** In-flight {@link requestFont} promises keyed by normalized font name. */
76
+ private fontRequestInFlight;
77
+ /**
78
+ * Fonts whose latest {@link requestFont} finished without registering the
79
+ * face. Prevents per-glyph retry storms until {@link release} clears state.
80
+ */
81
+ private fontRequestFailed;
82
+ /**
83
+ * Bumped by full {@link release} so in-flight loads that complete after a
84
+ * release do not re-register fonts into a cleared manager.
85
+ */
86
+ private loadEpoch;
69
87
  private constructor();
70
88
  /**
71
89
  * Gets the singleton instance of the FontManager
@@ -94,7 +112,7 @@ export declare class FontManager {
94
112
  * ```ts
95
113
  * FontManager.instance.setDefaultFonts('r12r14')
96
114
  * FontManager.instance.setDefaultFonts(['hztxt', 'simsun', 'gdt'])
97
- * FontManager.instance.setDefaultFonts('simkai')
115
+ * FontManager.instance.setDefaultFonts('simsun')
98
116
  * ```
99
117
  */
100
118
  setDefaultFonts(fonts: DefaultFontsPreset): void;
@@ -152,6 +170,19 @@ export declare class FontManager {
152
170
  * @returns Promise that resolves to an array of font load statuses
153
171
  */
154
172
  loadFontsByNames(names: string | readonly string[]): Promise<FontLoadStatus[]>;
173
+ /**
174
+ * Schedules a non-blocking load for a font that is not yet in memory.
175
+ *
176
+ * Concurrent callers for the same name share one in-flight promise.
177
+ * Already-loaded fonts resolve immediately. Safe to call from sync draw
178
+ * paths — do not await from the hot glyph loop.
179
+ */
180
+ requestFont(fontName: string): Promise<FontLoadStatus[]>;
181
+ /**
182
+ * Fire-and-forget {@link requestFont} for each name (deduped per name).
183
+ */
184
+ requestFonts(fontNames: readonly string[]): void;
185
+ private normalizeFontName;
155
186
  /**
156
187
  * Parses a user-uploaded font file, registers it for rendering, and stores
157
188
  * it in IndexedDB when {@link enableFontCache} is true.
@@ -102,10 +102,35 @@ export declare class ShxFont extends BaseFont {
102
102
  * Clears layout/code caches and disposes retained shape geometries.
103
103
  */
104
104
  dispose(): void;
105
+ /**
106
+ * Sentinel returned by {@link getCode} when `char` has no representation
107
+ * in this BIGFONT's legacy encoding. Never a real SHX/BIGFONT code point
108
+ * (those are non-negative), so callers can distinguish "cannot encode"
109
+ * from "encodes to some rarely-used code".
110
+ */
111
+ private static readonly NOT_ENCODABLE;
105
112
  /**
106
113
  * Resolves the internal SHX character code for a given Unicode character.
114
+ *
115
+ * For BIGFONT fonts, `char` is converted through a legacy encoding (e.g.
116
+ * GBK) via `iconv-lite`. Characters outside that encoding's repertoire
117
+ * (math/symbol glyphs like the diameter sign, U+2205) are not rejected by
118
+ * `iconv.encode` — it silently substitutes a replacement byte (commonly
119
+ * ASCII `?`, 0x3F). Left unchecked, that byte resolves to the BIGFONT's
120
+ * own, perfectly valid `?` glyph, so `hasChar`/`getCharShape` report a
121
+ * false positive: the caller believes this font renders the character,
122
+ * when it actually renders an unrelated question mark. That masked the
123
+ * real GDT/symbol-font fallback for diameter dimension text stored as a
124
+ * literal U+2205 (mlightcad/cad-viewer#473) — the correct glyph exists in
125
+ * `amgdt.shx`, but the fallback chain in {@link FontManager} never got a
126
+ * chance because this font's bogus "yes" won first.
127
+ *
128
+ * A decode-of-the-encoded-bytes round trip catches this: encoding is lossy
129
+ * exactly when it can't recover the original character.
130
+ *
107
131
  * @param char - The input character.
108
- * @returns The internal SHX code used for lookup.
132
+ * @returns The internal SHX code used for lookup, or {@link NOT_ENCODABLE}
133
+ * when `char` cannot be represented in this font's encoding.
109
134
  */
110
135
  private getCode;
111
136
  }
@@ -1,10 +1,30 @@
1
+ import { MTextColor } from '@mlightcad/mtext-parser';
1
2
  import { ColorSettings } from './types';
2
3
  export declare function resolveMTextColor(colorSettings: ColorSettings): number;
4
+ /** Wire-format for an `MTextColor` carried on worker glyph payloads. */
5
+ export interface SerializedMTextColor {
6
+ aci?: number | null;
7
+ rgbValue?: number | null;
8
+ }
9
+ /**
10
+ * Serializes an `MTextColor` for worker → main-thread transfer.
11
+ *
12
+ * Prefer this over baking only the resolved RGB: ACI 7 (canvas foreground) and
13
+ * ACI 255 (literal white) both resolve to `0xffffff` on a dark canvas, but only
14
+ * ACI 7 must invert when the background flips.
15
+ */
16
+ export declare function serializeMTextColor(color: MTextColor): SerializedMTextColor;
17
+ /** Revives a wire-format color into a real `MTextColor` instance. */
18
+ export declare function deserializeMTextColor(serialized?: SerializedMTextColor | null): MTextColor | undefined;
3
19
  /**
4
20
  * Rebuild ColorSettings for a worker-deserialized glyph material.
5
21
  *
6
- * Worker meshes already carry the final resolved RGB in their serialized
7
- * material. When the entity base color is ByLayer, preserve that semantic only
8
- * for glyphs that match the layer fallback color.
22
+ * Prefer {@link serializedColor} when the worker attached the original segment
23
+ * color (ACI / RGB). Falling back to resolved RGB alone cannot distinguish
24
+ * ACI 7 from ACI 255.
25
+ *
26
+ * Without a serialized color, preserve ByLayer / ByBlock / entity ACI when the
27
+ * glyph RGB still matches the entity base resolution; otherwise bake RGB for
28
+ * true inline overrides.
9
29
  */
10
- export declare function buildWorkerMaterialColorSettings(base: ColorSettings, resolvedColor: number, baseByLayer: boolean): ColorSettings;
30
+ export declare function buildWorkerMaterialColorSettings(base: ColorSettings, resolvedColor: number, baseByLayer: boolean, serializedColor?: SerializedMTextColor | null): ColorSettings;
@@ -60,7 +60,11 @@ export declare class MText extends THREE.Object3D {
60
60
  */
61
61
  dispose(): void;
62
62
  /**
63
- * Draw the MText object. This method loads required fonts on demand and builds the object graph.
63
+ * Draw the MText object. Schedules required fonts for background load and
64
+ * builds the object graph immediately with current fallbacks.
65
+ *
66
+ * Does not await downloads — callers that need glyphs from newly loaded fonts
67
+ * should redraw after {@link FontManager.events.fontLoaded}.
64
68
  */
65
69
  asyncDraw(): Promise<void>;
66
70
  /**
@@ -22,8 +22,9 @@ export declare class MainThreadRenderer implements MTextBaseRenderer {
22
22
  */
23
23
  setFontUrl(value: string): Promise<void>;
24
24
  /**
25
- * Render MText directly in the main thread asynchronously. It will ensure that default font
26
- * is loaded. And fonts needed in mtext are loaded on demand.
25
+ * Render MText directly in the main thread asynchronously. Fonts referenced by
26
+ * the text/style are scheduled via {@link FontManager.requestFonts} when
27
+ * {@link FontManager.lazyFontLoading} is enabled; otherwise they are awaited.
27
28
  */
28
29
  asyncRenderMText(mtextContent: MTextData, textStyle: TextStyle, colorSettings?: ColorSettings): Promise<MTextObject>;
29
30
  /**
@@ -15,6 +15,8 @@ export declare class UnifiedRenderer {
15
15
  private defaultMode;
16
16
  private workerConfig;
17
17
  private webWorkerConfigured;
18
+ /** Last lazyFontLoading value pushed to the worker pool, if any. */
19
+ private workerLazyFontLoading;
18
20
  /**
19
21
  * Constructor
20
22
  *
@@ -68,6 +70,11 @@ export declare class UnifiedRenderer {
68
70
  * Sets the default font fallback chain on the active renderer and workers.
69
71
  */
70
72
  setDefaultFonts(fonts: DefaultFontsPreset | string | readonly string[]): Promise<void>;
73
+ /**
74
+ * Mirrors {@link FontManager.lazyFontLoading} onto the main thread and any
75
+ * existing worker pool.
76
+ */
77
+ setLazyFontLoading(enabled: boolean): Promise<void>;
71
78
  /**
72
79
  * Returns font names for a predefined default-font preset.
73
80
  */
@@ -113,6 +113,11 @@ interface SerializedChild {
113
113
  opacity: number;
114
114
  side?: number;
115
115
  linewidth?: number;
116
+ /** Original segment colour when the worker preserved ACI/RGB semantics. */
117
+ mtextColor?: {
118
+ aci?: number | null;
119
+ rgbValue?: number | null;
120
+ };
116
121
  };
117
122
  charBoxType?: CharBox['type'];
118
123
  lineLayouts?: Array<{
@@ -135,6 +140,15 @@ export declare class WebWorkerRenderer implements MTextBaseRenderer {
135
140
  private readyPromise;
136
141
  private isInitialized;
137
142
  private defaultStyleManager;
143
+ /**
144
+ * Fonts known to be present in every worker after an explicit loadFonts or
145
+ * after a lazy fontLoaded was fan-out to the full pool.
146
+ */
147
+ private poolSyncedFonts;
148
+ /** In-flight pool-wide font syncs keyed by normalized font name. */
149
+ private poolFontSyncInFlight;
150
+ /** Fonts already forwarded as main-thread fontLoaded for this pool lifetime. */
151
+ private poolFontLoadedDispatched;
138
152
  constructor(config?: WebWorkerRendererConfig);
139
153
  /**
140
154
  * Used to manage materials used by texts
@@ -146,6 +160,11 @@ export declare class WebWorkerRenderer implements MTextBaseRenderer {
146
160
  * Handles messages coming from any worker.
147
161
  */
148
162
  private handleWorkerMessage;
163
+ /**
164
+ * Ensures every worker has `fontName`, then returns whether the main thread
165
+ * should emit {@link FontManager.events.fontLoaded} for this name.
166
+ */
167
+ private syncFontToWorkerPool;
149
168
  /**
150
169
  * Attaches message and error handlers to a worker.
151
170
  */
@@ -163,6 +182,10 @@ export declare class WebWorkerRenderer implements MTextBaseRenderer {
163
182
  * Syncs the default font fallback chain to all workers.
164
183
  */
165
184
  setDefaultFonts(fonts: readonly string[], symbolFonts: readonly string[]): Promise<void>;
185
+ /**
186
+ * Mirrors {@link FontManager.lazyFontLoading} into every worker isolate.
187
+ */
188
+ setLazyFontLoading(enabled: boolean): Promise<void>;
166
189
  /**
167
190
  * Render MText in one worker and return serialized data asynchronously.
168
191
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlightcad/mtext-renderer",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "AutoCAD MText renderer based on Three.js",
5
5
  "license": "MIT",
6
6
  "author": "MLight Lee <mlight.lee@outlook.com>",