@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 +1 -1
- package/dist/libredwg-web.js +148 -5
- package/dist/libredwg-web.umd.cjs +148 -5
- package/lib/converter/converter.d.ts +20 -0
- package/lib/converter/converter.js +139 -2
- package/lib/database/codepage.js +2 -1
- package/lib/database/index.d.ts +1 -0
- package/lib/database/index.js +1 -0
- package/lib/database/tables/ltype.d.ts +3 -0
- package/lib/database/textEscapes.d.ts +5 -0
- package/lib/database/textEscapes.js +19 -0
- package/lib/libredwg.js +12 -2
- package/package.json +1 -1
- package/wasm/libredwg-web.wasm +0 -0
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
|
-
-
|
|
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.
|
package/dist/libredwg-web.js
CHANGED
|
@@ -94,8 +94,9 @@ const encodings = [
|
|
|
94
94
|
"macintosh",
|
|
95
95
|
// 23
|
|
96
96
|
"big5",
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
3871
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
3873
|
-
|
|
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
|
-
|
|
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
|
-
|
|
572
|
-
|
|
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,
|
package/lib/database/codepage.js
CHANGED
|
@@ -73,7 +73,8 @@ const encodings = [
|
|
|
73
73
|
'shift-jis', // DOS Japanese (shiftjis)
|
|
74
74
|
'macintosh', // 23
|
|
75
75
|
'big5',
|
|
76
|
-
'
|
|
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
|
package/lib/database/index.d.ts
CHANGED
package/lib/database/index.js
CHANGED
|
@@ -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,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
|
-
|
|
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
package/wasm/libredwg-web.wasm
CHANGED
|
Binary file
|