@mlightcad/mtext-renderer 0.10.13 → 0.10.15

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.
@@ -24,10 +24,39 @@ export declare class ShxFont extends BaseFont {
24
24
  * @returns True if this font contains glyph of the specified character code. Otherwise, return false.
25
25
  */
26
26
  hasCode(code: number): boolean;
27
+ /**
28
+ * Horizontal advance for the space character (ASCII 32) at the given size.
29
+ * Uses the SHX glyph pen advance when defined; otherwise falls back to half the
30
+ * text height (common for AutoCAD SHX fonts).
31
+ */
32
+ getSpaceAdvance(size: number): number;
27
33
  generateShapes(text: string, size: number): ShxTextShape[];
28
34
  /**
29
- * SHX font always has fixed scale factor 1.
30
- * @returns Always return value 1
35
+ * Scale factor that converts AutoCAD's `TEXT.height` (cap-height in the SHX
36
+ * convention) into the em-box-scaled size expected by `generateShapes`.
37
+ *
38
+ * SHX glyphs are defined with `baseUp` units of cap-height (height of an
39
+ * uppercase letter above the baseline) and `height` units of total em-box
40
+ * (cap-height + descender + headroom for accents). AutoCAD's DXF group 40
41
+ * for TEXT/ATTRIB/MTEXT is the cap-height — the historical SHX convention
42
+ * since the format's introduction in AutoCAD R2 (1985), kept by AutoCAD
43
+ * when TrueType support was added in the early 1990s so that DWGs portable
44
+ * across font formats render at consistent visual sizes.
45
+ *
46
+ * Returning `1` (the previous behavior) meant SHX glyphs were rendered at
47
+ * `baseUp / height` of the expected size — typically ~0.75 for western
48
+ * fonts like `romans.shx`/`complex.shx` where `baseUp=21, height=28`.
49
+ * Multiplying by `height / baseUp` restores the cap-height interpretation.
50
+ *
51
+ * Mirrors the behavior already in place for mesh fonts (see
52
+ * `meshFontParser.ts` where `scaleFactor = unitsPerEm / glyph('A').yMax`),
53
+ * isolated to the glyph render path only — `mtextProcessor.currentLayoutFontSize`
54
+ * divides by this factor to keep layout metrics (line height, attachment
55
+ * offsets, blank width) on the original cap-height scale.
56
+ *
57
+ * @returns `height / baseUp` when both are populated and positive; `1`
58
+ * otherwise (e.g. exotic BIGFONT/symbol fonts without standard metrics),
59
+ * matching the prior behavior as a safe fallback.
31
60
  */
32
61
  getScaleFactor(): number;
33
62
  /**
@@ -19,6 +19,16 @@ export declare class ShxTextShape extends BaseTextShape {
19
19
  * @param shape - The shape data for this character
20
20
  */
21
21
  constructor(code: number, fontSize: number, shape: ShxShape, font: ShxFont);
22
+ /**
23
+ * Resolves horizontal advance for SHX glyphs.
24
+ *
25
+ * - Unicode / shapes fonts: prefer the pen-down end X (lastPoint), which matches
26
+ * AutoCAD's advance for single-byte SHX.
27
+ * - Big fonts (CJK): advance is the scaled font cell width (content.width /
28
+ * content.height * fontSize). Glyph bbox or lastPoint can be narrower than the
29
+ * cell; using only bbox caused successive Han characters to overlap visually.
30
+ */
31
+ private resolveAdvanceWidth;
22
32
  protected calcWidth(): number;
23
33
  offset(offset: Point): ShxTextShape;
24
34
  /**
@@ -109,20 +109,86 @@ export declare class MText extends THREE.Object3D {
109
109
  */
110
110
  private createMTextGroup;
111
111
  /**
112
- * Calculates the anchor point for text positioning based on alignment and flow direction.
113
- * @param width - The width of the text
114
- * @param height - The height of the text
115
- * @param attachmentPoint - The attachment point for text alignment
116
- * @param flowDirection - The text flow direction
117
- * @returns The calculated anchor point coordinates
112
+ * Measures the logical text frame used by attachment-point anchoring.
113
+ *
114
+ * @remarks
115
+ * Line-layout boxes include inter-line leading that is useful for hit testing, but
116
+ * that leading must not become extra padding above the first rendered line when
117
+ * aligning to top/middle/bottom attachment points. This method therefore prefers the
118
+ * visible geometry height from `geometryBox` when it is non-empty, and only falls
119
+ * back to `layoutHeight` when there are no measurable glyphs (e.g. empty or
120
+ * whitespace-only runs).
121
+ *
122
+ * @param object - Root group produced by layout; traversed for {@link LineLayout}
123
+ * metadata to locate the first line baseline.
124
+ * @param width - Column width used for horizontal anchoring when finite and
125
+ * positive; if not, horizontal extents are taken from `geometryBox`.
126
+ * @param layoutHeight - Total laid-out height from {@link MTextProcessor} when
127
+ * geometry height is unavailable or zero.
128
+ * @param geometryBox - Axis-aligned bounds of rendered primitives under `object`,
129
+ * typically from {@link THREE.Box3.setFromObject}.
130
+ * @param flowDirection - Optional flow; when {@link MTextFlowDirection.BOTTOM_TO_TOP},
131
+ * the vertical frame is anchored from the geometry minimum upward.
132
+ * @returns Metrics consumed by {@link MText.calculateAnchorPoint}.
133
+ */
134
+ private measureAnchorMetrics;
135
+ /**
136
+ * Computes the 2D translation that maps the logical frame described by `metrics`
137
+ * so that the chosen AutoCAD-style attachment point coincides with the insertion
138
+ * origin (before rotation/insertion-point transforms applied later in
139
+ * {@link MText.loadMText}).
140
+ *
141
+ * @remarks
142
+ * The returned vector is **added** to vertex positions and char-box metadata via
143
+ * `geometry.translate` / `CharBox.box.translate` / line `y` offsets. For `undefined`
144
+ * attachment, behavior matches {@link MTextAttachmentPoint.TopLeft}.
145
+ *
146
+ * @param metrics - Pre-anchoring frame from {@link MText.measureAnchorMetrics}.
147
+ * @param attachmentPoint - DWG attachment constant; determines which corner, edge
148
+ * center, or baseline of the frame is pinned to `(0,0)` in anchored space.
149
+ * @returns Translation `(x, y)` in drawing units applied uniformly to the laid-out
150
+ * group and its layout sidecars.
118
151
  */
119
152
  private calculateAnchorPoint;
120
153
  /**
121
- * Recursively calculates bounding boxes for an object and its children.
122
- * @param object - The Three.js object to process
123
- * @param boxes - Array to store the calculated bounding boxes
154
+ * Walks a laid-out MText subgraph and accumulates world-space character hit boxes and
155
+ * soft line rectangles for {@link MText.createLayoutData} (raycasting, cursors, debug).
156
+ *
157
+ * @remarks
158
+ * - When `userData.layout.chars` is present, entries are transformed with
159
+ * {@link buildCharBoxesFromObject} and appended to `chars`, then recursion stops
160
+ * for that branch (char metadata is authoritative).
161
+ * - When only mesh/line geometry exists, a single synthetic {@link CharBox} wrapping
162
+ * the geometry AABB may be emitted.
163
+ * - `userData.lineLayouts` rows are converted to world-space center `y` and height.
164
+ *
165
+ * @param object - Node to traverse (typically the root group from {@link MText.loadMText}).
166
+ * @param chars - Output collection; receives merged {@link CharBox} entries in world space.
167
+ * @param lines - Output collection; receives {@link LineLayout} summaries per line break.
124
168
  */
125
169
  private getLayout;
170
+ /**
171
+ * Depth-first union of this MText’s {@link MText.box} from layout metadata and mesh
172
+ * geometry, while optionally tracking coarse line AABB in `lineBounds`.
173
+ *
174
+ * @remarks
175
+ * Priority order per node:
176
+ * 1. If `userData.logicalBounds` exists (set in {@link MText.loadMText}), union that
177
+ * axis-aligned rectangle transformed by `matrixWorld` — this reflects anchored
178
+ * logical extents even when individual glyphs are expensive to union.
179
+ * 2. Else if `userData.lineLayouts` exists, expand `lineBounds` from the world-space
180
+ * top/bottom of each line strip (used as a fallback when no logical bounds were
181
+ * written, e.g. legacy paths).
182
+ * 3. Else if `userData.layout.chars` exists, union each transformed char AABB.
183
+ * 4. Else for mesh/line primitives, union non-decoration geometry bounds.
184
+ * 5. Always recurse into children.
185
+ *
186
+ * @param object - Current scene graph node.
187
+ * @param lineBounds - Mutable accumulator: `hasLine`/`min*`/`max*` track the union of
188
+ * line-layout strips in world space; `hasLogicalBounds` flips true when any child
189
+ * contributed `logicalBounds` so {@link MText.syncDraw} can avoid double-counting
190
+ * vertical extent from line strips alone.
191
+ */
126
192
  private updateBoxFromObject;
127
193
  /**
128
194
  * Remove the specified object from its parent and release geometry and material resource used
@@ -130,5 +196,12 @@ export declare class MText extends THREE.Object3D {
130
196
  * @param obj - Input object to dispose
131
197
  */
132
198
  private disposeThreeObject;
199
+ /**
200
+ * Normalizes a font file reference to the lowercase stem used by
201
+ * {@link FontManager.loadFontsByNames} (strip extension).
202
+ *
203
+ * @param fontFileName - Style font path such as `arial.ttf` or extension-less name.
204
+ * @returns Lowercase name without the last extension segment, or `undefined` if empty.
205
+ */
133
206
  private getFontName;
134
207
  }
