@cosyte/dicom 0.0.5 → 0.0.7
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/CHANGELOG.md +438 -31
- package/README.md +12 -7
- package/dist/index.cjs +548 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -12
- package/dist/index.d.ts +307 -12
- package/dist/index.mjs +549 -156
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -11,7 +11,7 @@ var __export = (target, all) => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/version.ts
|
|
14
|
-
var VERSION = "0.0.
|
|
14
|
+
var VERSION = "0.0.7";
|
|
15
15
|
|
|
16
16
|
// src/dictionary/index.ts
|
|
17
17
|
var dictionary_exports = {};
|
|
@@ -11574,6 +11574,13 @@ var ByteCursor = class {
|
|
|
11574
11574
|
}
|
|
11575
11575
|
};
|
|
11576
11576
|
|
|
11577
|
+
// src/parser/tokens.ts
|
|
11578
|
+
var WITHHELD = "<withheld>";
|
|
11579
|
+
function safePrivateCreator(creator, profile) {
|
|
11580
|
+
if (profile === void 0) return WITHHELD;
|
|
11581
|
+
return profile.privateDictionary.has(creator) ? creator : WITHHELD;
|
|
11582
|
+
}
|
|
11583
|
+
|
|
11577
11584
|
// src/dataset/vr/charset.ts
|
|
11578
11585
|
var TERM_TO_LABEL = /* @__PURE__ */ new Map([
|
|
11579
11586
|
// Default repertoire (ISO 646 IRV / ASCII). Latin-1 is a lenient,
|
|
@@ -11626,7 +11633,7 @@ var MULTIBYTE_LABELS = /* @__PURE__ */ new Set([
|
|
|
11626
11633
|
]);
|
|
11627
11634
|
function parseSpecificCharacterSet(bytes) {
|
|
11628
11635
|
const raw = bytes.toString("latin1");
|
|
11629
|
-
return raw.split("\\").map((t) => t.replace(/[ ]+$/u, "").replace(/^[ ]+/u, ""));
|
|
11636
|
+
return raw.split("\\").map((t) => t.replace(/[ ]+$/u, "").replace(/^[ ]+/u, "")).map((t) => t === "" || TERM_TO_LABEL.has(t) ? t : WITHHELD);
|
|
11630
11637
|
}
|
|
11631
11638
|
function isKnownCharsetTerm(term) {
|
|
11632
11639
|
return TERM_TO_LABEL.has(term);
|
|
@@ -11655,6 +11662,48 @@ function decodeText(bytes, terms) {
|
|
|
11655
11662
|
}
|
|
11656
11663
|
}
|
|
11657
11664
|
|
|
11665
|
+
// src/parser/endian.ts
|
|
11666
|
+
var BE_VR_STRIDE = Object.freeze({
|
|
11667
|
+
// 2-byte swaps
|
|
11668
|
+
AT: 2,
|
|
11669
|
+
US: 2,
|
|
11670
|
+
SS: 2,
|
|
11671
|
+
OW: 2,
|
|
11672
|
+
// 4-byte swaps
|
|
11673
|
+
UL: 4,
|
|
11674
|
+
SL: 4,
|
|
11675
|
+
FL: 4,
|
|
11676
|
+
OF: 4,
|
|
11677
|
+
OL: 4,
|
|
11678
|
+
// 8-byte swaps (64-bit floats + 2018 64-bit integer additions)
|
|
11679
|
+
FD: 8,
|
|
11680
|
+
OD: 8,
|
|
11681
|
+
OV: 8,
|
|
11682
|
+
SV: 8,
|
|
11683
|
+
UV: 8,
|
|
11684
|
+
// No swap - byte streams (D-24) and ASCII / spec-defined VRs.
|
|
11685
|
+
OB: 0,
|
|
11686
|
+
UN: 0,
|
|
11687
|
+
AE: 0,
|
|
11688
|
+
AS: 0,
|
|
11689
|
+
CS: 0,
|
|
11690
|
+
DA: 0,
|
|
11691
|
+
DS: 0,
|
|
11692
|
+
DT: 0,
|
|
11693
|
+
IS: 0,
|
|
11694
|
+
LO: 0,
|
|
11695
|
+
LT: 0,
|
|
11696
|
+
PN: 0,
|
|
11697
|
+
SH: 0,
|
|
11698
|
+
ST: 0,
|
|
11699
|
+
TM: 0,
|
|
11700
|
+
UC: 0,
|
|
11701
|
+
UI: 0,
|
|
11702
|
+
UR: 0,
|
|
11703
|
+
UT: 0,
|
|
11704
|
+
SQ: 0
|
|
11705
|
+
});
|
|
11706
|
+
|
|
11658
11707
|
// src/parser/warnings.ts
|
|
11659
11708
|
var WARNING_CODES = {
|
|
11660
11709
|
// === Phase 2 actively emits (D-08 active list - alphabetical-within-prefix per CONTEXT specifics §) ===
|
|
@@ -11668,6 +11717,7 @@ var WARNING_CODES = {
|
|
|
11668
11717
|
DICOM_ODD_LENGTH_VALUE_PADDED: "DICOM_ODD_LENGTH_VALUE_PADDED",
|
|
11669
11718
|
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "DICOM_PIXEL_DATA_LENGTH_MISMATCH",
|
|
11670
11719
|
DICOM_PRIVATE_TAG_NO_CREATOR: "DICOM_PRIVATE_TAG_NO_CREATOR",
|
|
11720
|
+
DICOM_SQ_NOT_DESCENDED: "DICOM_SQ_NOT_DESCENDED",
|
|
11671
11721
|
DICOM_UN_PARSED_AS_SQ: "DICOM_UN_PARSED_AS_SQ",
|
|
11672
11722
|
DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR: "DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR",
|
|
11673
11723
|
DICOM_VR_MISMATCH: "DICOM_VR_MISMATCH",
|
|
@@ -11685,162 +11735,168 @@ var WARNING_CODES = {
|
|
|
11685
11735
|
// === Reserved by later phases (declared, not emitted in Phase 2) ===
|
|
11686
11736
|
DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED: "DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED",
|
|
11687
11737
|
// reserved by Phase 7 - not emitted in Phase 2
|
|
11738
|
+
DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED: "DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED",
|
|
11739
|
+
// emitted by deidentify(), never by the parser
|
|
11740
|
+
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE",
|
|
11741
|
+
// emitted by deidentify(), never by the parser
|
|
11742
|
+
DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE",
|
|
11743
|
+
// emitted by deidentify(), never by the parser
|
|
11688
11744
|
DICOM_PRIVATE_CREATOR_UNKNOWN: "DICOM_PRIVATE_CREATOR_UNKNOWN"
|
|
11689
11745
|
// reserved by Phase 6 - not emitted in Phase 2
|
|
11690
11746
|
};
|
|
11747
|
+
var WARNING_MESSAGES = Object.freeze({
|
|
11748
|
+
DICOM_MISSING_PREAMBLE: "No DICM magic at offset 128; falling back to offset-0 dataset.",
|
|
11749
|
+
DICOM_FILE_META_GROUP_LENGTH_MISSING: "(0002,0000) FileMetaInformationGroupLength missing; scanning forward to first non-(0002,xxxx) element.",
|
|
11750
|
+
DICOM_FILE_META_GROUP_LENGTH_MISMATCH: "(0002,0000) FileMetaInformationGroupLength declared {n} bytes; actual File Meta group size is {n2} bytes. Trusting actual.",
|
|
11751
|
+
DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR: "Element ({tag}) uses undefined length (0xFFFFFFFF) under an Explicit VR transfer syntax.",
|
|
11752
|
+
DICOM_ODD_LENGTH_VALUE_PADDED: "Element ({tag}) has odd declared length {n}; cursor advanced by one padding byte to maintain alignment.",
|
|
11753
|
+
DICOM_VR_MISMATCH: "Element ({tag}) on-wire VR is {vr2}; dictionary lists {vr}. Trusting on-wire VR.",
|
|
11754
|
+
DICOM_PRIVATE_TAG_NO_CREATOR: "Private element ({tag}) has no Private Creator registered for its block; treating as VR=UN.",
|
|
11755
|
+
DICOM_GROUP_LENGTH_IN_DATASET: "Retired Group Length element ({tag}) encountered in dataset; preserved as-is.",
|
|
11756
|
+
DICOM_NONZERO_RESERVED_BYTES: "Element ({tag}) has non-zero reserved bytes between VR and length (first byte {n}, second byte {n2}); ignoring.",
|
|
11757
|
+
DICOM_SQ_NOT_DESCENDED: "Element ({tag}) resolved to VR=SQ from the dictionary but its defined-length value is not a valid item stream; kept as opaque bytes and NOT descended, so nothing nested inside it is visible to navigation, and deidentify() cannot audit it. See DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE.",
|
|
11758
|
+
DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
|
|
11759
|
+
DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
|
|
11760
|
+
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length {n} does not match computed {n2} bytes.",
|
|
11761
|
+
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "Private element ({tag}) under Implicit VR LE has no VR override; falling back to UN.",
|
|
11762
|
+
DICOM_PRIVATE_CREATOR_UNKNOWN: "Private element ({tag}) has a Private Creator the active profile's private dictionary does not name; falling back to UN. The creator string is not reproduced here - read the (gggg,00EE) element if you need it.",
|
|
11763
|
+
DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED: "Pixel Data is present and Burned In Annotation is not 'NO'; this metadata-only de-identifier cannot inspect or clean pixels. Recognizable text may remain burned into the image.",
|
|
11764
|
+
DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED: "Element ({tag}) {vr} was kept by the action table, but its value ends with {n} whole Data Element(s) - an over-declared Value Length swallowed what followed it. Emptied rather than kept, because the action table cannot see an attribute encoded inside a value. Read report.embeddedAttributes for the tags that were hidden there.",
|
|
11765
|
+
// Deliberately short. One of these is raised per un-auditable element, so a
|
|
11766
|
+
// long message is multiplied by an element count the input controls; the
|
|
11767
|
+
// reasoning belongs in the docs, not in a string repeated thousands of times.
|
|
11768
|
+
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "Element ({tag}) is VR=SQ with no parsed items, so its {n} value bytes could not be audited (PS3.15 E.1.1); emptied. See report.unauditableSequences.",
|
|
11769
|
+
// Deliberately short for the same reason as the code above: one per element,
|
|
11770
|
+
// and the element count is chosen by the input.
|
|
11771
|
+
//
|
|
11772
|
+
// NEITHER THE TAG NOR THE VR IS ECHOED, and that is specific to this code
|
|
11773
|
+
// rather than caution. Every other factory's {tag} is composed from a real
|
|
11774
|
+
// Data Element header; the condition that raises THIS one is that the header
|
|
11775
|
+
// was fabricated from bytes inside some element's value, so its four tag bytes
|
|
11776
|
+
// and its two VR bytes are document content. `renderTag` shape-checks a tag
|
|
11777
|
+
// and cannot refuse one, so the withholding has to happen at the call site.
|
|
11778
|
+
// The byte offset locates the element instead, and is a position this parser
|
|
11779
|
+
// counted rather than anything the document said.
|
|
11780
|
+
DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "An element at byte offset {n2} carries an on-wire VR that is not one of the 34 PS3.5 6.2 defines, so its {n} value bytes are not a Value Field this library decoded; emptied (PS3.15 E.1.1). Its tag and VR are withheld: an earlier under-declared length can make them fragments of some element's value. See report.undefinedVrElements.",
|
|
11781
|
+
DICOM_BOM_IN_TEXT_VR: "Element ({tag}) {vr} value begins with a UTF-8 BOM; stripped on decode.",
|
|
11782
|
+
DICOM_TRAILING_NULL_IN_TEXT_VR: "Element ({tag}) {vr} value has a trailing NULL pad where SPACE is expected; trimmed.",
|
|
11783
|
+
DICOM_UI_TRAILING_SPACE: "Element ({tag}) UI value is SPACE-padded; UI requires NULL padding (PS3.5 \xA76.2). Trimmed.",
|
|
11784
|
+
DICOM_NON_ASCII_IN_ASCII_VR: "Element ({tag}) {vr} is an ASCII-only VR but contains non-ASCII bytes; decoded as Latin-1 best-effort.",
|
|
11785
|
+
DICOM_IS_NONINTEGER_VALUE: "Element ({tag}) IS value is not a base-10 integer; surfaced as null with raw bytes preserved.",
|
|
11786
|
+
DICOM_DA_LEGACY_FORMAT: "Element ({tag}) DA value is not in canonical YYYYMMDD form; decoded best-effort, raw preserved.",
|
|
11787
|
+
DICOM_DT_NONSTANDARD_OFFSET: "Element ({tag}) DT value has a non-standard UTC offset; decoded best-effort, raw preserved.",
|
|
11788
|
+
DICOM_UNSUPPORTED_CHARSET: "(0008,0005) Specific Character Set value {n} names a defined term this build does not support; decoding text as UTF-8 best-effort. The term is not reproduced here - read the element if you need it.",
|
|
11789
|
+
DICOM_CHARSET_AMBIGUOUS_SEPARATOR: "(0008,0005) Specific Character Set value {n} is ambiguous under the active code extensions; decoding text as UTF-8 best-effort."
|
|
11790
|
+
});
|
|
11791
|
+
var KNOWN_VRS = new Set(Object.keys(BE_VR_STRIDE));
|
|
11792
|
+
var TAG_SHAPE = /^[0-9A-F]{8}$/u;
|
|
11793
|
+
function renderTag(tag) {
|
|
11794
|
+
return tag !== void 0 && TAG_SHAPE.test(tag) ? tag : WITHHELD;
|
|
11795
|
+
}
|
|
11796
|
+
function renderVr(vr) {
|
|
11797
|
+
return vr !== void 0 && KNOWN_VRS.has(vr) ? vr : WITHHELD;
|
|
11798
|
+
}
|
|
11799
|
+
function build(code, position, tokens = {}) {
|
|
11800
|
+
const message = WARNING_MESSAGES[code].replace("{tag}", renderTag(tokens.tag)).replace("{vr2}", renderVr(tokens.vr2)).replace("{vr}", renderVr(tokens.vr)).replace("{n2}", String(tokens.n2 ?? 0)).replace("{n}", String(tokens.n ?? 0));
|
|
11801
|
+
return { code, message, position };
|
|
11802
|
+
}
|
|
11691
11803
|
function missingPreamble(position) {
|
|
11692
|
-
return
|
|
11693
|
-
code: WARNING_CODES.DICOM_MISSING_PREAMBLE,
|
|
11694
|
-
message: "No DICM magic at offset 128; falling back to offset-0 dataset.",
|
|
11695
|
-
position
|
|
11696
|
-
};
|
|
11804
|
+
return build(WARNING_CODES.DICOM_MISSING_PREAMBLE, position);
|
|
11697
11805
|
}
|
|
11698
11806
|
function fileMetaGroupLengthMissing(position) {
|
|
11699
|
-
return
|
|
11700
|
-
code: WARNING_CODES.DICOM_FILE_META_GROUP_LENGTH_MISSING,
|
|
11701
|
-
message: "(0002,0000) FileMetaInformationGroupLength missing; scanning forward to first non-(0002,xxxx) element.",
|
|
11702
|
-
position
|
|
11703
|
-
};
|
|
11807
|
+
return build(WARNING_CODES.DICOM_FILE_META_GROUP_LENGTH_MISSING, position);
|
|
11704
11808
|
}
|
|
11705
11809
|
function fileMetaGroupLengthMismatch(position, declared, actual) {
|
|
11706
|
-
return {
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
};
|
|
11810
|
+
return build(WARNING_CODES.DICOM_FILE_META_GROUP_LENGTH_MISMATCH, position, {
|
|
11811
|
+
n: declared,
|
|
11812
|
+
n2: actual
|
|
11813
|
+
});
|
|
11711
11814
|
}
|
|
11712
11815
|
function undefinedLengthInExplicitVR(position, tag) {
|
|
11713
|
-
return {
|
|
11714
|
-
code: WARNING_CODES.DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR,
|
|
11715
|
-
message: `Element (${tag}) uses undefined length (0xFFFFFFFF) under an Explicit VR transfer syntax.`,
|
|
11716
|
-
position
|
|
11717
|
-
};
|
|
11816
|
+
return build(WARNING_CODES.DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR, position, { tag });
|
|
11718
11817
|
}
|
|
11719
11818
|
function oddLengthValuePadded(position, tag, declaredLength) {
|
|
11720
|
-
return {
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
};
|
|
11819
|
+
return build(WARNING_CODES.DICOM_ODD_LENGTH_VALUE_PADDED, position, {
|
|
11820
|
+
tag,
|
|
11821
|
+
n: declaredLength
|
|
11822
|
+
});
|
|
11725
11823
|
}
|
|
11726
11824
|
function vrMismatch(position, tag, dictVR, fileVR) {
|
|
11727
|
-
return {
|
|
11728
|
-
code: WARNING_CODES.DICOM_VR_MISMATCH,
|
|
11729
|
-
message: `Element (${tag}) on-wire VR is ${fileVR}; dictionary lists ${dictVR}. Trusting on-wire VR.`,
|
|
11730
|
-
position
|
|
11731
|
-
};
|
|
11825
|
+
return build(WARNING_CODES.DICOM_VR_MISMATCH, position, { tag, vr: dictVR, vr2: fileVR });
|
|
11732
11826
|
}
|
|
11733
11827
|
function privateTagNoCreator(position, tag) {
|
|
11734
|
-
return {
|
|
11735
|
-
code: WARNING_CODES.DICOM_PRIVATE_TAG_NO_CREATOR,
|
|
11736
|
-
message: `Private element (${tag}) has no Private Creator registered for its block; treating as VR=UN.`,
|
|
11737
|
-
position
|
|
11738
|
-
};
|
|
11828
|
+
return build(WARNING_CODES.DICOM_PRIVATE_TAG_NO_CREATOR, position, { tag });
|
|
11739
11829
|
}
|
|
11740
11830
|
function groupLengthInDataset(position, tag) {
|
|
11741
|
-
return {
|
|
11742
|
-
code: WARNING_CODES.DICOM_GROUP_LENGTH_IN_DATASET,
|
|
11743
|
-
message: `Retired Group Length element (${tag}) encountered in dataset; preserved as-is.`,
|
|
11744
|
-
position
|
|
11745
|
-
};
|
|
11831
|
+
return build(WARNING_CODES.DICOM_GROUP_LENGTH_IN_DATASET, position, { tag });
|
|
11746
11832
|
}
|
|
11747
|
-
function nonzeroReservedBytes(position, tag,
|
|
11748
|
-
return {
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
};
|
|
11833
|
+
function nonzeroReservedBytes(position, tag, first, second) {
|
|
11834
|
+
return build(WARNING_CODES.DICOM_NONZERO_RESERVED_BYTES, position, {
|
|
11835
|
+
tag,
|
|
11836
|
+
n: first,
|
|
11837
|
+
n2: second
|
|
11838
|
+
});
|
|
11753
11839
|
}
|
|
11754
11840
|
function unParsedAsSQ(position, tag) {
|
|
11755
|
-
return {
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
};
|
|
11841
|
+
return build(WARNING_CODES.DICOM_UN_PARSED_AS_SQ, position, { tag });
|
|
11842
|
+
}
|
|
11843
|
+
function sqNotDescended(position, tag) {
|
|
11844
|
+
return build(WARNING_CODES.DICOM_SQ_NOT_DESCENDED, position, { tag });
|
|
11760
11845
|
}
|
|
11761
11846
|
function emptyItemInSequence(position, tag) {
|
|
11762
|
-
return {
|
|
11763
|
-
code: WARNING_CODES.DICOM_EMPTY_ITEM_IN_SEQUENCE,
|
|
11764
|
-
message: `Sequence (${tag}) contains an empty item (length=0); tolerated.`,
|
|
11765
|
-
position
|
|
11766
|
-
};
|
|
11847
|
+
return build(WARNING_CODES.DICOM_EMPTY_ITEM_IN_SEQUENCE, position, { tag });
|
|
11767
11848
|
}
|
|
11768
11849
|
function implicitVRForPrivateTagWithoutVR(position, tag) {
|
|
11769
|
-
return {
|
|
11770
|
-
code: WARNING_CODES.DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR,
|
|
11771
|
-
message: `Private element (${tag}) under Implicit VR LE has no VR override; falling back to UN.`,
|
|
11772
|
-
position
|
|
11773
|
-
};
|
|
11850
|
+
return build(WARNING_CODES.DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR, position, { tag });
|
|
11774
11851
|
}
|
|
11775
|
-
function privateCreatorUnknown(position, tag
|
|
11776
|
-
return {
|
|
11777
|
-
code: WARNING_CODES.DICOM_PRIVATE_CREATOR_UNKNOWN,
|
|
11778
|
-
message: `Private element (${tag}) creator "${creator}" is not in the active profile's private dictionary; falling back to UN.`,
|
|
11779
|
-
position
|
|
11780
|
-
};
|
|
11852
|
+
function privateCreatorUnknown(position, tag) {
|
|
11853
|
+
return build(WARNING_CODES.DICOM_PRIVATE_CREATOR_UNKNOWN, position, { tag });
|
|
11781
11854
|
}
|
|
11782
11855
|
function burnedInAnnotationNotRemoved(position) {
|
|
11783
|
-
return
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11856
|
+
return build(WARNING_CODES.DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED, position);
|
|
11857
|
+
}
|
|
11858
|
+
function embeddedAttributeRemoved(position, tag, vr, count) {
|
|
11859
|
+
return build(WARNING_CODES.DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED, position, {
|
|
11860
|
+
tag,
|
|
11861
|
+
vr,
|
|
11862
|
+
n: count
|
|
11863
|
+
});
|
|
11864
|
+
}
|
|
11865
|
+
function sequenceNotAuditable(position, tag, byteLength) {
|
|
11866
|
+
return build(WARNING_CODES.DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE, position, {
|
|
11867
|
+
tag,
|
|
11868
|
+
n: byteLength
|
|
11869
|
+
});
|
|
11870
|
+
}
|
|
11871
|
+
function undefinedVrNotAuditable(position, byteLength) {
|
|
11872
|
+
return build(WARNING_CODES.DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE, position, {
|
|
11873
|
+
n: byteLength,
|
|
11874
|
+
n2: position.byteOffset
|
|
11875
|
+
});
|
|
11788
11876
|
}
|
|
11789
11877
|
function bomInTextVR(position, tag, vr) {
|
|
11790
|
-
return {
|
|
11791
|
-
code: WARNING_CODES.DICOM_BOM_IN_TEXT_VR,
|
|
11792
|
-
message: `Element (${tag}) ${vr} value begins with a UTF-8 BOM; stripped on decode.`,
|
|
11793
|
-
position
|
|
11794
|
-
};
|
|
11878
|
+
return build(WARNING_CODES.DICOM_BOM_IN_TEXT_VR, position, { tag, vr });
|
|
11795
11879
|
}
|
|
11796
11880
|
function trailingNullInTextVR(position, tag, vr) {
|
|
11797
|
-
return {
|
|
11798
|
-
code: WARNING_CODES.DICOM_TRAILING_NULL_IN_TEXT_VR,
|
|
11799
|
-
message: `Element (${tag}) ${vr} value has a trailing NULL pad where SPACE is expected; trimmed.`,
|
|
11800
|
-
position
|
|
11801
|
-
};
|
|
11881
|
+
return build(WARNING_CODES.DICOM_TRAILING_NULL_IN_TEXT_VR, position, { tag, vr });
|
|
11802
11882
|
}
|
|
11803
11883
|
function uiTrailingSpace(position, tag) {
|
|
11804
|
-
return {
|
|
11805
|
-
code: WARNING_CODES.DICOM_UI_TRAILING_SPACE,
|
|
11806
|
-
message: `Element (${tag}) UI value is SPACE-padded; UI requires NULL padding (PS3.5 \xA76.2). Trimmed.`,
|
|
11807
|
-
position
|
|
11808
|
-
};
|
|
11884
|
+
return build(WARNING_CODES.DICOM_UI_TRAILING_SPACE, position, { tag });
|
|
11809
11885
|
}
|
|
11810
11886
|
function nonAsciiInAsciiVR(position, tag, vr) {
|
|
11811
|
-
return {
|
|
11812
|
-
code: WARNING_CODES.DICOM_NON_ASCII_IN_ASCII_VR,
|
|
11813
|
-
message: `Element (${tag}) ${vr} is an ASCII-only VR but contains non-ASCII bytes; decoded as Latin-1 best-effort.`,
|
|
11814
|
-
position
|
|
11815
|
-
};
|
|
11887
|
+
return build(WARNING_CODES.DICOM_NON_ASCII_IN_ASCII_VR, position, { tag, vr });
|
|
11816
11888
|
}
|
|
11817
11889
|
function isNonintegerValue(position, tag) {
|
|
11818
|
-
return {
|
|
11819
|
-
code: WARNING_CODES.DICOM_IS_NONINTEGER_VALUE,
|
|
11820
|
-
message: `Element (${tag}) IS value is not a base-10 integer; surfaced as null with raw bytes preserved.`,
|
|
11821
|
-
position
|
|
11822
|
-
};
|
|
11890
|
+
return build(WARNING_CODES.DICOM_IS_NONINTEGER_VALUE, position, { tag });
|
|
11823
11891
|
}
|
|
11824
11892
|
function daLegacyFormat(position, tag) {
|
|
11825
|
-
return {
|
|
11826
|
-
code: WARNING_CODES.DICOM_DA_LEGACY_FORMAT,
|
|
11827
|
-
message: `Element (${tag}) DA value is not in canonical YYYYMMDD form; decoded best-effort, raw preserved.`,
|
|
11828
|
-
position
|
|
11829
|
-
};
|
|
11893
|
+
return build(WARNING_CODES.DICOM_DA_LEGACY_FORMAT, position, { tag });
|
|
11830
11894
|
}
|
|
11831
11895
|
function dtNonstandardOffset(position, tag) {
|
|
11832
|
-
return {
|
|
11833
|
-
code: WARNING_CODES.DICOM_DT_NONSTANDARD_OFFSET,
|
|
11834
|
-
message: `Element (${tag}) DT value has a non-standard UTC offset; decoded best-effort, raw preserved.`,
|
|
11835
|
-
position
|
|
11836
|
-
};
|
|
11896
|
+
return build(WARNING_CODES.DICOM_DT_NONSTANDARD_OFFSET, position, { tag });
|
|
11837
11897
|
}
|
|
11838
|
-
function unsupportedCharset(position,
|
|
11839
|
-
return {
|
|
11840
|
-
code: WARNING_CODES.DICOM_UNSUPPORTED_CHARSET,
|
|
11841
|
-
message: `Specific Character Set term "${term}" is not supported; decoding text as UTF-8 best-effort.`,
|
|
11842
|
-
position
|
|
11843
|
-
};
|
|
11898
|
+
function unsupportedCharset(position, valueIndex) {
|
|
11899
|
+
return build(WARNING_CODES.DICOM_UNSUPPORTED_CHARSET, position, { n: valueIndex });
|
|
11844
11900
|
}
|
|
11845
11901
|
|
|
11846
11902
|
// src/profiles/lookup.ts
|
|
@@ -11860,9 +11916,9 @@ var SPECIFIC_CHARACTER_SET_TAG = "00080005";
|
|
|
11860
11916
|
function applySpecificCharacterSet(tag, value, ctx, emit, position) {
|
|
11861
11917
|
if (tag !== SPECIFIC_CHARACTER_SET_TAG) return;
|
|
11862
11918
|
const terms = parseSpecificCharacterSet(value);
|
|
11863
|
-
|
|
11864
|
-
if (!isKnownCharsetTerm(term)) emit(unsupportedCharset(position,
|
|
11865
|
-
}
|
|
11919
|
+
terms.forEach((term, index) => {
|
|
11920
|
+
if (!isKnownCharsetTerm(term)) emit(unsupportedCharset(position, index + 1));
|
|
11921
|
+
});
|
|
11866
11922
|
ctx.currentCharset = terms;
|
|
11867
11923
|
}
|
|
11868
11924
|
var LONG_FORM_VRS = /* @__PURE__ */ new Set([
|
|
@@ -11898,7 +11954,7 @@ function resolveImplicitVR(tag, ctx, emit, position) {
|
|
|
11898
11954
|
const def = resolvePrivateTag(ctx.profile, tag, creator);
|
|
11899
11955
|
if (def !== void 0) return def.vr;
|
|
11900
11956
|
if (!ctx.profile.privateDictionary.has(creator)) {
|
|
11901
|
-
emit(privateCreatorUnknown(position, tag
|
|
11957
|
+
emit(privateCreatorUnknown(position, tag));
|
|
11902
11958
|
}
|
|
11903
11959
|
}
|
|
11904
11960
|
emit(implicitVRForPrivateTagWithoutVR(position, tag));
|
|
@@ -11923,6 +11979,9 @@ function resolvePrivateCreator(tag, ctx) {
|
|
|
11923
11979
|
if (blockId < 16 || blockId > 255) return void 0;
|
|
11924
11980
|
return ctx.creators.get(group)?.get(blockId);
|
|
11925
11981
|
}
|
|
11982
|
+
function safeModelCreator(creator, ctx) {
|
|
11983
|
+
return creator === void 0 ? void 0 : safePrivateCreator(creator, ctx.profile);
|
|
11984
|
+
}
|
|
11926
11985
|
function registerPrivateCreator(tag, value, ctx) {
|
|
11927
11986
|
const group = parseInt(tag.slice(0, 4), 16);
|
|
11928
11987
|
if (group % 2 === 0) return;
|
|
@@ -11982,8 +12041,7 @@ function readExplicitElementHeader(cursor, _ctx, emit) {
|
|
|
11982
12041
|
const reserved1 = cursor.buffer[cursor.position + 1];
|
|
11983
12042
|
cursor.position += 2;
|
|
11984
12043
|
if ((reserved0 ?? 0) !== 0 || (reserved1 ?? 0) !== 0) {
|
|
11985
|
-
|
|
11986
|
-
emit(nonzeroReservedBytes({ byteOffset: headerStart }, tag, observed));
|
|
12044
|
+
emit(nonzeroReservedBytes({ byteOffset: headerStart }, tag, reserved0 ?? 0, reserved1 ?? 0));
|
|
11987
12045
|
}
|
|
11988
12046
|
length = cursor.readUInt32();
|
|
11989
12047
|
headerLength = 12;
|
|
@@ -12619,6 +12677,13 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
12619
12677
|
ctx.currentCharset = parentCharset;
|
|
12620
12678
|
}
|
|
12621
12679
|
};
|
|
12680
|
+
const parentCreators = ctx.creators;
|
|
12681
|
+
const restoreParentCreators = () => {
|
|
12682
|
+
ctx.creators = parentCreators;
|
|
12683
|
+
};
|
|
12684
|
+
const beginItemCreatorScope = () => {
|
|
12685
|
+
ctx.creators = /* @__PURE__ */ new Map();
|
|
12686
|
+
};
|
|
12622
12687
|
try {
|
|
12623
12688
|
const cursor = new ByteCursor(buffer, opts.littleEndian, valueStart);
|
|
12624
12689
|
const items = [];
|
|
@@ -12683,6 +12748,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
12683
12748
|
continue;
|
|
12684
12749
|
}
|
|
12685
12750
|
restoreParentCharset();
|
|
12751
|
+
beginItemCreatorScope();
|
|
12686
12752
|
if (itemLength === UNDEFINED_LENGTH) {
|
|
12687
12753
|
const inner = opts.innerStrategy(buffer, cursor.position, ctx, emit, {
|
|
12688
12754
|
stopOnItemDelim: true
|
|
@@ -12710,9 +12776,39 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
12710
12776
|
ctx.nestingDepth -= 1;
|
|
12711
12777
|
ctx.encodingContextStack.pop();
|
|
12712
12778
|
restoreParentCharset();
|
|
12779
|
+
restoreParentCreators();
|
|
12780
|
+
}
|
|
12781
|
+
}
|
|
12782
|
+
function isStrictEscalation(err) {
|
|
12783
|
+
if (!(err instanceof DicomParseError)) return false;
|
|
12784
|
+
return Object.values(WARNING_CODES).includes(err.code);
|
|
12785
|
+
}
|
|
12786
|
+
function tryParseDefinedLengthSQ(buffer, valueStart, valueLength, ctx, emit, implicitLeInner, tag) {
|
|
12787
|
+
const savedDepth = ctx.nestingDepth;
|
|
12788
|
+
const savedStackLen = ctx.encodingContextStack.length;
|
|
12789
|
+
const savedWarningsLen = ctx.warnings.length;
|
|
12790
|
+
try {
|
|
12791
|
+
const slice = buffer.subarray(valueStart, valueStart + valueLength);
|
|
12792
|
+
const result = parseSequence(slice, 0, ctx, emit, {
|
|
12793
|
+
explicitLength: valueLength,
|
|
12794
|
+
littleEndian: true,
|
|
12795
|
+
innerStrategy: implicitLeInner
|
|
12796
|
+
});
|
|
12797
|
+
return { success: true, items: result.items };
|
|
12798
|
+
} catch (err) {
|
|
12799
|
+
if (isStrictEscalation(err)) throw err;
|
|
12800
|
+
ctx.nestingDepth = savedDepth;
|
|
12801
|
+
while (ctx.encodingContextStack.length > savedStackLen) {
|
|
12802
|
+
ctx.encodingContextStack.pop();
|
|
12803
|
+
}
|
|
12804
|
+
while (ctx.warnings.length > savedWarningsLen) {
|
|
12805
|
+
ctx.warnings.pop();
|
|
12806
|
+
}
|
|
12807
|
+
emit(sqNotDescended({ byteOffset: valueStart }, tag));
|
|
12808
|
+
return { success: false, items: [] };
|
|
12713
12809
|
}
|
|
12714
12810
|
}
|
|
12715
|
-
function tryParseUnAsSQ(buffer, valueStart, valueLength, ctx, emit, implicitLeInner) {
|
|
12811
|
+
function tryParseUnAsSQ(buffer, valueStart, valueLength, ctx, emit, implicitLeInner, tag) {
|
|
12716
12812
|
const savedDepth = ctx.nestingDepth;
|
|
12717
12813
|
const savedStackLen = ctx.encodingContextStack.length;
|
|
12718
12814
|
const savedWarningsLen = ctx.warnings.length;
|
|
@@ -12725,18 +12821,15 @@ function tryParseUnAsSQ(buffer, valueStart, valueLength, ctx, emit, implicitLeIn
|
|
|
12725
12821
|
innerStrategy: implicitLeInner
|
|
12726
12822
|
};
|
|
12727
12823
|
const result = parseSequence(slice, 0, ctx, emit, opts);
|
|
12728
|
-
emit(unParsedAsSQ({ byteOffset: valueStart },
|
|
12824
|
+
emit(unParsedAsSQ({ byteOffset: valueStart }, tag));
|
|
12729
12825
|
return {
|
|
12730
12826
|
success: true,
|
|
12731
12827
|
items: result.items,
|
|
12732
12828
|
endOffset: valueStart + result.endOffset
|
|
12733
12829
|
};
|
|
12734
12830
|
} catch (err) {
|
|
12735
|
-
if (err
|
|
12736
|
-
|
|
12737
|
-
if (warningCodeValues.includes(err.code)) {
|
|
12738
|
-
throw err;
|
|
12739
|
-
}
|
|
12831
|
+
if (isStrictEscalation(err)) {
|
|
12832
|
+
throw err;
|
|
12740
12833
|
}
|
|
12741
12834
|
ctx.nestingDepth = savedDepth;
|
|
12742
12835
|
while (ctx.encodingContextStack.length > savedStackLen) {
|
|
@@ -12797,7 +12890,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12797
12890
|
const valueRawStart = headerStart;
|
|
12798
12891
|
cursor.position = seq.endOffset;
|
|
12799
12892
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12800
|
-
const privateCreator2 = resolvePrivateCreator(tag, ctx);
|
|
12893
|
+
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12801
12894
|
elements.set(
|
|
12802
12895
|
tag,
|
|
12803
12896
|
new Element({
|
|
@@ -12814,6 +12907,40 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12814
12907
|
);
|
|
12815
12908
|
continue;
|
|
12816
12909
|
}
|
|
12910
|
+
if (vr === "UN") {
|
|
12911
|
+
const cp246 = tryParseUnAsSQ(
|
|
12912
|
+
buffer,
|
|
12913
|
+
cursor.position,
|
|
12914
|
+
4294967295,
|
|
12915
|
+
ctx,
|
|
12916
|
+
emit,
|
|
12917
|
+
parseImplicitLE,
|
|
12918
|
+
tag
|
|
12919
|
+
);
|
|
12920
|
+
if (cp246.success) {
|
|
12921
|
+
const valueRawStart = headerStart;
|
|
12922
|
+
cursor.position = cp246.endOffset;
|
|
12923
|
+
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12924
|
+
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12925
|
+
elements.set(
|
|
12926
|
+
tag,
|
|
12927
|
+
new Element({
|
|
12928
|
+
tag,
|
|
12929
|
+
vr: "SQ",
|
|
12930
|
+
// Promoted, exactly as the Explicit-VR path promotes it.
|
|
12931
|
+
vm: cp246.items.length,
|
|
12932
|
+
length: 4294967295,
|
|
12933
|
+
rawBytes,
|
|
12934
|
+
byteOffset: headerStart,
|
|
12935
|
+
cp246Promoted: true,
|
|
12936
|
+
littleEndian: true,
|
|
12937
|
+
items: cp246.items,
|
|
12938
|
+
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12939
|
+
})
|
|
12940
|
+
);
|
|
12941
|
+
continue;
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12817
12944
|
throw new DicomParseError(
|
|
12818
12945
|
FATAL_CODES.INVALID_FILE_META,
|
|
12819
12946
|
`Undefined length on non-SQ element ${tag} (vr=${vr}) under Implicit VR LE.`,
|
|
@@ -12833,6 +12960,35 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12833
12960
|
const valueEnd = valueStart + length;
|
|
12834
12961
|
const valueSlice = ctx.copyValues ? copyValueBytes(buffer.subarray(valueStart, valueEnd)) : buffer.subarray(valueStart, valueEnd);
|
|
12835
12962
|
cursor.position = valueEnd;
|
|
12963
|
+
if (vr === "SQ") {
|
|
12964
|
+
const seq = tryParseDefinedLengthSQ(
|
|
12965
|
+
buffer,
|
|
12966
|
+
valueStart,
|
|
12967
|
+
length,
|
|
12968
|
+
ctx,
|
|
12969
|
+
emit,
|
|
12970
|
+
parseImplicitLE,
|
|
12971
|
+
tag
|
|
12972
|
+
);
|
|
12973
|
+
if (seq.success) {
|
|
12974
|
+
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12975
|
+
elements.set(
|
|
12976
|
+
tag,
|
|
12977
|
+
new Element({
|
|
12978
|
+
tag,
|
|
12979
|
+
vr,
|
|
12980
|
+
vm: seq.items.length,
|
|
12981
|
+
length,
|
|
12982
|
+
rawBytes: valueSlice,
|
|
12983
|
+
byteOffset: headerStart,
|
|
12984
|
+
littleEndian: true,
|
|
12985
|
+
items: seq.items,
|
|
12986
|
+
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12987
|
+
})
|
|
12988
|
+
);
|
|
12989
|
+
continue;
|
|
12990
|
+
}
|
|
12991
|
+
}
|
|
12836
12992
|
if (element === 0 && group !== 2) {
|
|
12837
12993
|
emit(groupLengthInDataset(position, tag));
|
|
12838
12994
|
}
|
|
@@ -12840,7 +12996,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12840
12996
|
registerPrivateCreator(tag, valueSlice, ctx);
|
|
12841
12997
|
}
|
|
12842
12998
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
12843
|
-
const privateCreator = resolvePrivateCreator(tag, ctx);
|
|
12999
|
+
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12844
13000
|
elements.set(
|
|
12845
13001
|
tag,
|
|
12846
13002
|
new Element({
|
|
@@ -12960,7 +13116,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
12960
13116
|
}
|
|
12961
13117
|
cursor.position = seqResult.endOffset;
|
|
12962
13118
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
12963
|
-
const privateCreator2 = resolvePrivateCreator(tag, ctx);
|
|
13119
|
+
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12964
13120
|
elements.set(
|
|
12965
13121
|
tag,
|
|
12966
13122
|
new Element({
|
|
@@ -13012,12 +13168,13 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13012
13168
|
UNDEFINED_LENGTH2,
|
|
13013
13169
|
ctx,
|
|
13014
13170
|
emit,
|
|
13015
|
-
parseImplicitLE
|
|
13171
|
+
parseImplicitLE,
|
|
13172
|
+
tag
|
|
13016
13173
|
);
|
|
13017
13174
|
if (cp246.success) {
|
|
13018
13175
|
cursor.position = cp246.endOffset;
|
|
13019
13176
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13020
|
-
const privateCreator3 = resolvePrivateCreator(tag, ctx);
|
|
13177
|
+
const privateCreator3 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13021
13178
|
elements.set(
|
|
13022
13179
|
tag,
|
|
13023
13180
|
new Element({
|
|
@@ -13036,7 +13193,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13036
13193
|
);
|
|
13037
13194
|
continue;
|
|
13038
13195
|
}
|
|
13039
|
-
const privateCreator2 = resolvePrivateCreator(tag, ctx);
|
|
13196
|
+
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13040
13197
|
const fallbackEnd = buffer.length;
|
|
13041
13198
|
const fallbackBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, fallbackEnd)) : buffer.subarray(headerStart, fallbackEnd);
|
|
13042
13199
|
elements.set(
|
|
@@ -13083,7 +13240,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13083
13240
|
registerPrivateCreator(tag, valueSlice, ctx);
|
|
13084
13241
|
}
|
|
13085
13242
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
13086
|
-
const privateCreator = resolvePrivateCreator(tag, ctx);
|
|
13243
|
+
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13087
13244
|
const finalVr = vr;
|
|
13088
13245
|
elements.set(
|
|
13089
13246
|
tag,
|
|
@@ -13136,20 +13293,22 @@ function parseDeflatedLEWithCap(buffer, datasetStart, ctx, emit, maxInflatedByte
|
|
|
13136
13293
|
buildSnippet(buffer, datasetStart)
|
|
13137
13294
|
);
|
|
13138
13295
|
}
|
|
13296
|
+
const zlibCode = typeof code === "string" && code.length > 0 ? code : "unknown";
|
|
13139
13297
|
throw new DicomParseError(
|
|
13140
13298
|
FATAL_CODES.INVALID_FILE_META,
|
|
13141
|
-
`Failed to inflate Deflated TS payload: ${
|
|
13299
|
+
`Failed to inflate Deflated TS payload (zlib error code: ${zlibCode}).`,
|
|
13142
13300
|
datasetStart,
|
|
13143
13301
|
buildSnippet(buffer, datasetStart)
|
|
13144
13302
|
);
|
|
13145
13303
|
}
|
|
13146
13304
|
const innerCtx = { ...ctx, buffer: inflated };
|
|
13305
|
+
const innerChokepoint = makeEmitter(innerCtx);
|
|
13147
13306
|
const innerEmit = (w) => {
|
|
13148
13307
|
const wrapped = {
|
|
13149
13308
|
...w,
|
|
13150
13309
|
position: { ...w.position, deflated: true }
|
|
13151
13310
|
};
|
|
13152
|
-
|
|
13311
|
+
innerChokepoint(wrapped);
|
|
13153
13312
|
};
|
|
13154
13313
|
const result = parseExplicitLE(inflated, 0, innerCtx, innerEmit);
|
|
13155
13314
|
return { elements: result.elements, endOffset: buffer.length };
|
|
@@ -13176,6 +13335,7 @@ var TRANSFER_SYNTAX_PARSERS = Object.freeze({
|
|
|
13176
13335
|
});
|
|
13177
13336
|
|
|
13178
13337
|
// src/parser/index.ts
|
|
13338
|
+
var SUPPORTED_TRANSFER_SYNTAXES = Object.keys(TRANSFER_SYNTAX_PARSERS).join(", ");
|
|
13179
13339
|
function parseDicom(input, options = {}) {
|
|
13180
13340
|
if (rawInputIsEmpty(input)) {
|
|
13181
13341
|
throw new DicomParseError(FATAL_CODES.EMPTY_INPUT, "Input is empty.", 0, "");
|
|
@@ -13198,9 +13358,10 @@ function parseDicom(input, options = {}) {
|
|
|
13198
13358
|
const strategy = TRANSFER_SYNTAX_PARSERS[tsUid];
|
|
13199
13359
|
if (strategy === void 0) {
|
|
13200
13360
|
const tsName = uid(tsUid)?.name ?? "";
|
|
13361
|
+
const named = tsName.length > 0 ? `Transfer Syntax ${tsName}` : "The Transfer Syntax UID";
|
|
13201
13362
|
throw new DicomParseError(
|
|
13202
13363
|
FATAL_CODES.UNSUPPORTED_TRANSFER_SYNTAX,
|
|
13203
|
-
|
|
13364
|
+
`${named} in (0002,0010) is not supported by @cosyte/dicom v1 (supported: ${SUPPORTED_TRANSFER_SYNTAXES}).`,
|
|
13204
13365
|
fileMetaEnd,
|
|
13205
13366
|
tsName.length > 0 ? tsName : buildSnippet(buffer, fileMetaEnd)
|
|
13206
13367
|
);
|
|
@@ -13438,7 +13599,7 @@ function serializeDicom(ds) {
|
|
|
13438
13599
|
if (encoding === void 0) {
|
|
13439
13600
|
throw new DicomSerializeError(
|
|
13440
13601
|
SERIALIZE_ERROR_CODES.UNSUPPORTED_TRANSFER_SYNTAX,
|
|
13441
|
-
`Transfer Syntax UID
|
|
13602
|
+
`The Dataset's File Meta Transfer Syntax UID is not supported by the @cosyte/dicom v1 writer (supported: ${Object.keys(BODY_ENCODING).join(", ")}).`
|
|
13442
13603
|
);
|
|
13443
13604
|
}
|
|
13444
13605
|
const preamble = part10Preamble();
|
|
@@ -14537,6 +14698,132 @@ function uidValueMultiplicity(rawBytes) {
|
|
|
14537
14698
|
return raw.split("\\").length;
|
|
14538
14699
|
}
|
|
14539
14700
|
|
|
14701
|
+
// src/deident/embedded.ts
|
|
14702
|
+
var KNOWN_VRS2 = new Set(Object.keys(BE_VR_STRIDE));
|
|
14703
|
+
var UNDEFINED_LENGTH4 = 4294967295;
|
|
14704
|
+
var FFFE_GROUP = 65534;
|
|
14705
|
+
var SCANNABLE_CARRIER_VRS = /* @__PURE__ */ new Set([
|
|
14706
|
+
"AE",
|
|
14707
|
+
"AS",
|
|
14708
|
+
"CS",
|
|
14709
|
+
"DA",
|
|
14710
|
+
"DS",
|
|
14711
|
+
"DT",
|
|
14712
|
+
"IS",
|
|
14713
|
+
"LO",
|
|
14714
|
+
"LT",
|
|
14715
|
+
"PN",
|
|
14716
|
+
"SH",
|
|
14717
|
+
"ST",
|
|
14718
|
+
"TM",
|
|
14719
|
+
"UC",
|
|
14720
|
+
"UI",
|
|
14721
|
+
"UR",
|
|
14722
|
+
"UT"
|
|
14723
|
+
]);
|
|
14724
|
+
var ALLOWED_CONTROL_BYTES = /* @__PURE__ */ new Set([
|
|
14725
|
+
9,
|
|
14726
|
+
// TAB
|
|
14727
|
+
10,
|
|
14728
|
+
// LF
|
|
14729
|
+
12,
|
|
14730
|
+
// FF
|
|
14731
|
+
13,
|
|
14732
|
+
// CR
|
|
14733
|
+
27
|
|
14734
|
+
// ESC
|
|
14735
|
+
]);
|
|
14736
|
+
var CONTROL_TOLERANT_VRS = /* @__PURE__ */ new Set(["LT", "ST", "UT"]);
|
|
14737
|
+
var ESC_ONLY_VRS = /* @__PURE__ */ new Set(["LO", "SH", "UC", "PN"]);
|
|
14738
|
+
var MAX_SCAN_BYTES = 1 << 20;
|
|
14739
|
+
var MIN_HEADER = 8;
|
|
14740
|
+
function readU16(buf, off, littleEndian) {
|
|
14741
|
+
return littleEndian ? buf.readUInt16LE(off) : buf.readUInt16BE(off);
|
|
14742
|
+
}
|
|
14743
|
+
function readU32(buf, off, littleEndian) {
|
|
14744
|
+
return littleEndian ? buf.readUInt32LE(off) : buf.readUInt32BE(off);
|
|
14745
|
+
}
|
|
14746
|
+
function decodeAt(value, off, encoding) {
|
|
14747
|
+
if (off + MIN_HEADER > value.length) return void 0;
|
|
14748
|
+
const littleEndian = encoding !== "explicitBE";
|
|
14749
|
+
const group = readU16(value, off, littleEndian);
|
|
14750
|
+
const element = readU16(value, off + 2, littleEndian);
|
|
14751
|
+
if (group === FFFE_GROUP) {
|
|
14752
|
+
return { next: off + 8, tag: void 0 };
|
|
14753
|
+
}
|
|
14754
|
+
if (group === 0 && element === 0) return void 0;
|
|
14755
|
+
const tag = joinTag(group, element);
|
|
14756
|
+
if (encoding === "implicit") {
|
|
14757
|
+
const length2 = readU32(value, off + 4, littleEndian);
|
|
14758
|
+
if (length2 === UNDEFINED_LENGTH4 || length2 % 2 !== 0) return void 0;
|
|
14759
|
+
const next2 = off + 8 + length2;
|
|
14760
|
+
return next2 > value.length ? void 0 : { next: next2, tag };
|
|
14761
|
+
}
|
|
14762
|
+
const vr = value.toString("latin1", off + 4, off + 6);
|
|
14763
|
+
if (!KNOWN_VRS2.has(vr)) return void 0;
|
|
14764
|
+
if (LONG_FORM_VRS.has(vr)) {
|
|
14765
|
+
if (off + 12 > value.length) return void 0;
|
|
14766
|
+
if (value[off + 6] !== 0 || value[off + 7] !== 0) return void 0;
|
|
14767
|
+
const length2 = readU32(value, off + 8, littleEndian);
|
|
14768
|
+
if (length2 === UNDEFINED_LENGTH4 || length2 % 2 !== 0) return void 0;
|
|
14769
|
+
const next2 = off + 12 + length2;
|
|
14770
|
+
return next2 > value.length ? void 0 : { next: next2, tag };
|
|
14771
|
+
}
|
|
14772
|
+
const length = readU16(value, off + 6, littleEndian);
|
|
14773
|
+
if (length % 2 !== 0) return void 0;
|
|
14774
|
+
const next = off + 8 + length;
|
|
14775
|
+
return next > value.length ? void 0 : { next, tag };
|
|
14776
|
+
}
|
|
14777
|
+
function isScannableCarrier(vr) {
|
|
14778
|
+
return SCANNABLE_CARRIER_VRS.has(vr);
|
|
14779
|
+
}
|
|
14780
|
+
function hasByteOutsideRepertoire(region, carrierVr) {
|
|
14781
|
+
const allFive = CONTROL_TOLERANT_VRS.has(carrierVr);
|
|
14782
|
+
const escOnly = ESC_ONLY_VRS.has(carrierVr);
|
|
14783
|
+
for (const byte of region) {
|
|
14784
|
+
if (byte >= 32 && byte !== 127) continue;
|
|
14785
|
+
if (allFive && ALLOWED_CONTROL_BYTES.has(byte)) continue;
|
|
14786
|
+
if (escOnly && byte === 27) continue;
|
|
14787
|
+
return true;
|
|
14788
|
+
}
|
|
14789
|
+
return false;
|
|
14790
|
+
}
|
|
14791
|
+
function findEmbeddedAttributes(value, carrierVr, encoding, isActionable) {
|
|
14792
|
+
if (!isScannableCarrier(carrierVr)) return void 0;
|
|
14793
|
+
if (value.length < MIN_HEADER) return void 0;
|
|
14794
|
+
const windowStart = value.length > MAX_SCAN_BYTES ? value.length - MAX_SCAN_BYTES : 0;
|
|
14795
|
+
const window = value.subarray(windowStart);
|
|
14796
|
+
const n = window.length;
|
|
14797
|
+
const half = (n >> 1) + 1;
|
|
14798
|
+
const tiles = new Uint8Array(half);
|
|
14799
|
+
const hit = new Uint8Array(half);
|
|
14800
|
+
for (let off = n - MIN_HEADER & -2; off >= 0; off -= 2) {
|
|
14801
|
+
const decoded = decodeAt(window, off, encoding);
|
|
14802
|
+
if (decoded === void 0) continue;
|
|
14803
|
+
const atEnd = decoded.next === n;
|
|
14804
|
+
const onward = decoded.next < n ? tiles[decoded.next >> 1] === 1 : false;
|
|
14805
|
+
if (!atEnd && !onward) continue;
|
|
14806
|
+
tiles[off >> 1] = 1;
|
|
14807
|
+
const actionable = decoded.tag !== void 0 && isActionable(decoded.tag);
|
|
14808
|
+
const onwardHit = decoded.next < n ? hit[decoded.next >> 1] === 1 : false;
|
|
14809
|
+
if (actionable || onwardHit) hit[off >> 1] = 1;
|
|
14810
|
+
}
|
|
14811
|
+
for (let off = 0; off + MIN_HEADER <= n; off += 2) {
|
|
14812
|
+
if (hit[off >> 1] !== 1) continue;
|
|
14813
|
+
if (!hasByteOutsideRepertoire(window.subarray(off), carrierVr)) return void 0;
|
|
14814
|
+
const tags = [];
|
|
14815
|
+
let pos = off;
|
|
14816
|
+
while (pos < n) {
|
|
14817
|
+
const decoded = decodeAt(window, pos, encoding);
|
|
14818
|
+
if (decoded === void 0) break;
|
|
14819
|
+
if (decoded.tag !== void 0) tags.push(decoded.tag);
|
|
14820
|
+
pos = decoded.next;
|
|
14821
|
+
}
|
|
14822
|
+
return tags;
|
|
14823
|
+
}
|
|
14824
|
+
return void 0;
|
|
14825
|
+
}
|
|
14826
|
+
|
|
14540
14827
|
// src/deident/types.ts
|
|
14541
14828
|
var DEIDENTIFY_OPTIONS = Object.freeze([
|
|
14542
14829
|
"CleanGraphics",
|
|
@@ -14608,6 +14895,9 @@ var BODY_ENCODING2 = {
|
|
|
14608
14895
|
"1.2.840.10008.1.2.2": "explicitBE",
|
|
14609
14896
|
"1.2.840.10008.1.2.1.99": "explicitLE"
|
|
14610
14897
|
};
|
|
14898
|
+
var MAX_UNAUDITABLE_SEQUENCE_FINDINGS = 64;
|
|
14899
|
+
var MAX_UNDEFINED_VR_FINDINGS = 64;
|
|
14900
|
+
var KNOWN_VRS3 = new Set(Object.keys(BE_VR_STRIDE));
|
|
14611
14901
|
function validateRetain(retain) {
|
|
14612
14902
|
const valid = new Set(DEIDENTIFY_OPTIONS);
|
|
14613
14903
|
const active = /* @__PURE__ */ new Set();
|
|
@@ -14721,12 +15011,83 @@ function isPrivateCreatorElement(tag) {
|
|
|
14721
15011
|
const { element } = splitTag(tag);
|
|
14722
15012
|
return element >= 16 && element <= 255;
|
|
14723
15013
|
}
|
|
14724
|
-
function
|
|
15014
|
+
function actsOnTag(tag, ctx) {
|
|
15015
|
+
if (isPrivateTag(tag)) return true;
|
|
15016
|
+
const action = annexE(tag);
|
|
15017
|
+
if (action === void 0) return false;
|
|
15018
|
+
return resolveAction(effectiveCode(action, ctx.active)) !== "K";
|
|
15019
|
+
}
|
|
15020
|
+
function hasUndefinedVr(el) {
|
|
15021
|
+
return !KNOWN_VRS3.has(el.vr);
|
|
15022
|
+
}
|
|
15023
|
+
function keepOrEmpty(el, ctx, contextPath, out) {
|
|
15024
|
+
if (hasUndefinedVr(el)) {
|
|
15025
|
+
emptyUndefinedVrElement(el, ctx, contextPath, out);
|
|
15026
|
+
return false;
|
|
15027
|
+
}
|
|
15028
|
+
const hidden = findEmbeddedAttributes(el.rawBytes, el.vr, ctx.encoding, (t) => actsOnTag(t, ctx));
|
|
15029
|
+
if (hidden === void 0) {
|
|
15030
|
+
out.elements.set(el.tag, el);
|
|
15031
|
+
return true;
|
|
15032
|
+
}
|
|
15033
|
+
out.elements.set(el.tag, freshScalar(el, buffer.Buffer.alloc(0), 0));
|
|
15034
|
+
out.embeddedAttributes.push({
|
|
15035
|
+
tag: el.tag,
|
|
15036
|
+
vr: el.vr,
|
|
15037
|
+
hidden,
|
|
15038
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15039
|
+
});
|
|
15040
|
+
out.warnings.push(
|
|
15041
|
+
embeddedAttributeRemoved({ byteOffset: el.byteOffset }, el.tag, el.vr, hidden.length)
|
|
15042
|
+
);
|
|
15043
|
+
return false;
|
|
15044
|
+
}
|
|
15045
|
+
function emptyUndefinedVrElement(el, ctx, contextPath, out) {
|
|
15046
|
+
out.elements.set(el.tag, freshScalar(el, buffer.Buffer.alloc(0), 0));
|
|
15047
|
+
if (ctx.budget.undefinedVrElements >= MAX_UNDEFINED_VR_FINDINGS) return;
|
|
15048
|
+
ctx.budget.undefinedVrElements += 1;
|
|
15049
|
+
out.undefinedVrElements.push({
|
|
15050
|
+
byteOffset: el.byteOffset,
|
|
15051
|
+
byteLength: el.rawBytes.length,
|
|
15052
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15053
|
+
});
|
|
15054
|
+
out.warnings.push(undefinedVrNotAuditable({ byteOffset: el.byteOffset }, el.rawBytes.length));
|
|
15055
|
+
}
|
|
15056
|
+
function isUnauditableSequence(el) {
|
|
15057
|
+
return el.vr === "SQ" && el.items === void 0;
|
|
15058
|
+
}
|
|
15059
|
+
function emptyUnauditableSequence(el, ctx, contextPath, out) {
|
|
15060
|
+
out.elements.set(el.tag, rebuildSequence(el, [], ctx.encoding));
|
|
15061
|
+
if (ctx.budget.unauditableSequences >= MAX_UNAUDITABLE_SEQUENCE_FINDINGS) return;
|
|
15062
|
+
ctx.budget.unauditableSequences += 1;
|
|
15063
|
+
out.unauditableSequences.push({
|
|
15064
|
+
tag: el.tag,
|
|
15065
|
+
byteLength: el.rawBytes.length,
|
|
15066
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15067
|
+
});
|
|
15068
|
+
out.warnings.push(
|
|
15069
|
+
sequenceNotAuditable({ byteOffset: el.byteOffset }, el.tag, el.rawBytes.length)
|
|
15070
|
+
);
|
|
15071
|
+
}
|
|
15072
|
+
function creatorsInScope(source) {
|
|
15073
|
+
const byBlock = /* @__PURE__ */ new Map();
|
|
15074
|
+
for (const el of source) {
|
|
15075
|
+
if (!isPrivateTag(el.tag) || !isPrivateCreatorElement(el.tag)) continue;
|
|
15076
|
+
const { group, element } = splitTag(el.tag);
|
|
15077
|
+
byBlock.set(`${String(group)}:${String(element & 255)}`, decodeCreator(el));
|
|
15078
|
+
}
|
|
15079
|
+
return byBlock;
|
|
15080
|
+
}
|
|
15081
|
+
function creatorFor(tag, creators) {
|
|
15082
|
+
const { group, element } = splitTag(tag);
|
|
15083
|
+
return creators.get(`${String(group)}:${String(element >> 8 & 255)}`);
|
|
15084
|
+
}
|
|
15085
|
+
function keepsPrivate(el, ctx, creators) {
|
|
14725
15086
|
if (!ctx.active.has("RetainSafePrivate") || ctx.profile === void 0) return false;
|
|
14726
15087
|
if (isPrivateCreatorElement(el.tag)) {
|
|
14727
15088
|
return ctx.profile.privateDictionary.has(decodeCreator(el));
|
|
14728
15089
|
}
|
|
14729
|
-
const creator = el.
|
|
15090
|
+
const creator = creatorFor(el.tag, creators);
|
|
14730
15091
|
if (creator === void 0) return false;
|
|
14731
15092
|
return resolvePrivateTag(ctx.profile, el.tag, creator) !== void 0;
|
|
14732
15093
|
}
|
|
@@ -14734,20 +15095,26 @@ function processElements(source, ctx, contextPath) {
|
|
|
14734
15095
|
const out = {
|
|
14735
15096
|
elements: /* @__PURE__ */ new Map(),
|
|
14736
15097
|
attributes: [],
|
|
14737
|
-
removedPrivateTags: []
|
|
15098
|
+
removedPrivateTags: [],
|
|
15099
|
+
embeddedAttributes: [],
|
|
15100
|
+
unauditableSequences: [],
|
|
15101
|
+
undefinedVrElements: [],
|
|
15102
|
+
warnings: []
|
|
14738
15103
|
};
|
|
15104
|
+
const creators = creatorsInScope(source);
|
|
14739
15105
|
for (const el of source) {
|
|
14740
15106
|
if (isPrivateTag(el.tag)) {
|
|
14741
|
-
if (keepsPrivate(el, ctx))
|
|
15107
|
+
if (keepsPrivate(el, ctx, creators)) keepOrEmpty(el, ctx, contextPath, out);
|
|
14742
15108
|
else out.removedPrivateTags.push(el.tag);
|
|
14743
15109
|
continue;
|
|
14744
15110
|
}
|
|
14745
15111
|
const action = annexE(el.tag);
|
|
14746
15112
|
if (action === void 0) {
|
|
14747
|
-
if (el.vr === "SQ"
|
|
14748
|
-
|
|
15113
|
+
if (el.vr === "SQ") {
|
|
15114
|
+
if (isUnauditableSequence(el)) emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15115
|
+
else out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
14749
15116
|
} else {
|
|
14750
|
-
|
|
15117
|
+
keepOrEmpty(el, ctx, contextPath, out);
|
|
14751
15118
|
}
|
|
14752
15119
|
continue;
|
|
14753
15120
|
}
|
|
@@ -14759,8 +15126,7 @@ function processElements(source, ctx, contextPath) {
|
|
|
14759
15126
|
let applied;
|
|
14760
15127
|
switch (resolved) {
|
|
14761
15128
|
case "K":
|
|
14762
|
-
|
|
14763
|
-
applied = "kept";
|
|
15129
|
+
applied = keepOrEmpty(el, ctx, contextPath, out) ? "kept" : "emptied";
|
|
14764
15130
|
break;
|
|
14765
15131
|
case "X":
|
|
14766
15132
|
applied = "removed";
|
|
@@ -14802,6 +15168,10 @@ function descendSequence(el, ctx, contextPath, out) {
|
|
|
14802
15168
|
const inner = processElements(item.elements(), ctx, childPath);
|
|
14803
15169
|
out.attributes.push(...inner.attributes);
|
|
14804
15170
|
out.removedPrivateTags.push(...inner.removedPrivateTags);
|
|
15171
|
+
out.embeddedAttributes.push(...inner.embeddedAttributes);
|
|
15172
|
+
out.unauditableSequences.push(...inner.unauditableSequences);
|
|
15173
|
+
out.undefinedVrElements.push(...inner.undefinedVrElements);
|
|
15174
|
+
out.warnings.push(...inner.warnings);
|
|
14805
15175
|
newItems.push(new Item({ index, warnings: [], elements: inner.elements }));
|
|
14806
15176
|
});
|
|
14807
15177
|
return rebuildSequence(el, newItems, ctx.encoding);
|
|
@@ -14818,13 +15188,23 @@ function applySequenceAction(el, resolved, action, ctx, contextPath, out) {
|
|
|
14818
15188
|
applied = "emptied";
|
|
14819
15189
|
break;
|
|
14820
15190
|
case "C":
|
|
14821
|
-
|
|
14822
|
-
|
|
15191
|
+
if (isUnauditableSequence(el)) {
|
|
15192
|
+
emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15193
|
+
applied = "emptied";
|
|
15194
|
+
} else {
|
|
15195
|
+
out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
15196
|
+
applied = "cleaned";
|
|
15197
|
+
}
|
|
14823
15198
|
break;
|
|
14824
15199
|
case "U":
|
|
14825
15200
|
case "K":
|
|
14826
|
-
|
|
14827
|
-
|
|
15201
|
+
if (isUnauditableSequence(el)) {
|
|
15202
|
+
emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15203
|
+
applied = "emptied";
|
|
15204
|
+
} else {
|
|
15205
|
+
out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
15206
|
+
applied = "kept";
|
|
15207
|
+
}
|
|
14828
15208
|
break;
|
|
14829
15209
|
}
|
|
14830
15210
|
out.attributes.push(auditAttribute(el.tag, action, resolved, applied, contextPath));
|
|
@@ -14870,8 +15250,18 @@ function deidentify(ds, options = {}) {
|
|
|
14870
15250
|
active,
|
|
14871
15251
|
remap,
|
|
14872
15252
|
profile: options.profile,
|
|
14873
|
-
encoding
|
|
14874
|
-
|
|
15253
|
+
encoding,
|
|
15254
|
+
budget: { unauditableSequences: 0, undefinedVrElements: 0 }
|
|
15255
|
+
};
|
|
15256
|
+
const processed = processElements(ds.elements(), ctx, []);
|
|
15257
|
+
const {
|
|
15258
|
+
elements,
|
|
15259
|
+
attributes,
|
|
15260
|
+
removedPrivateTags,
|
|
15261
|
+
embeddedAttributes,
|
|
15262
|
+
unauditableSequences,
|
|
15263
|
+
undefinedVrElements
|
|
15264
|
+
} = processed;
|
|
14875
15265
|
elements.set(
|
|
14876
15266
|
TAG_PATIENT_IDENTITY_REMOVED,
|
|
14877
15267
|
insertedScalar(TAG_PATIENT_IDENTITY_REMOVED, "CS", buffer.Buffer.from("YES", "latin1"), littleEndian)
|
|
@@ -14881,7 +15271,7 @@ function deidentify(ds, options = {}) {
|
|
|
14881
15271
|
TAG_DEIDENTIFICATION_METHOD,
|
|
14882
15272
|
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO", buffer.Buffer.from(method, "latin1"), littleEndian)
|
|
14883
15273
|
);
|
|
14884
|
-
const warnings = [];
|
|
15274
|
+
const warnings = [...processed.warnings];
|
|
14885
15275
|
if (hasUncleanedBurnedIn(ds)) {
|
|
14886
15276
|
const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
|
|
14887
15277
|
warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
|
|
@@ -14896,6 +15286,9 @@ function deidentify(ds, options = {}) {
|
|
|
14896
15286
|
const report = {
|
|
14897
15287
|
attributes,
|
|
14898
15288
|
removedPrivateTags,
|
|
15289
|
+
embeddedAttributes,
|
|
15290
|
+
unauditableSequences,
|
|
15291
|
+
undefinedVrElements,
|
|
14899
15292
|
uidMap: remap.cache,
|
|
14900
15293
|
warnings,
|
|
14901
15294
|
retained: [...active]
|