@mlightcad/libredwg-web 0.7.9 → 0.7.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 CHANGED
@@ -53,7 +53,7 @@ pnpm build
53
53
  In order to reduce the size of wasm file, the following functionalities are not included by default when building web assembly.
54
54
 
55
55
  - write dwg file
56
- - read/write dxf file
56
+ - write dxf file
57
57
  - import/export json file
58
58
 
59
59
  If you want those functionalities, just modify command `build:prepare` defined in [package.json](./package.json) and remove the following options.
@@ -94,8 +94,9 @@ const encodings = [
94
94
  "macintosh",
95
95
  // 23
96
96
  "big5",
97
- "utf-8",
98
- // Korean (Wansung + Johab)
97
+ // WHATWG 'euc-kr' decodes windows-949, which covers CP949 (Wansung + UHC).
98
+ "euc-kr",
99
+ // Korean (CP949 / Wansung + UHC)
99
100
  "utf-8",
100
101
  // Johab?
101
102
  "ibm866",
@@ -529,6 +530,16 @@ var DwgDictionaryCloningFlags = /* @__PURE__ */ ((DwgDictionaryCloningFlags2) =>
529
530
  DwgDictionaryCloningFlags2[DwgDictionaryCloningFlags2["UnmangleName"] = 5] = "UnmangleName";
530
531
  return DwgDictionaryCloningFlags2;
531
532
  })(DwgDictionaryCloningFlags || {});