@@ -166,6 +166,8 @@ export declare class MTextProcessor {
166
166
  * The current scale factor of character width
167
167
  */
168
168
  get currentWidthFactor(): number;
169
+ /** Horizontal advance for one space, including tracking and width factor. */
170
+ get currentBlankAdvance(): number;
169
171
  /**
170
172
  * All of THREE.js objects in current line. It contains objects in all of sections of this line.
171
173
  */
@@ -305,14 +307,11 @@ export declare class MTextProcessor {
305
307
  */
306
308
  private processAlignment;
307
309
  /**
308
- * In AutoCAD, the width of a regular space character (ASCII 32, the space key on the keyboard) in MText
309
- * depends on the current font and text height, and is not a fixed value.
310
- * Specifically:
311
- * - Space width Text height × space width ratio defined by the font
312
- * - For common TrueType fonts (like Arial), the space width is typically about 1/4 to 1/3 of the text height.
313
- * For example, if the text height is 10 (units), the space width would be approximately 2.5 to 3.3 units.
314
- * - For SHX fonts (AutoCAD's built-in vector fonts, such as txt.shx), the space width is often half the text height.
315
- * So if the text height is 10, the space width is typically 5 units.
310
+ * Resolves horizontal advance for an ASCII space in the active font.
311
+ *
312
+ * AutoCAD uses each font's own space glyph metrics (SHX pen advance or TrueType
313
+ * horizontal advance). A fixed fraction of text height (e.g. 50% for SHX) is only
314
+ * a rough fallback when the font has no space definition.
316
315
  */
317
316
  private calculateBlankWidthForFont;
318
317
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlightcad/mtext-renderer",
3
- "version": "0.10.13",
3
+ "version": "0.10.15",
4
4
  "description": "AutoCAD MText renderer based on Three.js",
5
5
  "license": "MIT",
6
6
  "author": "MLight Lee <mlight.lee@outlook.com>",