@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/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.6";
14
+ var VERSION = "0.0.8";
15
15
 
16
16
  // src/dictionary/index.ts
17
17
  var dictionary_exports = {};
@@ -11735,6 +11735,12 @@ var WARNING_CODES = {
11735
11735
  // === Reserved by later phases (declared, not emitted in Phase 2) ===
11736
11736
  DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED: "DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED",
11737
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
11738
11744
  DICOM_PRIVATE_CREATOR_UNKNOWN: "DICOM_PRIVATE_CREATOR_UNKNOWN"
11739
11745
  // reserved by Phase 6 - not emitted in Phase 2
11740
11746
  };
@@ -11748,13 +11754,30 @@ var WARNING_MESSAGES = Object.freeze({
11748
11754
  DICOM_PRIVATE_TAG_NO_CREATOR: "Private element ({tag}) has no Private Creator registered for its block; treating as VR=UN.",
11749
11755
  DICOM_GROUP_LENGTH_IN_DATASET: "Retired Group Length element ({tag}) encountered in dataset; preserved as-is.",
11750
11756
  DICOM_NONZERO_RESERVED_BYTES: "Element ({tag}) has non-zero reserved bytes between VR and length (first byte {n}, second byte {n2}); ignoring.",
11751
- 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 or de-identification.",
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.",
11752
11758
  DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
11753
11759
  DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
11754
11760
  DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length {n} does not match computed {n2} bytes.",
11755
11761
  DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "Private element ({tag}) under Implicit VR LE has no VR override; falling back to UN.",
11756
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.",
11757
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.",
11758
11781
  DICOM_BOM_IN_TEXT_VR: "Element ({tag}) {vr} value begins with a UTF-8 BOM; stripped on decode.",
11759
11782
  DICOM_TRAILING_NULL_IN_TEXT_VR: "Element ({tag}) {vr} value has a trailing NULL pad where SPACE is expected; trimmed.",
11760
11783
  DICOM_UI_TRAILING_SPACE: "Element ({tag}) UI value is SPACE-padded; UI requires NULL padding (PS3.5 \xA76.2). Trimmed.",
@@ -11832,6 +11855,25 @@ function privateCreatorUnknown(position, tag) {
11832
11855
  function burnedInAnnotationNotRemoved(position) {
11833
11856
  return build(WARNING_CODES.DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED, position);
11834
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
+ });
11876
+ }
11835
11877
  function bomInTextVR(position, tag, vr) {
11836
11878
  return build(WARNING_CODES.DICOM_BOM_IN_TEXT_VR, position, { tag, vr });
11837
11879
  }
@@ -14656,6 +14698,132 @@ function uidValueMultiplicity(rawBytes) {
14656
14698
  return raw.split("\\").length;
14657
14699
  }
14658
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
+
14659
14827
  // src/deident/types.ts
14660
14828
  var DEIDENTIFY_OPTIONS = Object.freeze([
14661
14829
  "CleanGraphics",
@@ -14727,6 +14895,9 @@ var BODY_ENCODING2 = {
14727
14895
  "1.2.840.10008.1.2.2": "explicitBE",
14728
14896
  "1.2.840.10008.1.2.1.99": "explicitLE"
14729
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));
14730
14901
  function validateRetain(retain) {
14731
14902
  const valid = new Set(DEIDENTIFY_OPTIONS);
14732
14903
  const active = /* @__PURE__ */ new Set();
@@ -14840,6 +15011,64 @@ function isPrivateCreatorElement(tag) {
14840
15011
  const { element } = splitTag(tag);
14841
15012
  return element >= 16 && element <= 255;
14842
15013
  }
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
+ }
14843
15072
  function creatorsInScope(source) {
14844
15073
  const byBlock = /* @__PURE__ */ new Map();
14845
15074
  for (const el of source) {
@@ -14866,21 +15095,26 @@ function processElements(source, ctx, contextPath) {
14866
15095
  const out = {
14867
15096
  elements: /* @__PURE__ */ new Map(),
14868
15097
  attributes: [],
14869
- removedPrivateTags: []
15098
+ removedPrivateTags: [],
15099
+ embeddedAttributes: [],
15100
+ unauditableSequences: [],
15101
+ undefinedVrElements: [],
15102
+ warnings: []
14870
15103
  };
14871
15104
  const creators = creatorsInScope(source);
14872
15105
  for (const el of source) {
14873
15106
  if (isPrivateTag(el.tag)) {
14874
- if (keepsPrivate(el, ctx, creators)) out.elements.set(el.tag, el);
15107
+ if (keepsPrivate(el, ctx, creators)) keepOrEmpty(el, ctx, contextPath, out);
14875
15108
  else out.removedPrivateTags.push(el.tag);
14876
15109
  continue;
14877
15110
  }
14878
15111
  const action = annexE(el.tag);
14879
15112
  if (action === void 0) {
14880
- if (el.vr === "SQ" && el.items !== void 0) {
14881
- out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
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));
14882
15116
  } else {
14883
- out.elements.set(el.tag, el);
15117
+ keepOrEmpty(el, ctx, contextPath, out);
14884
15118
  }
14885
15119
  continue;
14886
15120
  }
@@ -14892,8 +15126,7 @@ function processElements(source, ctx, contextPath) {
14892
15126
  let applied;
14893
15127
  switch (resolved) {
14894
15128
  case "K":
14895
- out.elements.set(el.tag, el);
14896
- applied = "kept";
15129
+ applied = keepOrEmpty(el, ctx, contextPath, out) ? "kept" : "emptied";
14897
15130
  break;
14898
15131
  case "X":
14899
15132
  applied = "removed";
@@ -14935,6 +15168,10 @@ function descendSequence(el, ctx, contextPath, out) {
14935
15168
  const inner = processElements(item.elements(), ctx, childPath);
14936
15169
  out.attributes.push(...inner.attributes);
14937
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);
14938
15175
  newItems.push(new Item({ index, warnings: [], elements: inner.elements }));
14939
15176
  });
14940
15177
  return rebuildSequence(el, newItems, ctx.encoding);
@@ -14951,13 +15188,23 @@ function applySequenceAction(el, resolved, action, ctx, contextPath, out) {
14951
15188
  applied = "emptied";
14952
15189
  break;
14953
15190
  case "C":
14954
- out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
14955
- applied = "cleaned";
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
+ }
14956
15198
  break;
14957
15199
  case "U":
14958
15200
  case "K":
14959
- out.elements.set(el.tag, descendSequence(el, ctx, contextPath, out));
14960
- applied = "kept";
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
+ }
14961
15208
  break;
14962
15209
  }
14963
15210
  out.attributes.push(auditAttribute(el.tag, action, resolved, applied, contextPath));
@@ -15003,8 +15250,18 @@ function deidentify(ds, options = {}) {
15003
15250
  active,
15004
15251
  remap,
15005
15252
  profile: options.profile,
15006
- encoding};
15007
- const { elements, attributes, removedPrivateTags } = processElements(ds.elements(), ctx, []);
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;
15008
15265
  elements.set(
15009
15266
  TAG_PATIENT_IDENTITY_REMOVED,
15010
15267
  insertedScalar(TAG_PATIENT_IDENTITY_REMOVED, "CS", buffer.Buffer.from("YES", "latin1"), littleEndian)
@@ -15014,7 +15271,7 @@ function deidentify(ds, options = {}) {
15014
15271
  TAG_DEIDENTIFICATION_METHOD,
15015
15272
  insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO", buffer.Buffer.from(method, "latin1"), littleEndian)
15016
15273
  );
15017
- const warnings = [];
15274
+ const warnings = [...processed.warnings];
15018
15275
  if (hasUncleanedBurnedIn(ds)) {
15019
15276
  const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
15020
15277
  warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
@@ -15029,6 +15286,9 @@ function deidentify(ds, options = {}) {
15029
15286
  const report = {
15030
15287
  attributes,
15031
15288
  removedPrivateTags,
15289
+ embeddedAttributes,
15290
+ unauditableSequences,
15291
+ undefinedVrElements,
15032
15292
  uidMap: remap.cache,
15033
15293
  warnings,
15034
15294
  retained: [...active]