533
+ const UNICODE_ESCAPE = /\\U\+([0-9A-Fa-f]{4})/g;
534
+ function decodeUnicodeEscapes(text) {
535
+ if (!text || !text.includes("\\U+")) {
536
+ return text;
537
+ }
538
+ return text.replace(
539
+ UNICODE_ESCAPE,
540
+ (_, hex) => String.fromCharCode(parseInt(hex, 16))
541
+ );
542
+ }
532
543
  const dwgVersions = [
533
544
  {
534
545
  type: "invalid",
@@ -2701,7 +2712,7 @@ class LibreEntityConverter {
2701
2712
  const elevation = libredwg.dwg_dynapi_entity_data(entity, "elevation");
2702
2713
  const thickness = libredwg.dwg_dynapi_entity_data(entity, "thickness");
2703
2714
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, "extrusion");
2704
- const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object);
2715
+ const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object) ?? [];
2705
2716
  return {
2706
2717
  type: "POLYLINE2D",
2707
2718
  ...commonAttrs,
@@ -2737,7 +2748,7 @@ class LibreEntityConverter {
2737
2748
  const startWidth = libredwg.dwg_dynapi_entity_data(entity, "start_width");
2738
2749
  const endWidth = libredwg.dwg_dynapi_entity_data(entity, "end_width");
2739
2750
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, "extrusion");
2740
- const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object);
2751
+ const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object) ?? [];
2741
2752
  return {
2742
2753
  type: "POLYLINE3D",
2743
2754
  ...commonAttrs,
@@ -3341,12 +3352,24 @@ class LibreDwgConverter {
3341
3352
  constructor(instance) {
3342
3353
  __publicField(this, "libredwg");
3343
3354
  __publicField(this, "entityConverter");
3355
+ /**
3356
+ * Entity object indices keyed by owning BLOCK_HEADER object pointer.
3357
+ * Built lazily when an ownership walk returns no entities (LibreDWG
3358
+ * https://github.com/LibreDWG/libredwg/issues/1199).
3359
+ */
3360
+ __publicField(this, "orphanEntitiesByBlock", null);
3361
+ /** Entity indices whose ownerhandle does not resolve to any object. */
3362
+ __publicField(this, "ownerlessOrphanIndices", null);
3363
+ __publicField(this, "orphanIndexDataPtr", null);
3344
3364
  this.libredwg = instance;
3345
3365
  this.entityConverter = new LibreEntityConverter(instance);
3346
3366
  }
3347
3367
  convert(data) {
3348
3368
  var _a;
3349
3369
  this.entityConverter.clear();
3370
+ this.orphanEntitiesByBlock = null;
3371
+ this.ownerlessOrphanIndices = null;
3372
+ this.orphanIndexDataPtr = data;
3350
3373
  const db = {
3351
3374
  tables: {
3352
3375
  APPID: {
@@ -3600,6 +3623,26 @@ class LibreDwgConverter {
3600
3623
  }
3601
3624
  }
3602
3625
  }
3626
+ if (this.orphanIndexDataPtr != null) {
3627
+ const orphans = this.convertOrphanOwnedEntities(
3628
+ this.orphanIndexDataPtr,
3629
+ obj,
3630
+ commonAttrs.handle,
3631
+ isModelSpace(commonAttrs.name) === true
3632
+ );
3633
+ if (orphans.length) {
3634
+ if (entities.length === 0) {
3635
+ entities = orphans;
3636
+ } else {
3637
+ const seen = new Set(entities.map((e) => e.handle));
3638
+ for (const entity of orphans) {
3639
+ if (!seen.has(entity.handle)) {
3640
+ entities.push(entity);
3641
+ }
3642
+ }
3643
+ }
3644
+ }
3645
+ }
3603
3646
  return {
3604
3647
  ...commonAttrs,
3605
3648
  flags,
@@ -3628,6 +3671,89 @@ class LibreDwgConverter {
3628
3671
  }
3629
3672
  return entities;
3630
3673
  }
3674
+ /**
3675
+ * Builds (once) a map of BLOCK_HEADER object pointer → entity object indices
3676
+ * for entities that are not reachable through the ownership chain, then
3677
+ * converts the entities owned by `blockHeaderPtr`.
3678
+ *
3679
+ * @param includeOwnerless - When true (only for *Model_Space), also convert
3680
+ * entities that still have no resolvable owner after ownerhandle + entmode
3681
+ * fallback via dwg_entity_owner.
3682
+ */
3683
+ convertOrphanOwnedEntities(data, blockHeaderPtr, ownerHandle, includeOwnerless) {
3684
+ this.ensureOrphanEntityIndex(data);
3685
+ const byBlock = this.orphanEntitiesByBlock ?? /* @__PURE__ */ new Map();
3686
+ const owned = byBlock.get(blockHeaderPtr) ?? [];
3687
+ const ownerless = includeOwnerless && this.ownerlessOrphanIndices ? this.ownerlessOrphanIndices : [];
3688
+ const indices = owned.length && ownerless.length ? owned.concat(ownerless) : owned.length ? owned : ownerless;
3689
+ if (indices.length === 0) {
3690
+ return [];
3691
+ }
3692
+ const libredwg = this.libredwg;
3693
+ const converter = this.entityConverter;
3694
+ const entities = [];
3695
+ for (const index of indices) {
3696
+ const object = libredwg.dwg_get_object(data, index);
3697
+ const entity = converter.convert(object);
3698
+ if (entity) {
3699
+ entity.ownerBlockRecordSoftId = ownerHandle;
3700
+ entities.push(entity);
3701
+ }
3702
+ }
3703
+ return entities;
3704
+ }
3705
+ ensureOrphanEntityIndex(data) {
3706
+ if (this.orphanEntitiesByBlock) {
3707
+ return;
3708
+ }
3709
+ const libredwg = this.libredwg;
3710
+ const numObjects = libredwg.dwg_get_num_objects(data);
3711
+ const indexByEntityPtr = /* @__PURE__ */ new Map();
3712
+ const entityIndices = [];
3713
+ const blockHeaderPtrs = [];
3714
+ const blockObjByTio = /* @__PURE__ */ new Map();
3715
+ for (let i = 0; i < numObjects; i++) {
3716
+ const obj = libredwg.dwg_get_object(data, i);
3717
+ if (!obj) continue;
3718
+ if (libredwg.dwg_object_get_supertype(obj) === Dwg_Object_Supertype.DWG_SUPERTYPE_ENTITY) {
3719
+ entityIndices.push(i);
3720
+ indexByEntityPtr.set(obj, i);
3721
+ } else if (libredwg.dwg_object_get_fixedtype(obj) === Dwg_Object_Type.DWG_TYPE_BLOCK_HEADER) {
3722
+ blockHeaderPtrs.push(obj);
3723
+ const tio = libredwg.dwg_object_to_object_tio(obj);
3724
+ if (tio) blockObjByTio.set(tio, obj);
3725
+ }
3726
+ }
3727
+ const claimed = /* @__PURE__ */ new Set();
3728
+ for (const blockHeader of blockHeaderPtrs) {
3729
+ let current = libredwg.get_first_owned_entity(blockHeader);
3730
+ let steps = 0;
3731
+ while (current && steps++ <= entityIndices.length) {
3732
+ const index = indexByEntityPtr.get(current);
3733
+ if (index === void 0 || claimed.has(index)) break;
3734
+ claimed.add(index);
3735
+ current = libredwg.get_next_owned_entity(blockHeader, current);
3736
+ }
3737
+ }
3738
+ const orphansByBlock = /* @__PURE__ */ new Map();
3739
+ const ownerless = [];
3740
+ for (const index of entityIndices) {
3741
+ if (claimed.has(index)) continue;
3742
+ const obj = libredwg.dwg_get_object(data, index);
3743
+ const etio = obj ? libredwg.dwg_object_to_entity_tio(obj) : 0;
3744
+ const ownerTio = etio ? libredwg.dwg_entity_owner(etio) : 0;
3745
+ const owner = ownerTio ? blockObjByTio.get(ownerTio) ?? 0 : 0;
3746
+ if (owner) {
3747
+ const owned = orphansByBlock.get(owner);
3748
+ if (owned) owned.push(index);
3749
+ else orphansByBlock.set(owner, [index]);
3750
+ } else {
3751
+ ownerless.push(index);
3752
+ }
3753
+ }
3754
+ this.orphanEntitiesByBlock = orphansByBlock;
3755
+ this.ownerlessOrphanIndices = ownerless;
3756
+ }
3631
3757
  convertDimStyle(item, obj) {
3632
3758
  const libredwg = this.libredwg;
3633
3759
  const commonAttrs = this.getCommonTableEntryAttrs(item, obj);
@@ -6449,6 +6575,9 @@ const _LibreDwg = class _LibreDwg {
6449
6575
  if (value.bin && this.decoder) {
6450
6576
  value.data = this.decoder.decode(value.bin);
6451
6577
  }
6578
+ if (typeof value.data === "string") {
6579
+ value.data = decodeUnicodeEscapes(value.data);
6580
+ }
6452
6581
  return value;
6453
6582
  }
6454
6583
  /**
@@ -6502,7 +6631,18 @@ const _LibreDwg = class _LibreDwg {
6502
6631
  * @returns Returns the sub-field value of one complex field.
6503
6632
  */
6504
6633
  dwg_dynapi_subclass_value(obj, subclass, field) {
6505
- return this.wasmInstance.dwg_dynapi_subclass_value(obj, subclass, field);
6634
+ const value = this.wasmInstance.dwg_dynapi_subclass_value(
6635
+ obj,
6636
+ subclass,
6637
+ field
6638
+ );
6639
+ if (value.bin && this.decoder) {
6640
+ value.data = this.decoder.decode(value.bin);
6641
+ }
6642
+ if (typeof value.data === "string") {
6643
+ value.data = decodeUnicodeEscapes(value.data);
6644
+ }
6645
+ return value;
6506
6646
  }
6507
6647
  /**
6508
6648
  * Returns the sub-field data of one complex field.
@@ -6938,6 +7078,7 @@ export {
6938
7078
  HEADER_VARIABLES,
6939
7079
  LibreDwg,
6940
7080
  default2 as createModule,
7081
+ decodeUnicodeEscapes,
6941
7082
  dwgCodePageToEncoding,
6942
7083
  dwgVersions
6943
7084
  };
@@ -96,8 +96,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
96
96
  "macintosh",
97
97
  // 23
98
98
  "big5",
99
- "utf-8",
100
- // Korean (Wansung + Johab)
99
+ // WHATWG 'euc-kr' decodes windows-949, which covers CP949 (Wansung + UHC).
100
+ "euc-kr",
101
+ // Korean (CP949 / Wansung + UHC)
101
102
  "utf-8",
102
103
  // Johab?
103
104
  "ibm866",
@@ -531,6 +532,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
531
532
  DwgDictionaryCloningFlags2[DwgDictionaryCloningFlags2["UnmangleName"] = 5] = "UnmangleName";
532
533
  return DwgDictionaryCloningFlags2;
533
534
  })(DwgDictionaryCloningFlags || {});
535
+ const UNICODE_ESCAPE = /\\U\+([0-9A-Fa-f]{4})/g;
536
+ function decodeUnicodeEscapes(text) {
537
+ if (!text || !text.includes("\\U+")) {
538
+ return text;
539
+ }
540
+ return text.replace(
541
+ UNICODE_ESCAPE,
542
+ (_, hex) => String.fromCharCode(parseInt(hex, 16))
543
+ );
544
+ }
534
545
  const dwgVersions = [
535
546
  {
536
547
  type: "invalid",
@@ -2703,7 +2714,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
2703
2714
  const elevation = libredwg.dwg_dynapi_entity_data(entity, "elevation");
2704
2715
  const thickness = libredwg.dwg_dynapi_entity_data(entity, "thickness");
2705
2716
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, "extrusion");
2706
- const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object);
2717
+ const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object) ?? [];
2707
2718
  return {
2708
2719
  type: "POLYLINE2D",
2709
2720
  ...commonAttrs,
@@ -2739,7 +2750,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
2739
2750
  const startWidth = libredwg.dwg_dynapi_entity_data(entity, "start_width");
2740
2751
  const endWidth = libredwg.dwg_dynapi_entity_data(entity, "end_width");
2741
2752
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, "extrusion");
2742
- const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object);
2753
+ const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object) ?? [];
2743
2754
  return {
2744
2755
  type: "POLYLINE3D",
2745
2756
  ...commonAttrs,
@@ -3343,12 +3354,24 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3343
3354
  constructor(instance) {
3344
3355
  __publicField(this, "libredwg");
3345
3356
  __publicField(this, "entityConverter");
3357
+ /**
3358
+ * Entity object indices keyed by owning BLOCK_HEADER object pointer.
3359
+ * Built lazily when an ownership walk returns no entities (LibreDWG
3360
+ * https://github.com/LibreDWG/libredwg/issues/1199).
3361
+ */
3362
+ __publicField(this, "orphanEntitiesByBlock", null);
3363
+ /** Entity indices whose ownerhandle does not resolve to any object. */
3364
+ __publicField(this, "ownerlessOrphanIndices", null);
3365
+ __publicField(this, "orphanIndexDataPtr", null);
3346
3366
  this.libredwg = instance;
3347
3367
  this.entityConverter = new LibreEntityConverter(instance);
3348
3368
  }
3349
3369
  convert(data) {
3350
3370
  var _a;
3351
3371
  this.entityConverter.clear();
3372
+ this.orphanEntitiesByBlock = null;
3373
+ this.ownerlessOrphanIndices = null;
3374
+ this.orphanIndexDataPtr = data;
3352
3375
  const db = {
3353
3376
  tables: {
3354
3377
  APPID: {
@@ -3602,6 +3625,26 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3602
3625
  }
3603
3626
  }
3604
3627
  }
3628
+ if (this.orphanIndexDataPtr != null) {
3629
+ const orphans = this.convertOrphanOwnedEntities(
3630
+ this.orphanIndexDataPtr,
3631
+ obj,
3632
+ commonAttrs.handle,
3633
+ isModelSpace(commonAttrs.name) === true
3634
+ );
3635
+ if (orphans.length) {
3636
+ if (entities.length === 0) {
3637
+ entities = orphans;
3638
+ } else {
3639
+ const seen = new Set(entities.map((e) => e.handle));
3640
+ for (const entity of orphans) {
3641
+ if (!seen.has(entity.handle)) {
3642
+ entities.push(entity);
3643
+ }
3644
+ }
3645
+ }
3646
+ }
3647
+ }
3605
3648
  return {
3606
3649
  ...commonAttrs,
3607
3650
  flags,
@@ -3630,6 +3673,89 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3630
3673
  }
3631
3674
  return entities;
3632
3675
  }
3676
+ /**
3677
+ * Builds (once) a map of BLOCK_HEADER object pointer → entity object indices
3678
+ * for entities that are not reachable through the ownership chain, then
3679
+ * converts the entities owned by `blockHeaderPtr`.
3680
+ *
3681
+ * @param includeOwnerless - When true (only for *Model_Space), also convert
3682
+ * entities that still have no resolvable owner after ownerhandle + entmode
3683
+ * fallback via dwg_entity_owner.
3684
+ */
3685
+ convertOrphanOwnedEntities(data, blockHeaderPtr, ownerHandle, includeOwnerless) {
3686
+ this.ensureOrphanEntityIndex(data);
3687
+ const byBlock = this.orphanEntitiesByBlock ?? /* @__PURE__ */ new Map();
3688
+ const owned = byBlock.get(blockHeaderPtr) ?? [];
3689
+ const ownerless = includeOwnerless && this.ownerlessOrphanIndices ? this.ownerlessOrphanIndices : [];
3690
+ const indices = owned.length && ownerless.length ? owned.concat(ownerless) : owned.length ? owned : ownerless;
3691
+ if (indices.length === 0) {
3692
+ return [];
3693
+ }
3694
+ const libredwg = this.libredwg;
3695
+ const converter = this.entityConverter;
3696
+ const entities = [];
3697
+ for (const index of indices) {
3698
+ const object = libredwg.dwg_get_object(data, index);
3699
+ const entity = converter.convert(object);
3700
+ if (entity) {
3701
+ entity.ownerBlockRecordSoftId = ownerHandle;
3702
+ entities.push(entity);
3703
+ }
3704
+ }
3705
+ return entities;
3706
+ }
3707
+ ensureOrphanEntityIndex(data) {
3708
+ if (this.orphanEntitiesByBlock) {
3709
+ return;
3710
+ }
3711
+ const libredwg = this.libredwg;
3712
+ const numObjects = libredwg.dwg_get_num_objects(data);
3713
+ const indexByEntityPtr = /* @__PURE__ */ new Map();
3714
+ const entityIndices = [];
3715
+ const blockHeaderPtrs = [];
3716
+ const blockObjByTio = /* @__PURE__ */ new Map();
3717
+ for (let i = 0; i < numObjects; i++) {
3718
+ const obj = libredwg.dwg_get_object(data, i);
3719
+ if (!obj) continue;
3720
+ if (libredwg.dwg_object_get_supertype(obj) === Dwg_Object_Supertype.DWG_SUPERTYPE_ENTITY) {
3721
+ entityIndices.push(i);
3722
+ indexByEntityPtr.set(obj, i);
3723
+ } else if (libredwg.dwg_object_get_fixedtype(obj) === Dwg_Object_Type.DWG_TYPE_BLOCK_HEADER) {
3724
+ blockHeaderPtrs.push(obj);
3725
+ const tio = libredwg.dwg_object_to_object_tio(obj);
3726
+ if (tio) blockObjByTio.set(tio, obj);
3727
+ }
3728
+ }
3729
+ const claimed = /* @__PURE__ */ new Set();
3730
+ for (const blockHeader of blockHeaderPtrs) {
3731
+ let current = libredwg.get_first_owned_entity(blockHeader);
3732
+ let steps = 0;
3733
+ while (current && steps++ <= entityIndices.length) {
3734
+ const index = indexByEntityPtr.get(current);
3735
+ if (index === void 0 || claimed.has(index)) break;
3736
+ claimed.add(index);
3737
+ current = libredwg.get_next_owned_entity(blockHeader, current);
3738
+ }
3739
+ }
3740
+ const orphansByBlock = /* @__PURE__ */ new Map();
3741
+ const ownerless = [];
3742
+ for (const index of entityIndices) {
3743
+ if (claimed.has(index)) continue;
3744
+ const obj = libredwg.dwg_get_object(data, index);
3745
+ const etio = obj ? libredwg.dwg_object_to_entity_tio(obj) : 0;
3746
+ const ownerTio = etio ? libredwg.dwg_entity_owner(etio) : 0;
3747
+ const owner = ownerTio ? blockObjByTio.get(ownerTio) ?? 0 : 0;
3748
+ if (owner) {
3749
+ const owned = orphansByBlock.get(owner);
3750
+ if (owned) owned.push(index);
3751
+ else orphansByBlock.set(owner, [index]);
3752
+ } else {
3753
+ ownerless.push(index);
3754
+ }
3755
+ }
3756
+ this.orphanEntitiesByBlock = orphansByBlock;
3757
+ this.ownerlessOrphanIndices = ownerless;
3758
+ }
3633
3759
  convertDimStyle(item, obj) {
3634
3760
  const libredwg = this.libredwg;
3635
3761
  const commonAttrs = this.getCommonTableEntryAttrs(item, obj);
@@ -6451,6 +6577,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6451
6577
  if (value.bin && this.decoder) {
6452
6578
  value.data = this.decoder.decode(value.bin);
6453
6579
  }
6580
+ if (typeof value.data === "string") {
6581
+ value.data = decodeUnicodeEscapes(value.data);
6582
+ }
6454
6583
  return value;
6455
6584
  }
6456
6585
  /**
@@ -6504,7 +6633,18 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6504
6633
  * @returns Returns the sub-field value of one complex field.
6505
6634
  */
6506
6635
  dwg_dynapi_subclass_value(obj, subclass, field) {
6507
- return this.wasmInstance.dwg_dynapi_subclass_value(obj, subclass, field);
6636
+ const value = this.wasmInstance.dwg_dynapi_subclass_value(
6637
+ obj,
6638
+ subclass,
6639
+ field
6640
+ );
6641
+ if (value.bin && this.decoder) {
6642
+ value.data = this.decoder.decode(value.bin);
6643
+ }
6644
+ if (typeof value.data === "string") {
6645
+ value.data = decodeUnicodeEscapes(value.data);
6646
+ }
6647
+ return value;
6508
6648
  }
6509
6649
  /**
6510
6650
  * Returns the sub-field data of one complex field.
@@ -6939,6 +7079,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6939
7079
  exports2.Dwg_Object_Type_Inverted = Dwg_Object_Type_Inverted;
6940
7080
  exports2.HEADER_VARIABLES = HEADER_VARIABLES;
6941
7081
  exports2.LibreDwg = LibreDwg;
7082
+ exports2.decodeUnicodeEscapes = decodeUnicodeEscapes;
6942
7083
  exports2.dwgCodePageToEncoding = dwgCodePageToEncoding;
6943
7084
  exports2.dwgVersions = dwgVersions;
6944
7085
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
@@ -7,6 +7,15 @@ import { Dwg_Data_Ptr } from '../types';
7
7
  export declare class LibreDwgConverter {
8
8
  private libredwg;
9
9
  private entityConverter;
10
+ /**
11
+ * Entity object indices keyed by owning BLOCK_HEADER object pointer.
12
+ * Built lazily when an ownership walk returns no entities (LibreDWG
13
+ * https://github.com/LibreDWG/libredwg/issues/1199).
14
+ */
15
+ private orphanEntitiesByBlock;
16
+ /** Entity indices whose ownerhandle does not resolve to any object. */
17
+ private ownerlessOrphanIndices;
18
+ private orphanIndexDataPtr;
10
19
  constructor(instance: LibreDwgEx);
11
20
  convert(data: Dwg_Data_Ptr): DwgDatabase;
12
21
  getConversionStats(): {
@@ -18,6 +27,17 @@ export declare class LibreDwgConverter {
18
27
  private convertAppId;
19
28
  private convertBlockRecord;
20
29
  private convertEntities;
30
+ /**
31
+ * Builds (once) a map of BLOCK_HEADER object pointer → entity object indices
32
+ * for entities that are not reachable through the ownership chain, then
33
+ * converts the entities owned by `blockHeaderPtr`.
34
+ *
35
+ * @param includeOwnerless - When true (only for *Model_Space), also convert
36
+ * entities that still have no resolvable owner after ownerhandle + entmode
37
+ * fallback via dwg_entity_owner.
38
+ */
39
+ private convertOrphanOwnedEntities;
40
+ private ensureOrphanEntityIndex;
21
41
  private convertDimStyle;
22
42
  private convertLayer;
23
43
  private convertLineType;
@@ -9,12 +9,24 @@ import { idToString, isModelSpace, isPaperSpace, uint8ArrayToHexString } from '.
9
9
  export class LibreDwgConverter {
10
10
  libredwg;
11
11
  entityConverter;
12
+ /**
13
+ * Entity object indices keyed by owning BLOCK_HEADER object pointer.
14
+ * Built lazily when an ownership walk returns no entities (LibreDWG
15
+ * https://github.com/LibreDWG/libredwg/issues/1199).
16
+ */
17
+ orphanEntitiesByBlock = null;
18
+ /** Entity indices whose ownerhandle does not resolve to any object. */
19
+ ownerlessOrphanIndices = null;
20
+ orphanIndexDataPtr = null;
12
21
  constructor(instance) {
13
22
  this.libredwg = instance;
14
23
  this.entityConverter = new LibreEntityConverter(instance);
15
24
  }
16
25
  convert(data) {
17
26
  this.entityConverter.clear();
27
+ this.orphanEntitiesByBlock = null;
28
+ this.ownerlessOrphanIndices = null;
29
+ this.orphanIndexDataPtr = data;
18
30
  const db = {
19
31
  tables: {
20
32
  APPID: {
@@ -287,6 +299,27 @@ export class LibreDwgConverter {
287
299
  }
288
300
  }
289
301
  }
302
+ // Some DWG writers (including LibreDWG's own) declare first_entity /
303
+ // last_entity handles that resolve to no object, or only link a
304
+ // partial ownership chain. Fall back to entity objects whose owner
305
+ // (ownerhandle / entmode via dwg_entity_owner) names this block.
306
+ // For *Model_Space only, also pick up entities with no resolvable owner.
307
+ if (this.orphanIndexDataPtr != null) {
308
+ const orphans = this.convertOrphanOwnedEntities(this.orphanIndexDataPtr, obj, commonAttrs.handle, isModelSpace(commonAttrs.name) === true);
309
+ if (orphans.length) {
310
+ if (entities.length === 0) {
311
+ entities = orphans;
312
+ }
313
+ else {
314
+ const seen = new Set(entities.map(e => e.handle));
315
+ for (const entity of orphans) {
316
+ if (!seen.has(entity.handle)) {
317
+ entities.push(entity);
318
+ }
319
+ }
320
+ }
321
+ }
322
+ }
290
323
  return {
291
324
  ...commonAttrs,
292
325
  flags: flags,
@@ -315,6 +348,108 @@ export class LibreDwgConverter {
315
348
  }
316
349
  return entities;
317
350
  }
351
+ /**
352
+ * Builds (once) a map of BLOCK_HEADER object pointer → entity object indices
353
+ * for entities that are not reachable through the ownership chain, then
354
+ * converts the entities owned by `blockHeaderPtr`.
355
+ *
356
+ * @param includeOwnerless - When true (only for *Model_Space), also convert
357
+ * entities that still have no resolvable owner after ownerhandle + entmode
358
+ * fallback via dwg_entity_owner.
359
+ */
360
+ convertOrphanOwnedEntities(data, blockHeaderPtr, ownerHandle, includeOwnerless) {
361
+ this.ensureOrphanEntityIndex(data);
362
+ const byBlock = this.orphanEntitiesByBlock ?? new Map();
363
+ const owned = byBlock.get(blockHeaderPtr) ?? [];
364
+ const ownerless = includeOwnerless && this.ownerlessOrphanIndices
365
+ ? this.ownerlessOrphanIndices
366
+ : [];
367
+ const indices = owned.length && ownerless.length
368
+ ? owned.concat(ownerless)
369
+ : owned.length
370
+ ? owned
371
+ : ownerless;
372
+ if (indices.length === 0) {
373
+ return [];
374
+ }
375
+ const libredwg = this.libredwg;
376
+ const converter = this.entityConverter;
377
+ const entities = [];
378
+ for (const index of indices) {
379
+ const object = libredwg.dwg_get_object(data, index);
380
+ const entity = converter.convert(object);
381
+ if (entity) {
382
+ entity.ownerBlockRecordSoftId = ownerHandle;
383
+ entities.push(entity);
384
+ }
385
+ }
386
+ return entities;
387
+ }
388
+ ensureOrphanEntityIndex(data) {
389
+ if (this.orphanEntitiesByBlock) {
390
+ return;
391
+ }
392
+ const libredwg = this.libredwg;
393
+ const numObjects = libredwg.dwg_get_num_objects(data);
394
+ const indexByEntityPtr = new Map();
395
+ const entityIndices = [];
396
+ const blockHeaderPtrs = [];
397
+ // dwg_entity_owner returns a BLOCK_HEADER TIO pointer; map it back to the
398
+ // Dwg_Object* used as the orphan-index key.
399
+ const blockObjByTio = new Map();
400
+ for (let i = 0; i < numObjects; i++) {
401
+ const obj = libredwg.dwg_get_object(data, i);
402
+ if (!obj)
403
+ continue;
404
+ if (libredwg.dwg_object_get_supertype(obj) ===
405
+ Dwg_Object_Supertype.DWG_SUPERTYPE_ENTITY) {
406
+ entityIndices.push(i);
407
+ indexByEntityPtr.set(obj, i);
408
+ }
409
+ else if (libredwg.dwg_object_get_fixedtype(obj) ===
410
+ Dwg_Object_Type.DWG_TYPE_BLOCK_HEADER) {
411
+ blockHeaderPtrs.push(obj);
412
+ const tio = libredwg.dwg_object_to_object_tio(obj);
413
+ if (tio)
414
+ blockObjByTio.set(tio, obj);
415
+ }
416
+ }
417
+ const claimed = new Set();
418
+ for (const blockHeader of blockHeaderPtrs) {
419
+ let current = libredwg.get_first_owned_entity(blockHeader);
420
+ let steps = 0;
421
+ while (current && steps++ <= entityIndices.length) {
422
+ const index = indexByEntityPtr.get(current);
423
+ if (index === undefined || claimed.has(index))
424
+ break;
425
+ claimed.add(index);
426
+ current = libredwg.get_next_owned_entity(blockHeader, current);
427
+ }
428
+ }
429
+ const orphansByBlock = new Map();
430
+ const ownerless = [];
431
+ for (const index of entityIndices) {
432
+ if (claimed.has(index))
433
+ continue;
434
+ const obj = libredwg.dwg_get_object(data, index);
435
+ const etio = obj ? libredwg.dwg_object_to_entity_tio(obj) : 0;
436
+ // Resolves ownerhandle, then entmode → MSPACE/PSPACE (LibreDWG).
437
+ const ownerTio = etio ? libredwg.dwg_entity_owner(etio) : 0;
438
+ const owner = ownerTio ? (blockObjByTio.get(ownerTio) ?? 0) : 0;
439
+ if (owner) {
440
+ const owned = orphansByBlock.get(owner);
441
+ if (owned)
442
+ owned.push(index);
443
+ else
444
+ orphansByBlock.set(owner, [index]);
445
+ }
446
+ else {
447
+ ownerless.push(index);
448
+ }
449
+ }
450
+ this.orphanEntitiesByBlock = orphansByBlock;
451
+ this.ownerlessOrphanIndices = ownerless;
452
+ }
318
453
  convertDimStyle(item, obj) {
319
454
  const libredwg = this.libredwg;
320
455
  const commonAttrs = this.getCommonTableEntryAttrs(item, obj);
@@ -1320,7 +1320,7 @@ export class LibreEntityConverter {
1320
1320
  const elevation = libredwg.dwg_dynapi_entity_data(entity, 'elevation');
1321
1321
  const thickness = libredwg.dwg_dynapi_entity_data(entity, 'thickness');
1322
1322
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, 'extrusion');
1323
- const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object);
1323
+ const vertices = libredwg.dwg_entity_polyline_2d_get_vertices(object) ?? [];
1324
1324
  return {
1325
1325
  type: 'POLYLINE2D',
1326
1326
  ...commonAttrs,
@@ -1356,7 +1356,7 @@ export class LibreEntityConverter {
1356
1356
  const startWidth = libredwg.dwg_dynapi_entity_data(entity, 'start_width');
1357
1357
  const endWidth = libredwg.dwg_dynapi_entity_data(entity, 'end_width');
1358
1358
  const extrusionDirection = libredwg.dwg_dynapi_entity_data(entity, 'extrusion');
1359
- const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object);
1359
+ const vertices = libredwg.dwg_entity_polyline_3d_get_vertices(object) ?? [];
1360
1360
  return {
1361
1361
  type: 'POLYLINE3D',
1362
1362
  ...commonAttrs,
@@ -73,7 +73,8 @@ const encodings = [
73
73
  'shift-jis', // DOS Japanese (shiftjis)
74
74
  'macintosh', // 23
75
75
  'big5',
76
- 'utf-8', // Korean (Wansung + Johab)
76
+ // WHATWG 'euc-kr' decodes windows-949, which covers CP949 (Wansung + UHC).
77
+ 'euc-kr', // Korean (CP949 / Wansung + UHC)
77
78
  'utf-8', // Johab?
78
79
  'ibm866', // Russian
79
80
  'windows-1250', // Central + Eastern European
@@ -7,5 +7,6 @@ export * from './header';
7
7
  export * from './objects';
8
8
  export * from './shared';
9
9
  export * from './tables';
10
+ export * from './textEscapes';
10
11
  export * from './version';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -7,5 +7,6 @@ export * from './header';
7
7
  export * from './objects';
8
8
  export * from './shared';
9
9
  export * from './tables';
10
+ export * from './textEscapes';
10
11
  export * from './version';
11
12
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Replaces AutoCAD `\U+XXXX` escapes with the corresponding Unicode characters.
3
+ */
4
+ export declare function decodeUnicodeEscapes(text: string): string;
5
+ //# sourceMappingURL=textEscapes.d.ts.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * AutoCAD stores characters outside the drawing's code page as `\U+XXXX`
3
+ * escape sequences (exactly four hex digits). Decode them so TEXT / MTEXT /
4
+ * ATTRIB / layer names become real Unicode instead of literal escapes.
5
+ *
6
+ * Adjacent high/low surrogate escapes (e.g. `\U+D83D\U+DE00`) expand to
7
+ * UTF-16 code units that form one supplementary-plane character in JS strings.
8
+ */
9
+ const UNICODE_ESCAPE = /\\U\+([0-9A-Fa-f]{4})/g;
10
+ /**
11
+ * Replaces AutoCAD `\U+XXXX` escapes with the corresponding Unicode characters.
12
+ */
13
+ export function decodeUnicodeEscapes(text) {
14
+ if (!text || !text.includes('\\U+')) {
15
+ return text;
16
+ }
17
+ return text.replace(UNICODE_ESCAPE, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
18
+ }
19
+ //# sourceMappingURL=textEscapes.js.map
package/lib/libredwg.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import createModule from '../wasm/libredwg-web.js';
2
2
  import { LibreDwgConverter } from './converter';
3
- import { dwgCodePageToEncoding, dwgVersions } from './database';
3
+ import { decodeUnicodeEscapes, dwgCodePageToEncoding, dwgVersions } from './database';
4
4
  import { SvgConverter } from './svg';
5
5
  import { Dwg_Error, Dwg_File_Type, Dwg_Object_Type } from './types';
6
6
  export { createModule };
@@ -799,6 +799,9 @@ export class LibreDwg {
799
799
  if (value.bin && this.decoder) {
800
800
  value.data = this.decoder.decode(value.bin);
801
801
  }
802
+ if (typeof value.data === 'string') {
803
+ value.data = decodeUnicodeEscapes(value.data);
804
+ }
802
805
  return value;
803
806
  }
804
807
  /**
@@ -852,7 +855,14 @@ export class LibreDwg {
852
855
  * @returns Returns the sub-field value of one complex field.
853
856
  */
854
857
  dwg_dynapi_subclass_value(obj, subclass, field) {
855
- return this.wasmInstance.dwg_dynapi_subclass_value(obj, subclass, field);
858
+ const value = this.wasmInstance.dwg_dynapi_subclass_value(obj, subclass, field);
859
+ if (value.bin && this.decoder) {
860
+ value.data = this.decoder.decode(value.bin);
861
+ }
862
+ if (typeof value.data === 'string') {
863
+ value.data = decodeUnicodeEscapes(value.data);
864
+ }
865
+ return value;
856
866
  }
857
867
  /**
858
868
  * Returns the sub-field data of one complex field.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A DWG/DXF JavaScript parser based on libredwg",
4
4
  "license": "GPL-3.0",
5
5
  "private": false,
6
- "version": "0.7.9",
6
+ "version": "0.7.11",
7
7
  "author": "MLight Lee <mlight.lee@outlook.com>",
8
8
  "type": "module",
9
9
  "engines": {
@@ -87,6 +87,6 @@
87
87
  "test": "node --test test/node-api.test.mjs",
88
88
  "test:node": "pnpm build && pnpm test",
89
89
  "publish:npm": "pnpm publish --access public --no-git-checks",
90
- "release": "node tools/release.mjs"
90
+ "release": "node script/release.mjs"
91
91
  }
92
92
  }