@mlightcad/libredwg-web 0.7.10 → 0.7.12

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",
@@ -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);
@@ -3867,8 +3993,10 @@ class LibreDwgConverter {
3867
3993
  dashes.forEach((dash) => {
3868
3994
  patterns.push({
3869
3995
  elementLength: dash.length || 0,
3870
- elementTypeFlag: dash.complex_shapecode,
3871
- shapeNumber: dash.shape_flag,
3996
+ // DXF 74: 1=absolute rotation, 2=text, 4=shape
3997
+ elementTypeFlag: dash.shape_flag || 0,
3998
+ // DXF 75: shape number (when flag has bit 4)
3999
+ shapeNumber: dash.complex_shapecode,
3872
4000
  // TODO: convert style handle to style object id
3873
4001
  // styleObjectId: dash.style,
3874
4002
  scale: dash.scale,
@@ -6449,6 +6577,9 @@ const _LibreDwg = class _LibreDwg {
6449
6577
  if (value.bin && this.decoder) {
6450
6578
  value.data = this.decoder.decode(value.bin);
6451
6579
  }
6580
+ if (typeof value.data === "string") {
6581
+ value.data = decodeUnicodeEscapes(value.data);
6582
+ }
6452
6583
  return value;
6453
6584
  }
6454
6585
  /**
@@ -6502,7 +6633,18 @@ const _LibreDwg = class _LibreDwg {
6502
6633
  * @returns Returns the sub-field value of one complex field.
6503
6634
  */
6504
6635
  dwg_dynapi_subclass_value(obj, subclass, field) {
6505
- 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;
6506
6648
  }
6507
6649
  /**
6508
6650
  * Returns the sub-field data of one complex field.
@@ -6938,6 +7080,7 @@ export {
6938
7080
  HEADER_VARIABLES,
6939
7081
  LibreDwg,
6940
7082
  default2 as createModule,
7083
+ decodeUnicodeEscapes,
6941
7084
  dwgCodePageToEncoding,
6942
7085
  dwgVersions
6943
7086
  };
@@ -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",
@@ -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);
@@ -3869,8 +3995,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3869
3995
  dashes.forEach((dash) => {
3870
3996
  patterns.push({
3871
3997
  elementLength: dash.length || 0,
3872
- elementTypeFlag: dash.complex_shapecode,
3873
- shapeNumber: dash.shape_flag,
3998
+ // DXF 74: 1=absolute rotation, 2=text, 4=shape
3999
+ elementTypeFlag: dash.shape_flag || 0,
4000
+ // DXF 75: shape number (when flag has bit 4)
4001
+ shapeNumber: dash.complex_shapecode,
3874
4002
  // TODO: convert style handle to style object id
3875
4003
  // styleObjectId: dash.style,
3876
4004
  scale: dash.scale,
@@ -6451,6 +6579,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6451
6579
  if (value.bin && this.decoder) {
6452
6580
  value.data = this.decoder.decode(value.bin);
6453
6581
  }
6582
+ if (typeof value.data === "string") {
6583
+ value.data = decodeUnicodeEscapes(value.data);
6584
+ }
6454
6585
  return value;
6455
6586
  }
6456
6587
  /**
@@ -6504,7 +6635,18 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6504
6635
  * @returns Returns the sub-field value of one complex field.
6505
6636
  */
6506
6637
  dwg_dynapi_subclass_value(obj, subclass, field) {
6507
- return this.wasmInstance.dwg_dynapi_subclass_value(obj, subclass, field);
6638
+ const value = this.wasmInstance.dwg_dynapi_subclass_value(
6639
+ obj,
6640
+ subclass,
6641
+ field
6642
+ );
6643
+ if (value.bin && this.decoder) {
6644
+ value.data = this.decoder.decode(value.bin);
6645
+ }
6646
+ if (typeof value.data === "string") {
6647
+ value.data = decodeUnicodeEscapes(value.data);
6648
+ }
6649
+ return value;
6508
6650
  }
6509
6651
  /**
6510
6652
  * Returns the sub-field data of one complex field.
@@ -6939,6 +7081,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6939
7081
  exports2.Dwg_Object_Type_Inverted = Dwg_Object_Type_Inverted;
6940
7082
  exports2.HEADER_VARIABLES = HEADER_VARIABLES;
6941
7083
  exports2.LibreDwg = LibreDwg;
7084
+ exports2.decodeUnicodeEscapes = decodeUnicodeEscapes;
6942
7085
  exports2.dwgCodePageToEncoding = dwgCodePageToEncoding;
6943
7086
  exports2.dwgVersions = dwgVersions;
6944
7087
  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);
@@ -568,8 +703,10 @@ export class LibreDwgConverter {
568
703
  dashes.forEach(dash => {
569
704
  patterns.push({
570
705
  elementLength: dash.length || 0,
571
- elementTypeFlag: dash.complex_shapecode,
572
- shapeNumber: dash.shape_flag,
706
+ // DXF 74: 1=absolute rotation, 2=text, 4=shape
707
+ elementTypeFlag: dash.shape_flag || 0,
708
+ // DXF 75: shape number (when flag has bit 4)
709
+ shapeNumber: dash.complex_shapecode,
573
710
  // TODO: convert style handle to style object id
574
711
  // styleObjectId: dash.style,
575
712
  scale: dash.scale,
@@ -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
@@ -9,13 +9,16 @@ export interface DwgLTypeTableEntry extends DwgCommonTableEntry {
9
9
  }
10
10
  export interface DwgLineTypeElement {
11
11
  elementLength: number;
12
+ /** DXF 74: 1 = absolute rotation, 2 = embedded text, 4 = shape */
12
13
  elementTypeFlag: number;
14
+ /** DXF 75: shape number when elementTypeFlag has bit 4 */
13
15
  shapeNumber?: number;
14
16
  styleObjectId?: string;
15
17
  scale?: number;
16
18
  rotation?: number;
17
19
  offsetX?: number;
18
20
  offsetY?: number;
21
+ /** DXF 9: embedded text when elementTypeFlag has bit 2 */
19
22
  text?: string;
20
23
  }
21
24
  //# sourceMappingURL=ltype.d.ts.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.10",
6
+ "version": "0.7.12",
7
7
  "author": "MLight Lee <mlight.lee@outlook.com>",
8
8
  "type": "module",
9
9
  "engines": {
Binary file