@cosyte/dicom 0.0.6 → 0.0.8
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 +376 -0
- package/README.md +6 -2
- package/dist/index.cjs +276 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +276 -10
- package/dist/index.d.ts +276 -10
- package/dist/index.mjs +276 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/version.ts
|
|
12
|
-
var VERSION = "0.0.
|
|
12
|
+
var VERSION = "0.0.8";
|
|
13
13
|
|
|
14
14
|
// src/dictionary/index.ts
|
|
15
15
|
var dictionary_exports = {};
|
|
@@ -11733,6 +11733,12 @@ var WARNING_CODES = {
|
|
|
11733
11733
|
// === Reserved by later phases (declared, not emitted in Phase 2) ===
|
|
11734
11734
|
DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED: "DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED",
|
|
11735
11735
|
// reserved by Phase 7 - not emitted in Phase 2
|
|
11736
|
+
DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED: "DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED",
|
|
11737
|
+
// emitted by deidentify(), never by the parser
|
|
11738
|
+
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE",
|
|
11739
|
+
// emitted by deidentify(), never by the parser
|
|
11740
|
+
DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE",
|
|
11741
|
+
// emitted by deidentify(), never by the parser
|
|
11736
11742
|
DICOM_PRIVATE_CREATOR_UNKNOWN: "DICOM_PRIVATE_CREATOR_UNKNOWN"
|
|
11737
11743
|
// reserved by Phase 6 - not emitted in Phase 2
|
|
11738
11744
|
};
|
|
@@ -11746,13 +11752,30 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
11746
11752
|
DICOM_PRIVATE_TAG_NO_CREATOR: "Private element ({tag}) has no Private Creator registered for its block; treating as VR=UN.",
|
|
11747
11753
|
DICOM_GROUP_LENGTH_IN_DATASET: "Retired Group Length element ({tag}) encountered in dataset; preserved as-is.",
|
|
11748
11754
|
DICOM_NONZERO_RESERVED_BYTES: "Element ({tag}) has non-zero reserved bytes between VR and length (first byte {n}, second byte {n2}); ignoring.",
|
|
11749
|
-
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
|
|
11755
|
+
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.",
|
|
11750
11756
|
DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
|
|
11751
11757
|
DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
|
|
11752
11758
|
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length {n} does not match computed {n2} bytes.",
|
|
11753
11759
|
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "Private element ({tag}) under Implicit VR LE has no VR override; falling back to UN.",
|
|
11754
11760
|
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.",
|
|
11755
11761
|
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.",
|
|
11762
|
+
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.",
|
|
11763
|
+
// Deliberately short. One of these is raised per un-auditable element, so a
|
|
11764
|
+
// long message is multiplied by an element count the input controls; the
|
|
11765
|
+
// reasoning belongs in the docs, not in a string repeated thousands of times.
|
|
11766
|
+
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.",
|
|
11767
|
+
// Deliberately short for the same reason as the code above: one per element,
|
|
11768
|
+
// and the element count is chosen by the input.
|
|
11769
|
+
//
|
|
11770
|
+
// NEITHER THE TAG NOR THE VR IS ECHOED, and that is specific to this code
|
|
11771
|
+
// rather than caution. Every other factory's {tag} is composed from a real
|
|
11772
|
+
// Data Element header; the condition that raises THIS one is that the header
|
|
11773
|
+
// was fabricated from bytes inside some element's value, so its four tag bytes
|
|
11774
|
+
// and its two VR bytes are document content. `renderTag` shape-checks a tag
|
|
11775
|
+
// and cannot refuse one, so the withholding has to happen at the call site.
|
|
11776
|
+
// The byte offset locates the element instead, and is a position this parser
|
|
11777
|
+
// counted rather than anything the document said.
|
|
11778
|
+
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.",
|
|
11756
11779
|
DICOM_BOM_IN_TEXT_VR: "Element ({tag}) {vr} value begins with a UTF-8 BOM; stripped on decode.",
|
|
11757
11780
|
DICOM_TRAILING_NULL_IN_TEXT_VR: "Element ({tag}) {vr} value has a trailing NULL pad where SPACE is expected; trimmed.",
|
|
11758
11781
|
DICOM_UI_TRAILING_SPACE: "Element ({tag}) UI value is SPACE-padded; UI requires NULL padding (PS3.5 \xA76.2). Trimmed.",
|
|
@@ -11830,6 +11853,25 @@ function privateCreatorUnknown(position, tag) {
|
|
|
11830
11853
|
function burnedInAnnotationNotRemoved(position) {
|
|
11831
11854
|
return build(WARNING_CODES.DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED, position);
|
|
11832
11855
|
}
|
|
11856
|
+
function embeddedAttributeRemoved(position, tag, vr, count) {
|
|
11857
|
+
return build(WARNING_CODES.DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED, position, {
|
|
11858
|
+
tag,
|
|
11859
|
+
vr,
|
|
11860
|
+
n: count
|
|
11861
|
+
});
|
|
11862
|
+
}
|
|
11863
|
+
function sequenceNotAuditable(position, tag, byteLength) {
|
|
11864
|
+
return build(WARNING_CODES.DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE, position, {
|
|
11865
|
+
tag,
|
|
11866
|
+
n: byteLength
|
|
11867
|
+
});
|
|
11868
|
+
}
|
|
11869
|
+
function undefinedVrNotAuditable(position, byteLength) {
|
|
11870
|
+
return build(WARNING_CODES.DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE, position, {
|
|
11871
|
+
n: byteLength,
|
|
11872
|
+
n2: position.byteOffset
|
|
11873
|
+
});
|
|
11874
|
+
}
|
|
11833
11875
|
function bomInTextVR(position, tag, vr) {
|
|
11834
11876
|
return build(WARNING_CODES.DICOM_BOM_IN_TEXT_VR, position, { tag, vr });
|
|
11835
11877
|
}
|
|
@@ -14654,6 +14696,132 @@ function uidValueMultiplicity(rawBytes) {
|
|
|
14654
14696
|
return raw.split("\\").length;
|
|
14655
14697
|
}
|
|
14656
14698
|
|
|
14699
|
+
// src/deident/embedded.ts
|
|
14700
|
+
var KNOWN_VRS2 = new Set(Object.keys(BE_VR_STRIDE));
|
|
14701
|
+
var UNDEFINED_LENGTH4 = 4294967295;
|
|
14702
|
+
var FFFE_GROUP = 65534;
|
|
14703
|
+
var SCANNABLE_CARRIER_VRS = /* @__PURE__ */ new Set([
|
|
14704
|
+
"AE",
|
|
14705
|
+
"AS",
|
|
14706
|
+
"CS",
|
|
14707
|
+
"DA",
|
|
14708
|
+
"DS",
|
|
14709
|
+
"DT",
|
|
14710
|
+
"IS",
|
|
14711
|
+
"LO",
|
|
14712
|
+
"LT",
|
|
14713
|
+
"PN",
|
|
14714
|
+
"SH",
|
|
14715
|
+
"ST",
|
|
14716
|
+
"TM",
|
|
14717
|
+
"UC",
|
|
14718
|
+
"UI",
|
|
14719
|
+
"UR",
|
|
14720
|
+
"UT"
|
|
14721
|
+
]);
|
|
14722
|
+
var ALLOWED_CONTROL_BYTES = /* @__PURE__ */ new Set([
|
|
14723
|
+
9,
|
|
14724
|
+
// TAB
|
|
14725
|
+
10,
|
|
14726
|
+
// LF
|
|
14727
|
+
12,
|
|
14728
|
+
// FF
|
|
14729
|
+
13,
|
|
14730
|
+
// CR
|
|
14731
|
+
27
|
|
14732
|
+
// ESC
|
|
14733
|
+
]);
|
|
14734
|
+
var CONTROL_TOLERANT_VRS = /* @__PURE__ */ new Set(["LT", "ST", "UT"]);
|
|
14735
|
+
var ESC_ONLY_VRS = /* @__PURE__ */ new Set(["LO", "SH", "UC", "PN"]);
|
|
14736
|
+
var MAX_SCAN_BYTES = 1 << 20;
|
|
14737
|
+
var MIN_HEADER = 8;
|
|
14738
|
+
function readU16(buf, off, littleEndian) {
|
|
14739
|
+
return littleEndian ? buf.readUInt16LE(off) : buf.readUInt16BE(off);
|
|
14740
|
+
}
|
|
14741
|
+
function readU32(buf, off, littleEndian) {
|
|
14742
|
+
return littleEndian ? buf.readUInt32LE(off) : buf.readUInt32BE(off);
|
|
14743
|
+
}
|
|
14744
|
+
function decodeAt(value, off, encoding) {
|
|
14745
|
+
if (off + MIN_HEADER > value.length) return void 0;
|
|
14746
|
+
const littleEndian = encoding !== "explicitBE";
|
|
14747
|
+
const group = readU16(value, off, littleEndian);
|
|
14748
|
+
const element = readU16(value, off + 2, littleEndian);
|
|
14749
|
+
if (group === FFFE_GROUP) {
|
|
14750
|
+
return { next: off + 8, tag: void 0 };
|
|
14751
|
+
}
|
|
14752
|
+
if (group === 0 && element === 0) return void 0;
|
|
14753
|
+
const tag = joinTag(group, element);
|
|
14754
|
+
if (encoding === "implicit") {
|
|
14755
|
+
const length2 = readU32(value, off + 4, littleEndian);
|
|
14756
|
+
if (length2 === UNDEFINED_LENGTH4 || length2 % 2 !== 0) return void 0;
|
|
14757
|
+
const next2 = off + 8 + length2;
|
|
14758
|
+
return next2 > value.length ? void 0 : { next: next2, tag };
|
|
14759
|
+
}
|
|
14760
|
+
const vr = value.toString("latin1", off + 4, off + 6);
|
|
14761
|
+
if (!KNOWN_VRS2.has(vr)) return void 0;
|
|
14762
|
+
if (LONG_FORM_VRS.has(vr)) {
|
|
14763
|
+
if (off + 12 > value.length) return void 0;
|
|
14764
|
+
if (value[off + 6] !== 0 || value[off + 7] !== 0) return void 0;
|
|
14765
|
+
const length2 = readU32(value, off + 8, littleEndian);
|
|
14766
|
+
if (length2 === UNDEFINED_LENGTH4 || length2 % 2 !== 0) return void 0;
|
|
14767
|
+
const next2 = off + 12 + length2;
|
|
14768
|
+
return next2 > value.length ? void 0 : { next: next2, tag };
|
|
14769
|
+
}
|
|
14770
|
+
const length = readU16(value, off + 6, littleEndian);
|
|
14771
|
+
if (length % 2 !== 0) return void 0;
|
|
14772
|
+
const next = off + 8 + length;
|
|
14773
|
+
return next > value.length ? void 0 : { next, tag };
|
|
14774
|
+
}
|
|
14775
|
+
function isScannableCarrier(vr) {
|
|
14776
|
+
return SCANNABLE_CARRIER_VRS.has(vr);
|
|
14777
|
+
}
|
|
14778
|
+
function hasByteOutsideRepertoire(region, carrierVr) {
|
|
14779
|
+
const allFive = CONTROL_TOLERANT_VRS.has(carrierVr);
|
|
14780
|
+
const escOnly = ESC_ONLY_VRS.has(carrierVr);
|
|
14781
|
+
for (const byte of region) {
|
|
14782
|
+
if (byte >= 32 && byte !== 127) continue;
|
|
14783
|
+
if (allFive && ALLOWED_CONTROL_BYTES.has(byte)) continue;
|
|
14784
|
+
if (escOnly && byte === 27) continue;
|
|
14785
|
+
return true;
|
|
14786
|
+
}
|
|
14787
|
+
return false;
|
|
14788
|
+
}
|
|
14789
|
+
function findEmbeddedAttributes(value, carrierVr, encoding, isActionable) {
|
|
14790
|
+
if (!isScannableCarrier(carrierVr)) return void 0;
|
|
14791
|
+
if (value.length < MIN_HEADER) return void 0;
|
|
14792
|
+
const windowStart = value.length > MAX_SCAN_BYTES ? value.length - MAX_SCAN_BYTES : 0;
|
|
14793
|
+
const window = value.subarray(windowStart);
|
|
14794
|
+
const n = window.length;
|
|
14795
|
+
const half = (n >> 1) + 1;
|
|
14796
|
+
const tiles = new Uint8Array(half);
|
|
14797
|
+
const hit = new Uint8Array(half);
|
|
14798
|
+
for (let off = n - MIN_HEADER & -2; off >= 0; off -= 2) {
|
|
14799
|
+
const decoded = decodeAt(window, off, encoding);
|
|
14800
|
+
if (decoded === void 0) continue;
|
|
14801
|
+
const atEnd = decoded.next === n;
|
|
14802
|
+
const onward = decoded.next < n ? tiles[decoded.next >> 1] === 1 : false;
|
|
14803
|
+
if (!atEnd && !onward) continue;
|
|
14804
|
+
tiles[off >> 1] = 1;
|
|
14805
|
+
const actionable = decoded.tag !== void 0 && isActionable(decoded.tag);
|
|
14806
|
+
const onwardHit = decoded.next < n ? hit[decoded.next >> 1] === 1 : false;
|
|
14807
|
+
if (actionable || onwardHit) hit[off >> 1] = 1;
|
|
14808
|
+
}
|
|
14809
|
+
for (let off = 0; off + MIN_HEADER <= n; off += 2) {
|
|
14810
|
+
if (hit[off >> 1] !== 1) continue;
|
|
14811
|
+
if (!hasByteOutsideRepertoire(window.subarray(off), carrierVr)) return void 0;
|
|
14812
|
+
const tags = [];
|
|
14813
|
+
let pos = off;
|
|
14814
|
+
while (pos < n) {
|
|
14815
|
+
const decoded = decodeAt(window, pos, encoding);
|
|
14816
|
+
if (decoded === void 0) break;
|
|
14817
|
+
if (decoded.tag !== void 0) tags.push(decoded.tag);
|
|
14818
|
+
pos = decoded.next;
|
|
14819
|
+
}
|
|
14820
|
+
return tags;
|
|
14821
|
+
}
|
|
14822
|
+
return void 0;
|
|
14823
|
+
}
|
|
14824
|
+
|
|
14657
14825
|
// src/deident/types.ts
|
|
14658
14826
|
var DEIDENTIFY_OPTIONS = Object.freeze([
|
|
14659
14827
|
"CleanGraphics",
|
|
@@ -14725,6 +14893,9 @@ var BODY_ENCODING2 = {
|
|
|
14725
14893
|
"1.2.840.10008.1.2.2": "explicitBE",
|
|
14726
14894
|
"1.2.840.10008.1.2.1.99": "explicitLE"
|
|
14727
14895
|
};
|
|
14896
|
+
var MAX_UNAUDITABLE_SEQUENCE_FINDINGS = 64;
|
|
14897
|
+
var MAX_UNDEFINED_VR_FINDINGS = 64;
|
|
14898
|
+
var KNOWN_VRS3 = new Set(Object.keys(BE_VR_STRIDE));
|
|
14728
14899
|
function validateRetain(retain) {
|
|
14729
14900
|
const valid = new Set(DEIDENTIFY_OPTIONS);
|
|
14730
14901
|
const active = /* @__PURE__ */ new Set();
|
|
@@ -14838,6 +15009,64 @@ function isPrivateCreatorElement(tag) {
|
|
|
14838
15009
|
const { element } = splitTag(tag);
|
|
14839
15010
|
return element >= 16 && element <= 255;
|
|
14840
15011
|
}
|
|
15012
|
+
function actsOnTag(tag, ctx) {
|
|
15013
|
+
if (isPrivateTag(tag)) return true;
|
|
15014
|
+
const action = annexE(tag);
|
|
15015
|
+
if (action === void 0) return false;
|
|
15016
|
+
return resolveAction(effectiveCode(action, ctx.active)) !== "K";
|
|
15017
|
+
}
|
|
15018
|
+
function hasUndefinedVr(el) {
|
|
15019
|
+
return !KNOWN_VRS3.has(el.vr);
|
|
15020
|
+
}
|
|
15021
|
+
function keepOrEmpty(el, ctx, contextPath, out) {
|
|
15022
|
+
if (hasUndefinedVr(el)) {
|
|
15023
|
+
emptyUndefinedVrElement(el, ctx, contextPath, out);
|
|
15024
|
+
return false;
|
|
15025
|
+
}
|
|
15026
|
+
const hidden = findEmbeddedAttributes(el.rawBytes, el.vr, ctx.encoding, (t) => actsOnTag(t, ctx));
|
|
15027
|
+
if (hidden === void 0) {
|
|
15028
|
+
out.elements.set(el.tag, el);
|
|
15029
|
+
return true;
|
|
15030
|
+
}
|
|
15031
|
+
out.elements.set(el.tag, freshScalar(el, Buffer.alloc(0), 0));
|
|
15032
|
+
out.embeddedAttributes.push({
|
|
15033
|
+
tag: el.tag,
|
|
15034
|
+
vr: el.vr,
|
|
15035
|
+
hidden,
|
|
15036
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15037
|
+
});
|
|
15038
|
+
out.warnings.push(
|
|
15039
|
+
embeddedAttributeRemoved({ byteOffset: el.byteOffset }, el.tag, el.vr, hidden.length)
|
|
15040
|
+
);
|
|
15041
|
+
return false;
|
|
15042
|
+
}
|
|
15043
|
+
function emptyUndefinedVrElement(el, ctx, contextPath, out) {
|
|
15044
|
+
out.elements.set(el.tag, freshScalar(el, Buffer.alloc(0), 0));
|
|
15045
|
+
if (ctx.budget.undefinedVrElements >= MAX_UNDEFINED_VR_FINDINGS) return;
|
|
15046
|
+
ctx.budget.undefinedVrElements += 1;
|
|
15047
|
+
out.undefinedVrElements.push({
|
|
15048
|
+
byteOffset: el.byteOffset,
|
|
15049
|
+
byteLength: el.rawBytes.length,
|
|
15050
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15051
|
+
});
|
|
15052
|
+
out.warnings.push(undefinedVrNotAuditable({ byteOffset: el.byteOffset }, el.rawBytes.length));
|
|
15053
|
+
}
|
|
15054
|
+
function isUnauditableSequence(el) {
|
|
15055
|
+
return el.vr === "SQ" && el.items === void 0;
|
|
15056
|
+
}
|
|
15057
|
+
function emptyUnauditableSequence(el, ctx, contextPath, out) {
|
|
15058
|
+
out.elements.set(el.tag, rebuildSequence(el, [], ctx.encoding));
|
|
15059
|
+
if (ctx.budget.unauditableSequences >= MAX_UNAUDITABLE_SEQUENCE_FINDINGS) return;
|
|
15060
|
+
ctx.budget.unauditableSequences += 1;
|
|
15061
|
+
out.unauditableSequences.push({
|
|
15062
|
+
tag: el.tag,
|
|
15063
|
+
byteLength: el.rawBytes.length,
|
|
15064
|
+
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15065
|
+
});
|
|
15066
|
+
out.warnings.push(
|
|
15067
|
+
sequenceNotAuditable({ byteOffset: el.byteOffset }, el.tag, el.rawBytes.length)
|
|
15068
|
+
);
|
|
15069
|
+
}
|
|
14841
15070
|
function creatorsInScope(source) {
|
|
14842
15071
|
const byBlock = /* @__PURE__ */ new Map();
|
|
14843
15072
|
for (const el of source) {
|
|
@@ -14864,21 +15093,26 @@ function processElements(source, ctx, contextPath) {
|
|
|
14864
15093
|
const out = {
|
|
14865
15094
|
elements: /* @__PURE__ */ new Map(),
|
|
14866
15095
|
attributes: [],
|
|
14867
|
-
removedPrivateTags: []
|
|
15096
|
+
removedPrivateTags: [],
|
|
15097
|
+
embeddedAttributes: [],
|
|
15098
|
+
unauditableSequences: [],
|
|
15099
|
+
undefinedVrElements: [],
|
|
15100
|
+
warnings: []
|
|
14868
15101
|
};
|
|
14869
15102
|
const creators = creatorsInScope(source);
|
|
14870
15103
|
for (const el of source) {
|
|
14871
15104
|
if (isPrivateTag(el.tag)) {
|
|
14872
|
-
if (keepsPrivate(el, ctx, creators))
|
|
15105
|
+
if (keepsPrivate(el, ctx, creators)) keepOrEmpty(el, ctx, contextPath, out);
|
|
14873
15106
|
else out.removedPrivateTags.push(el.tag);
|
|
14874
15107
|
continue;
|
|
14875
15108
|
}
|
|
14876
15109
|
const action = annexE(el.tag);
|
|
14877
15110
|
if (action === void 0) {
|
|
14878
|
-
if (el.vr === "SQ"
|
|
14879
|
-
|
|
15111
|
+
if (el.vr === "SQ") {
|
|
15112
|
+
if (isUnauditableSequence(el)) emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15113
|
+
else out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
14880
15114
|
} else {
|
|
14881
|
-
|
|
15115
|
+
keepOrEmpty(el, ctx, contextPath, out);
|
|
14882
15116
|
}
|
|
14883
15117
|
continue;
|
|
14884
15118
|
}
|
|
@@ -14890,8 +15124,7 @@ function processElements(source, ctx, contextPath) {
|
|
|
14890
15124
|
let applied;
|
|
14891
15125
|
switch (resolved) {
|
|
14892
15126
|
case "K":
|
|
14893
|
-
|
|
14894
|
-
applied = "kept";
|
|
15127
|
+
applied = keepOrEmpty(el, ctx, contextPath, out) ? "kept" : "emptied";
|
|
14895
15128
|
break;
|
|
14896
15129
|
case "X":
|
|
14897
15130
|
applied = "removed";
|
|
@@ -14933,6 +15166,10 @@ function descendSequence(el, ctx, contextPath, out) {
|
|
|
14933
15166
|
const inner = processElements(item.elements(), ctx, childPath);
|
|
14934
15167
|
out.attributes.push(...inner.attributes);
|
|
14935
15168
|
out.removedPrivateTags.push(...inner.removedPrivateTags);
|
|
15169
|
+
out.embeddedAttributes.push(...inner.embeddedAttributes);
|
|
15170
|
+
out.unauditableSequences.push(...inner.unauditableSequences);
|
|
15171
|
+
out.undefinedVrElements.push(...inner.undefinedVrElements);
|
|
15172
|
+
out.warnings.push(...inner.warnings);
|
|
14936
15173
|
newItems.push(new Item({ index, warnings: [], elements: inner.elements }));
|
|
14937
15174
|
});
|
|
14938
15175
|
return rebuildSequence(el, newItems, ctx.encoding);
|
|
@@ -14949,13 +15186,23 @@ function applySequenceAction(el, resolved, action, ctx, contextPath, out) {
|
|
|
14949
15186
|
applied = "emptied";
|
|
14950
15187
|
break;
|
|
14951
15188
|
case "C":
|
|
14952
|
-
|
|
14953
|
-
|
|
15189
|
+
if (isUnauditableSequence(el)) {
|
|
15190
|
+
emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15191
|
+
applied = "emptied";
|
|
15192
|
+
} else {
|
|
15193
|
+
out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
15194
|
+
applied = "cleaned";
|
|
15195
|
+
}
|
|
14954
15196
|
break;
|
|
14955
15197
|
case "U":
|
|
14956
15198
|
case "K":
|
|
14957
|
-
|
|
14958
|
-
|
|
15199
|
+
if (isUnauditableSequence(el)) {
|
|
15200
|
+
emptyUnauditableSequence(el, ctx, contextPath, out);
|
|
15201
|
+
applied = "emptied";
|
|
15202
|
+
} else {
|
|
15203
|
+
out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
|
|
15204
|
+
applied = "kept";
|
|
15205
|
+
}
|
|
14959
15206
|
break;
|
|
14960
15207
|
}
|
|
14961
15208
|
out.attributes.push(auditAttribute(el.tag, action, resolved, applied, contextPath));
|
|
@@ -15001,8 +15248,18 @@ function deidentify(ds, options = {}) {
|
|
|
15001
15248
|
active,
|
|
15002
15249
|
remap,
|
|
15003
15250
|
profile: options.profile,
|
|
15004
|
-
encoding
|
|
15005
|
-
|
|
15251
|
+
encoding,
|
|
15252
|
+
budget: { unauditableSequences: 0, undefinedVrElements: 0 }
|
|
15253
|
+
};
|
|
15254
|
+
const processed = processElements(ds.elements(), ctx, []);
|
|
15255
|
+
const {
|
|
15256
|
+
elements,
|
|
15257
|
+
attributes,
|
|
15258
|
+
removedPrivateTags,
|
|
15259
|
+
embeddedAttributes,
|
|
15260
|
+
unauditableSequences,
|
|
15261
|
+
undefinedVrElements
|
|
15262
|
+
} = processed;
|
|
15006
15263
|
elements.set(
|
|
15007
15264
|
TAG_PATIENT_IDENTITY_REMOVED,
|
|
15008
15265
|
insertedScalar(TAG_PATIENT_IDENTITY_REMOVED, "CS", Buffer.from("YES", "latin1"), littleEndian)
|
|
@@ -15012,7 +15269,7 @@ function deidentify(ds, options = {}) {
|
|
|
15012
15269
|
TAG_DEIDENTIFICATION_METHOD,
|
|
15013
15270
|
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO", Buffer.from(method, "latin1"), littleEndian)
|
|
15014
15271
|
);
|
|
15015
|
-
const warnings = [];
|
|
15272
|
+
const warnings = [...processed.warnings];
|
|
15016
15273
|
if (hasUncleanedBurnedIn(ds)) {
|
|
15017
15274
|
const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
|
|
15018
15275
|
warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
|
|
@@ -15027,6 +15284,9 @@ function deidentify(ds, options = {}) {
|
|
|
15027
15284
|
const report = {
|
|
15028
15285
|
attributes,
|
|
15029
15286
|
removedPrivateTags,
|
|
15287
|
+
embeddedAttributes,
|
|
15288
|
+
unauditableSequences,
|
|
15289
|
+
undefinedVrElements,
|
|
15030
15290
|
uidMap: remap.cache,
|
|
15031
15291
|
warnings,
|
|
15032
15292
|
retained: [...active]
|