@mlightcad/mtext-renderer 0.10.14 → 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.
@@ -28418,8 +28418,25 @@ var P1 = Fc((L) => {
28418
28418
  * @param shape - The shape data for this character
28419
28419
  */
28420
28420
  constructor(e, t, r, i) {
28421
- var s;
28422
- super(), this.fontSize = t, this.shape = r, this.font = i, this.code = e, i.data.header.fontType === Te.BIGFONT ? this.width = this.calcWidth() : this.width = ((s = r.lastPoint) == null ? void 0 : s.x) ?? this.calcWidth();
28421
+ super(), this.fontSize = t, this.shape = r, this.font = i, this.code = e, this.width = this.resolveAdvanceWidth(r);
28422
+ }
28423
+ /**
28424
+ * Resolves horizontal advance for SHX glyphs.
28425
+ *
28426
+ * - Unicode / shapes fonts: prefer the pen-down end X (lastPoint), which matches
28427
+ * AutoCAD's advance for single-byte SHX.
28428
+ * - Big fonts (CJK): advance is the scaled font cell width (content.width /
28429
+ * content.height * fontSize). Glyph bbox or lastPoint can be narrower than the
28430
+ * cell; using only bbox caused successive Han characters to overlap visually.
28431
+ */
28432
+ resolveAdvanceWidth(e) {
28433
+ var i;
28434
+ const t = this.calcWidth(), r = ((i = e.lastPoint) == null ? void 0 : i.x) ?? 0;
28435
+ if (this.font.data.header.fontType === Te.BIGFONT) {
28436
+ const { width: s, height: a } = this.font.data.content, o = a > 0 ? s / a * this.fontSize : 0;
28437
+ return Math.max(t, r, o);
28438
+ }
28439
+ return r > 0 ? r : t;
28423
28440
  }
28424
28441
  calcWidth() {
28425
28442
  const e = this.shape.bbox;
@@ -28479,18 +28496,27 @@ var P1 = Fc((L) => {
28479
28496
  hasCode(e) {
28480
28497
  return this.font.hasChar(e);
28481
28498
  }
28499
+ /**
28500
+ * Horizontal advance for the space character (ASCII 32) at the given size.
28501
+ * Uses the SHX glyph pen advance when defined; otherwise falls back to half the
28502
+ * text height (common for AutoCAD SHX fonts).
28503
+ */
28504
+ getSpaceAdvance(e) {
28505
+ const t = this.getCharShape(" ", e);
28506
+ return t ? t.width : e * 0.5;
28507
+ }
28482
28508
  generateShapes(e, t) {
28483
28509
  const r = [];
28484
28510
  let i = 0;
28485
28511
  for (let s = 0; s < e.length; s++) {
28486
28512
  const a = e[s];
28487
28513
  if (a === " ") {
28488
- i += t;
28514
+ i += this.getSpaceAdvance(t);
28489
28515
  continue;
28490
28516
  }
28491
28517
  const o = this.getCharShape(a, t);
28492
28518
  if (!o) {
28493
- i += t, this.addUnsupportedChar(a);
28519
+ i += this.getSpaceAdvance(t), this.addUnsupportedChar(a);
28494
28520
  continue;
28495
28521
  }
28496
28522
  r.push(o.offset(new fe(i, 0))), i += o.width;
@@ -28498,11 +28524,35 @@ var P1 = Fc((L) => {
28498
28524
  return r;
28499
28525
  }
28500
28526
  /**
28501
- * SHX font always has fixed scale factor 1.
28502
- * @returns Always return value 1
28527
+ * Scale factor that converts AutoCAD's `TEXT.height` (cap-height in the SHX
28528
+ * convention) into the em-box-scaled size expected by `generateShapes`.
28529
+ *
28530
+ * SHX glyphs are defined with `baseUp` units of cap-height (height of an
28531
+ * uppercase letter above the baseline) and `height` units of total em-box
28532
+ * (cap-height + descender + headroom for accents). AutoCAD's DXF group 40
28533
+ * for TEXT/ATTRIB/MTEXT is the cap-height — the historical SHX convention
28534
+ * since the format's introduction in AutoCAD R2 (1985), kept by AutoCAD
28535
+ * when TrueType support was added in the early 1990s so that DWGs portable
28536
+ * across font formats render at consistent visual sizes.
28537
+ *
28538
+ * Returning `1` (the previous behavior) meant SHX glyphs were rendered at
28539
+ * `baseUp / height` of the expected size — typically ~0.75 for western
28540
+ * fonts like `romans.shx`/`complex.shx` where `baseUp=21, height=28`.
28541
+ * Multiplying by `height / baseUp` restores the cap-height interpretation.
28542
+ *
28543
+ * Mirrors the behavior already in place for mesh fonts (see
28544
+ * `meshFontParser.ts` where `scaleFactor = unitsPerEm / glyph('A').yMax`),
28545
+ * isolated to the glyph render path only — `mtextProcessor.currentLayoutFontSize`
28546
+ * divides by this factor to keep layout metrics (line height, attachment
28547
+ * offsets, blank width) on the original cap-height scale.
28548
+ *
28549
+ * @returns `height / baseUp` when both are populated and positive; `1`
28550
+ * otherwise (e.g. exotic BIGFONT/symbol fonts without standard metrics),
28551
+ * matching the prior behavior as a safe fallback.
28503
28552
  */
28504
28553
  getScaleFactor() {
28505
- return 1;
28554
+ const { height: e, baseUp: t } = this.data.content;
28555
+ return t > 0 && e > 0 ? e / t : 1;
28506
28556
  }
28507
28557
  /**
28508
28558
  * Gets the shape data for a specific character at a given size.
@@ -29203,6 +29253,10 @@ var P1 = Fc((L) => {
29203
29253
  get currentWidthFactor() {
29204
29254
  return this._currentContext.widthFactor.value;
29205
29255
  }
29256
+ /** Horizontal advance for one space, including tracking and width factor. */
29257
+ get currentBlankAdvance() {
29258
+ return this._currentContext.blankWidth * this.currentWordSpace * this.currentWidthFactor;
29259
+ }
29206
29260
  /**
29207
29261
  * All of THREE.js objects in current line. It contains objects in all of sections of this line.
29208
29262
  */
@@ -29535,7 +29589,7 @@ var P1 = Fc((L) => {
29535
29589
  let a = 0;
29536
29590
  for (let o = 0; o < e.length; o++) {
29537
29591
  const h = this.getCharShape(e[o]);
29538
- h ? this.currentHorizontalAlignment == ce.DISTRIBUTED ? a += h.width * this.currentWidthFactor : a += h.width * this.currentWordSpace * this.currentWidthFactor : a += this._currentContext.blankWidth;
29592
+ h ? this.currentHorizontalAlignment == ce.DISTRIBUTED ? a += h.width * this.currentWidthFactor : a += h.width * this.currentWordSpace * this.currentWidthFactor : a += this.currentBlankAdvance;
29539
29593
  }
29540
29594
  this.hOffset + a > (this.maxLineWidth || 1 / 0) && (this._vOffset <= 0 && this._currentLineObjects.length <= 0 || (this.recordVisualLineBreak(i, s), this.advanceToNextLine(!1)));
29541
29595
  for (let o = 0; o < e.length; o++)
@@ -29680,7 +29734,7 @@ var P1 = Fc((L) => {
29680
29734
  const r = this._hOffset, i = this.flowDirection == Mt.BOTTOM_TO_TOP ? this._vOffset : this._vOffset - this.currentLayoutFontSize, s = new ke(
29681
29735
  new E(r, i, 0),
29682
29736
  new E(
29683
- r + this._currentContext.blankWidth,
29737
+ r + this.currentBlankAdvance,
29684
29738
  i + this.currentLayoutFontSize,
29685
29739
  0
29686
29740
  )
@@ -29697,7 +29751,7 @@ var P1 = Fc((L) => {
29697
29751
  children: []
29698
29752
  });
29699
29753
  }
29700
- this._hOffset += this._currentContext.blankWidth;
29754
+ this._hOffset += this.currentBlankAdvance;
29701
29755
  }
29702
29756
  recordVisualLineBreak(e, t) {
29703
29757
  const r = this._processedCharCount + this.countFinalCharBoxes(
@@ -29723,12 +29777,15 @@ var P1 = Fc((L) => {
29723
29777
  this._lineHasRenderableChar || this.applyPendingEmptyLineYAdjust();
29724
29778
  const o = a.toGeometry();
29725
29779
  o.scale(this.currentWidthFactor, 1, 1);
29726
- let h = this._currentContext.oblique;
29727
- if (this._currentContext.italic && (h += 15), h) {
29728
- const x = h * Math.PI / 180, v = new Fe();
29729
- v.set(
29780
+ const h = this.currentLayoutFontSize;
29781
+ let c = this._currentContext.oblique;
29782
+ this._currentContext.italic && (c += 15);
29783
+ let u = 0;
29784
+ if (c) {
29785
+ const w = c * Math.PI / 180, b = new Fe();
29786
+ b.set(
29730
29787
  1,
29731
- Math.tan(x),
29788
+ Math.tan(w),
29732
29789
  0,
29733
29790
  0,
29734
29791
  0,
@@ -29743,77 +29800,79 @@ var P1 = Fc((L) => {
29743
29800
  0,
29744
29801
  0,
29745
29802
  1
29746
- ), o.applyMatrix4(v);
29747
- }
29748
- const c = this.fontManager.getFontType(this.currentFont);
29749
- this._currentContext.bold && c === "mesh" && o.scale(1.06, 1.06, 1), this.hOffset > (this.maxLineWidth || 1 / 0) && (this.recordVisualLineBreak(i, s), this.advanceToNextLine(!1));
29750
- const u = this.hOffset, f = this.flowDirection == Mt.BOTTOM_TO_TOP ? this.vOffset : this.vOffset - this.currentLayoutFontSize, l = a.width * this.currentWidthFactor, p = this.currentLayoutFontSize;
29751
- if (o.translate(u, f, 0), this.currentHorizontalAlignment == ce.DISTRIBUTED ? this._hOffset += a.width * this.currentWidthFactor : this._hOffset += a.width * this.currentWordSpace * this.currentWidthFactor, t.push(o), this._lineHasRenderableChar = !0, this._options.collectCharBoxes !== !1) {
29803
+ ), o.applyMatrix4(b), u = Math.tan(w) * h;
29804
+ }
29805
+ const f = this.fontManager.getFontType(this.currentFont);
29806
+ this._currentContext.bold && f === "mesh" && o.scale(1.06, 1.06, 1), this.hOffset > (this.maxLineWidth || 1 / 0) && (this.recordVisualLineBreak(i, s), this.advanceToNextLine(!1));
29807
+ const l = this.hOffset, p = this.flowDirection == Mt.BOTTOM_TO_TOP ? this.vOffset : this.vOffset - this.currentLayoutFontSize, d = a.width * this.currentWidthFactor;
29808
+ o.translate(l, p, 0);
29809
+ const m = a.width * this.currentWidthFactor + u * this.currentWidthFactor;
29810
+ if (this.currentHorizontalAlignment == ce.DISTRIBUTED ? this._hOffset += m : this._hOffset += a.width * this.currentWordSpace * this.currentWidthFactor + u * this.currentWidthFactor, t.push(o), this._lineHasRenderableChar = !0, this._options.collectCharBoxes !== !1) {
29752
29811
  o.userData.char = e, o.boundingBox || o.computeBoundingBox();
29753
- const x = new ke().copy(o.boundingBox);
29812
+ const w = new ke().copy(o.boundingBox);
29754
29813
  o instanceof mr ? (this._lastCharBoxTarget = "mesh", i.push({
29755
29814
  type: ye.CHAR,
29756
- box: x,
29815
+ box: w,
29757
29816
  char: e,
29758
29817
  children: []
29759
29818
  })) : (this._lastCharBoxTarget = "line", s.push({
29760
29819
  type: ye.CHAR,
29761
- box: x,
29820
+ box: w,
29762
29821
  char: e,
29763
29822
  children: []
29764
29823
  }));
29765
29824
  }
29766
- const d = p * 0.05, m = 1e-3;
29825
+ const x = h * 0.05, v = 1e-3;
29767
29826
  if (this._currentContext.underline) {
29768
- const x = new ze(), v = f - d;
29769
- x.setAttribute(
29827
+ const w = new ze(), b = p - x;
29828
+ w.setAttribute(
29770
29829
  "position",
29771
29830
  new Ye(
29772
29831
  new Float32Array([
29773
- u,
29774
- v,
29775
- m,
29776
- u + l,
29832
+ l,
29833
+ b,
29777
29834
  v,
29778
- m
29835
+ l + d,
29836
+ b,
29837
+ v
29779
29838
  ]),
29780
29839
  3
29781
29840
  )
29782
- ), x.setIndex(null), x.userData = { isDecoration: !0 }, r.push(x);
29841
+ ), w.setIndex(null), w.userData = { isDecoration: !0 }, r.push(w);
29783
29842
  }
29784
29843
  if (this._currentContext.overline) {
29785
- const x = new ze(), v = f + p + d;
29786
- x.setAttribute(
29844
+ const w = new ze(), b = p + h + x;
29845
+ w.setAttribute(
29787
29846
  "position",
29788
29847
  new Ye(
29789
29848
  new Float32Array([
29790
- u,
29791
- v,
29792
- m,
29793
- u + l,
29849
+ l,
29850
+ b,
29794
29851
  v,
29795
- m
29852
+ l + d,
29853
+ b,
29854
+ v
29796
29855
  ]),
29797
29856
  3
29798
29857
  )
29799
- ), x.setIndex(null), x.userData = { isDecoration: !0 }, r.push(x);
29858
+ ), w.setIndex(null), w.userData = { isDecoration: !0 }, r.push(w);
29800
29859
  }
29801
29860
  if (this._currentContext.strikeThrough) {
29802
- const x = new ze(), v = f + p / 2 - p * 0.2;
29803
- x.setAttribute(
29861
+ const w = new ze(), b = p + h / 2 - h * 0.2;
29862
+ w.setAttribute(
29804
29863
  "position",
29805
29864
  new Ye(
29806
29865
  new Float32Array([
29807
- u,
29808
- v,
29809
- m,
29810
- u + l,
29866
+ l,
29867
+ b,
29811
29868
  v,
29812
- m
29869
+ l + d,
29870
+ b,
29871
+ v
29813
29872
  ]),
29814
29873
  3
29815
29874
  )
29816
- ), x.setIndex(null), x.userData = { isDecoration: !0 }, r.push(x);
29875
+ ), w.setIndex(null), w.userData = { isDecoration: !0 }, r.push(w);
29817
29876
  }
29818
29877
  }
29819
29878
  processLastLine() {
@@ -29946,17 +30005,15 @@ var P1 = Fc((L) => {
29946
30005
  }
29947
30006
  }
29948
30007
  /**
29949
- * In AutoCAD, the width of a regular space character (ASCII 32, the space key on the keyboard) in MText
29950
- * depends on the current font and text height, and is not a fixed value.
29951
- * Specifically:
29952
- * - Space width Text height × space width ratio defined by the font
29953
- * - For common TrueType fonts (like Arial), the space width is typically about 1/4 to 1/3 of the text height.
29954
- * For example, if the text height is 10 (units), the space width would be approximately 2.5 to 3.3 units.
29955
- * - For SHX fonts (AutoCAD's built-in vector fonts, such as txt.shx), the space width is often half the text height.
29956
- * So if the text height is 10, the space width is typically 5 units.
30008
+ * Resolves horizontal advance for an ASCII space in the active font.
30009
+ *
30010
+ * AutoCAD uses each font's own space glyph metrics (SHX pen advance or TrueType
30011
+ * horizontal advance). A fixed fraction of text height (e.g. 50% for SHX) is only
30012
+ * a rough fallback when the font has no space definition.
29957
30013
  */
29958
30014
  calculateBlankWidthForFont(e, t) {
29959
- return this.fontManager.getFontType(e) === "shx" ? t * 0.5 : t * 0.3;
30015
+ const r = this.fontManager.getCharShape(" ", e, t);
30016
+ return r && r.width > 0 ? r.width : this.fontManager.getFontType(e) === "shx" ? t * 0.5 : t * 0.3;
29960
30017
  }
29961
30018
  /**
29962
30019
  * Convert the text shape geometries to three.js object
@@ -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
  /**
@@ -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.14",
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>",