@cosyte/dicom 0.0.10 โ 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +578 -7
- package/README.md +9 -3
- package/dist/index.cjs +212 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -5
- package/dist/index.d.ts +107 -5
- package/dist/index.mjs +212 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.12";
|
|
13
13
|
|
|
14
14
|
// src/dictionary/index.ts
|
|
15
15
|
var dictionary_exports = {};
|
|
@@ -11711,11 +11711,14 @@ function isRecognizedVr(vr) {
|
|
|
11711
11711
|
// src/parser/warnings.ts
|
|
11712
11712
|
var WARNING_CODES = {
|
|
11713
11713
|
// === Phase 2 actively emits (D-08 active list - alphabetical-within-prefix per CONTEXT specifics ยง) ===
|
|
11714
|
+
DICOM_DUPLICATE_FILE_META_ELEMENT: "DICOM_DUPLICATE_FILE_META_ELEMENT",
|
|
11715
|
+
DICOM_DUPLICATE_TAG_IN_DATA_SET: "DICOM_DUPLICATE_TAG_IN_DATA_SET",
|
|
11714
11716
|
DICOM_EMPTY_ITEM_IN_SEQUENCE: "DICOM_EMPTY_ITEM_IN_SEQUENCE",
|
|
11715
11717
|
DICOM_FILE_META_GROUP_LENGTH_MISMATCH: "DICOM_FILE_META_GROUP_LENGTH_MISMATCH",
|
|
11716
11718
|
DICOM_FILE_META_GROUP_LENGTH_MISSING: "DICOM_FILE_META_GROUP_LENGTH_MISSING",
|
|
11717
11719
|
DICOM_GROUP_LENGTH_IN_DATASET: "DICOM_GROUP_LENGTH_IN_DATASET",
|
|
11718
11720
|
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR",
|
|
11721
|
+
DICOM_ITEM_CROSSES_SEQUENCE_END: "DICOM_ITEM_CROSSES_SEQUENCE_END",
|
|
11719
11722
|
DICOM_MISSING_PREAMBLE: "DICOM_MISSING_PREAMBLE",
|
|
11720
11723
|
DICOM_NONZERO_RESERVED_BYTES: "DICOM_NONZERO_RESERVED_BYTES",
|
|
11721
11724
|
DICOM_ODD_LENGTH_VALUE_PADDED: "DICOM_ODD_LENGTH_VALUE_PADDED",
|
|
@@ -11741,6 +11744,12 @@ var WARNING_CODES = {
|
|
|
11741
11744
|
// reserved by Phase 7 - not emitted in Phase 2
|
|
11742
11745
|
DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED: "DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED",
|
|
11743
11746
|
// emitted by deidentify(), never by the parser
|
|
11747
|
+
DICOM_DEIDENT_METHOD_NOT_ADDED: "DICOM_DEIDENT_METHOD_NOT_ADDED",
|
|
11748
|
+
// emitted by deidentify(), never by the parser
|
|
11749
|
+
DICOM_DEIDENT_METHOD_NOT_LO: "DICOM_DEIDENT_METHOD_NOT_LO",
|
|
11750
|
+
// emitted by deidentify(), never by the parser
|
|
11751
|
+
DICOM_DEIDENT_METHOD_PRIOR_RETAINED: "DICOM_DEIDENT_METHOD_PRIOR_RETAINED",
|
|
11752
|
+
// emitted by deidentify(), never by the parser
|
|
11744
11753
|
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE",
|
|
11745
11754
|
// emitted by deidentify(), never by the parser
|
|
11746
11755
|
DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE",
|
|
@@ -11765,6 +11774,41 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
11765
11774
|
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.",
|
|
11766
11775
|
DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
|
|
11767
11776
|
DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
|
|
11777
|
+
// Neither the tag nor the replaced element's value is here, and the tag's
|
|
11778
|
+
// absence is specific to this code rather than caution. See
|
|
11779
|
+
// `duplicateTagInDataSet`. Short by measurement rather than by intention: one
|
|
11780
|
+
// is raised per collision and the collision count is chosen by the input, so a
|
|
11781
|
+
// 400-character draft was measured at 50 M characters over a 1 MiB file and
|
|
11782
|
+
// cut. The reasoning lives in the factory's JSDoc, not in the string.
|
|
11783
|
+
DICOM_DUPLICATE_TAG_IN_DATA_SET: "A Data Element carrying a tag this Data Set already holds replaced the earlier one at this byte offset; that element's value is not in the parsed object. PS3.5 7.1 and 7.5.1. Tag withheld.",
|
|
11784
|
+
// The tag is deliberately absent for the same reason as the code above, and
|
|
11785
|
+
// the surviving copy is named because the File Meta group resolves a repeat
|
|
11786
|
+
// the OPPOSITE way round to a Data Set: first copy wins here, last read wins
|
|
11787
|
+
// there. Short for the same measured reason - one is raised per repeat and the
|
|
11788
|
+
// repeat count is chosen by the input.
|
|
11789
|
+
// ๐ NO SPEC CITATION HERE, AND ITS ABSENCE IS DELIBERATE. A draft ended this
|
|
11790
|
+
// string "PS3.5 7.1.", which asserts that section's "at most once in a Data
|
|
11791
|
+
// Set" governs the File Meta group - the applicability this slice states it
|
|
11792
|
+
// cannot establish, because PS3.10 governs that group and is not vendored
|
|
11793
|
+
// here. Under `{ strict: true }` this string is also the thrown message, so an
|
|
11794
|
+
// unqualified citation there is the claim, not a footnote. Do not put it back.
|
|
11795
|
+
DICOM_DUPLICATE_FILE_META_ELEMENT: "A File Meta Data Element repeats a (0002,xxxx) tag the group already carries; the copy at this byte offset is not in the parsed File Meta, and the FIRST copy of that tag is the one projected. Tag withheld.",
|
|
11796
|
+
// No value and no length: the prior value is the file's own text. The tag is a
|
|
11797
|
+
// constant of this code rather than composed from input, as in the two
|
|
11798
|
+
// (0002,0000) messages above.
|
|
11799
|
+
DICOM_DEIDENT_METHOD_NOT_ADDED: "The De-identification Method this run recorded could not be added beside the value (0012,0063) already carried without exceeding the largest Value Length that VR can encode, so the earlier value was replaced (PS3.15 E.1.1). The replaced text is not in the output.",
|
|
11800
|
+
// No value, no length AND NO VR. The VR is two bytes read from the file, and a
|
|
11801
|
+
// fabricated header makes those two bytes document content - the shape that
|
|
11802
|
+
// put "ITHS" into DICOM_NONZERO_RESERVED_BYTES. Naming the VR here would read
|
|
11803
|
+
// as harmless and is exactly the same defect.
|
|
11804
|
+
DICOM_DEIDENT_METHOD_NOT_LO: "The (0012,0063) value the source file carried is encoded under a Value Representation other than LO, which is not a De-identification Method this run can add its own text to (PS3.15 E.1.1), so the earlier value was replaced. The replaced text is not in the output. The text and the VR are both withheld from this message.",
|
|
11805
|
+
// No value and no length, for the same reason as the code above: the retained
|
|
11806
|
+
// text is the file's own. The tag is a constant of this code.
|
|
11807
|
+
DICOM_DEIDENT_METHOD_PRIOR_RETAINED: "A De-identification Method (0012,0063) value the source file already carried was kept beside the one this run recorded, as PS3.15 E.1.1 requires. That attribute is not in Table E.1-1, so no rule in this run inspected, audited or redacted those bytes: if the sender wrote identifying text there it is in the de-identified output, under (0012,0062) = YES. The text is withheld from this message.",
|
|
11808
|
+
// The Item's own declared length is deliberately absent. See
|
|
11809
|
+
// `itemCrossesSequenceEnd`; `{n2}` stays because the emit site's
|
|
11810
|
+
// `endLimit < buffer.length` conjunct bounds it by the buffer.
|
|
11811
|
+
DICOM_ITEM_CROSSES_SEQUENCE_END: "Item ({tag}) declares a length reaching past its enclosing sequence's declared end, so it reads the enclosing Data Set's bytes; {n2} bytes remained inside the sequence. The file's two length fields disagree (PS3.5 7.5.1 and 7.5.2 govern them); the item's is used. The declared length is withheld; the byte offset locates the item.",
|
|
11768
11812
|
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length {n} does not match computed {n2} bytes.",
|
|
11769
11813
|
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "Private element ({tag}) under Implicit VR LE has no VR override; falling back to UN.",
|
|
11770
11814
|
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.",
|
|
@@ -11849,9 +11893,21 @@ function unParsedAsSQ(position, tag) {
|
|
|
11849
11893
|
function sqNotDescended(position, tag) {
|
|
11850
11894
|
return build(WARNING_CODES.DICOM_SQ_NOT_DESCENDED, position, { tag });
|
|
11851
11895
|
}
|
|
11896
|
+
function itemCrossesSequenceEnd(position, tag, availableLength) {
|
|
11897
|
+
return build(WARNING_CODES.DICOM_ITEM_CROSSES_SEQUENCE_END, position, {
|
|
11898
|
+
tag,
|
|
11899
|
+
n2: availableLength
|
|
11900
|
+
});
|
|
11901
|
+
}
|
|
11852
11902
|
function emptyItemInSequence(position, tag) {
|
|
11853
11903
|
return build(WARNING_CODES.DICOM_EMPTY_ITEM_IN_SEQUENCE, position, { tag });
|
|
11854
11904
|
}
|
|
11905
|
+
function duplicateTagInDataSet(position) {
|
|
11906
|
+
return build(WARNING_CODES.DICOM_DUPLICATE_TAG_IN_DATA_SET, position);
|
|
11907
|
+
}
|
|
11908
|
+
function duplicateFileMetaElement(position) {
|
|
11909
|
+
return build(WARNING_CODES.DICOM_DUPLICATE_FILE_META_ELEMENT, position);
|
|
11910
|
+
}
|
|
11855
11911
|
function implicitVRForPrivateTagWithoutVR(position, tag) {
|
|
11856
11912
|
return build(WARNING_CODES.DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR, position, { tag });
|
|
11857
11913
|
}
|
|
@@ -11861,6 +11917,15 @@ function privateCreatorUnknown(position, tag) {
|
|
|
11861
11917
|
function burnedInAnnotationNotRemoved(position) {
|
|
11862
11918
|
return build(WARNING_CODES.DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED, position);
|
|
11863
11919
|
}
|
|
11920
|
+
function deidentMethodNotAdded(position) {
|
|
11921
|
+
return build(WARNING_CODES.DICOM_DEIDENT_METHOD_NOT_ADDED, position);
|
|
11922
|
+
}
|
|
11923
|
+
function deidentMethodNotLo(position) {
|
|
11924
|
+
return build(WARNING_CODES.DICOM_DEIDENT_METHOD_NOT_LO, position);
|
|
11925
|
+
}
|
|
11926
|
+
function deidentMethodPriorRetained(position) {
|
|
11927
|
+
return build(WARNING_CODES.DICOM_DEIDENT_METHOD_PRIOR_RETAINED, position);
|
|
11928
|
+
}
|
|
11864
11929
|
function embeddedAttributeRemoved(position, tag, vr, count) {
|
|
11865
11930
|
return build(WARNING_CODES.DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED, position, {
|
|
11866
11931
|
tag,
|
|
@@ -12155,6 +12220,16 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
|
|
|
12155
12220
|
fmElements.push(next);
|
|
12156
12221
|
}
|
|
12157
12222
|
}
|
|
12223
|
+
const seenModeledTags = /* @__PURE__ */ new Set();
|
|
12224
|
+
if (declaredFmLength !== void 0) seenModeledTags.add("00020000");
|
|
12225
|
+
for (const e of fmElements) {
|
|
12226
|
+
if (!MODELED_FM_TAGS.has(e.tag)) continue;
|
|
12227
|
+
if (seenModeledTags.has(e.tag)) {
|
|
12228
|
+
emit(duplicateFileMetaElement({ byteOffset: e.byteOffset, fileMeta: true }));
|
|
12229
|
+
continue;
|
|
12230
|
+
}
|
|
12231
|
+
seenModeledTags.add(e.tag);
|
|
12232
|
+
}
|
|
12158
12233
|
const tsElement = fmElements.find((e) => e.tag === "00020010");
|
|
12159
12234
|
if (tsElement === void 0 || tsElement.vr !== "UI") {
|
|
12160
12235
|
throw new DicomParseError(
|
|
@@ -12201,7 +12276,7 @@ function readExplicitLeElement(cursor) {
|
|
|
12201
12276
|
if (bytesConsumed !== headerLength + length) {
|
|
12202
12277
|
throw new RangeError("File Meta element accounting mismatch");
|
|
12203
12278
|
}
|
|
12204
|
-
return { tag, vr, value, bytesConsumed };
|
|
12279
|
+
return { tag, vr, value, bytesConsumed, byteOffset: headerStart };
|
|
12205
12280
|
}
|
|
12206
12281
|
function trimUI(buf) {
|
|
12207
12282
|
let end = buf.length;
|
|
@@ -12641,6 +12716,14 @@ var Element = class {
|
|
|
12641
12716
|
}
|
|
12642
12717
|
};
|
|
12643
12718
|
|
|
12719
|
+
// src/parser/data-set-map.ts
|
|
12720
|
+
function defineElement(elements, element, emit) {
|
|
12721
|
+
if (elements.has(element.tag)) {
|
|
12722
|
+
emit(duplicateTagInDataSet({ byteOffset: element.byteOffset }));
|
|
12723
|
+
}
|
|
12724
|
+
elements.set(element.tag, element);
|
|
12725
|
+
}
|
|
12726
|
+
|
|
12644
12727
|
// src/dataset/item.ts
|
|
12645
12728
|
var Item = class extends Dataset {
|
|
12646
12729
|
index;
|
|
@@ -12762,6 +12845,15 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
12762
12845
|
items.push(new Item({ warnings: [], elements: inner.elements, index: itemIndex }));
|
|
12763
12846
|
cursor.position = inner.endOffset;
|
|
12764
12847
|
} else {
|
|
12848
|
+
if (opts.explicitLength !== void 0 && endLimit < buffer.length && cursor.position + itemLength > endLimit) {
|
|
12849
|
+
emit(
|
|
12850
|
+
itemCrossesSequenceEnd(
|
|
12851
|
+
{ byteOffset: itemHeaderStart },
|
|
12852
|
+
itemTag,
|
|
12853
|
+
Math.max(0, endLimit - cursor.position)
|
|
12854
|
+
)
|
|
12855
|
+
);
|
|
12856
|
+
}
|
|
12765
12857
|
if (cursor.position + itemLength > buffer.length) {
|
|
12766
12858
|
throw new DicomParseError(
|
|
12767
12859
|
FATAL_CODES.INVALID_FILE_META,
|
|
@@ -12897,8 +12989,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12897
12989
|
cursor.position = seq.endOffset;
|
|
12898
12990
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12899
12991
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12900
|
-
|
|
12901
|
-
|
|
12992
|
+
defineElement(
|
|
12993
|
+
elements,
|
|
12902
12994
|
new Element({
|
|
12903
12995
|
tag,
|
|
12904
12996
|
vr,
|
|
@@ -12909,7 +13001,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12909
13001
|
littleEndian: true,
|
|
12910
13002
|
items: seq.items,
|
|
12911
13003
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12912
|
-
})
|
|
13004
|
+
}),
|
|
13005
|
+
emit
|
|
12913
13006
|
);
|
|
12914
13007
|
continue;
|
|
12915
13008
|
}
|
|
@@ -12928,8 +13021,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12928
13021
|
cursor.position = cp246.endOffset;
|
|
12929
13022
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12930
13023
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12931
|
-
|
|
12932
|
-
|
|
13024
|
+
defineElement(
|
|
13025
|
+
elements,
|
|
12933
13026
|
new Element({
|
|
12934
13027
|
tag,
|
|
12935
13028
|
vr: "SQ",
|
|
@@ -12942,7 +13035,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12942
13035
|
littleEndian: true,
|
|
12943
13036
|
items: cp246.items,
|
|
12944
13037
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12945
|
-
})
|
|
13038
|
+
}),
|
|
13039
|
+
emit
|
|
12946
13040
|
);
|
|
12947
13041
|
continue;
|
|
12948
13042
|
}
|
|
@@ -12978,8 +13072,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12978
13072
|
);
|
|
12979
13073
|
if (seq.success) {
|
|
12980
13074
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12981
|
-
|
|
12982
|
-
|
|
13075
|
+
defineElement(
|
|
13076
|
+
elements,
|
|
12983
13077
|
new Element({
|
|
12984
13078
|
tag,
|
|
12985
13079
|
vr,
|
|
@@ -12990,7 +13084,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12990
13084
|
littleEndian: true,
|
|
12991
13085
|
items: seq.items,
|
|
12992
13086
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12993
|
-
})
|
|
13087
|
+
}),
|
|
13088
|
+
emit
|
|
12994
13089
|
);
|
|
12995
13090
|
continue;
|
|
12996
13091
|
}
|
|
@@ -13003,8 +13098,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13003
13098
|
}
|
|
13004
13099
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
13005
13100
|
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13006
|
-
|
|
13007
|
-
|
|
13101
|
+
defineElement(
|
|
13102
|
+
elements,
|
|
13008
13103
|
new Element({
|
|
13009
13104
|
tag,
|
|
13010
13105
|
vr,
|
|
@@ -13016,7 +13111,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13016
13111
|
littleEndian: true,
|
|
13017
13112
|
...ctx.currentCharset !== void 0 ? { specificCharacterSet: ctx.currentCharset } : {},
|
|
13018
13113
|
...privateCreator !== void 0 ? { privateCreator } : {}
|
|
13019
|
-
})
|
|
13114
|
+
}),
|
|
13115
|
+
emit
|
|
13020
13116
|
);
|
|
13021
13117
|
}
|
|
13022
13118
|
return { elements, endOffset: cursor.position };
|
|
@@ -13123,8 +13219,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13123
13219
|
cursor.position = seqResult.endOffset;
|
|
13124
13220
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13125
13221
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13126
|
-
|
|
13127
|
-
|
|
13222
|
+
defineElement(
|
|
13223
|
+
elements,
|
|
13128
13224
|
new Element({
|
|
13129
13225
|
tag,
|
|
13130
13226
|
vr: "SQ",
|
|
@@ -13135,7 +13231,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13135
13231
|
littleEndian: mode.littleEndian,
|
|
13136
13232
|
items: seqResult.items,
|
|
13137
13233
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
13138
|
-
})
|
|
13234
|
+
}),
|
|
13235
|
+
emit
|
|
13139
13236
|
);
|
|
13140
13237
|
continue;
|
|
13141
13238
|
}
|
|
@@ -13150,8 +13247,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13150
13247
|
const result = parseSequence(buffer, valueStart2, ctx, emit, seqOpts);
|
|
13151
13248
|
cursor.position = result.endOffset;
|
|
13152
13249
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13153
|
-
|
|
13154
|
-
|
|
13250
|
+
defineElement(
|
|
13251
|
+
elements,
|
|
13155
13252
|
new Element({
|
|
13156
13253
|
tag,
|
|
13157
13254
|
vr: "OB",
|
|
@@ -13162,7 +13259,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13162
13259
|
byteOffset: headerStart,
|
|
13163
13260
|
littleEndian: mode.littleEndian,
|
|
13164
13261
|
items: result.items
|
|
13165
|
-
})
|
|
13262
|
+
}),
|
|
13263
|
+
emit
|
|
13166
13264
|
);
|
|
13167
13265
|
continue;
|
|
13168
13266
|
}
|
|
@@ -13181,8 +13279,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13181
13279
|
cursor.position = cp246.endOffset;
|
|
13182
13280
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13183
13281
|
const privateCreator3 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13184
|
-
|
|
13185
|
-
|
|
13282
|
+
defineElement(
|
|
13283
|
+
elements,
|
|
13186
13284
|
new Element({
|
|
13187
13285
|
tag,
|
|
13188
13286
|
vr: "SQ",
|
|
@@ -13195,15 +13293,16 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13195
13293
|
littleEndian: mode.littleEndian,
|
|
13196
13294
|
items: cp246.items,
|
|
13197
13295
|
...privateCreator3 !== void 0 ? { privateCreator: privateCreator3 } : {}
|
|
13198
|
-
})
|
|
13296
|
+
}),
|
|
13297
|
+
emit
|
|
13199
13298
|
);
|
|
13200
13299
|
continue;
|
|
13201
13300
|
}
|
|
13202
13301
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13203
13302
|
const fallbackEnd = buffer.length;
|
|
13204
13303
|
const fallbackBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, fallbackEnd)) : buffer.subarray(headerStart, fallbackEnd);
|
|
13205
|
-
|
|
13206
|
-
|
|
13304
|
+
defineElement(
|
|
13305
|
+
elements,
|
|
13207
13306
|
new Element({
|
|
13208
13307
|
tag,
|
|
13209
13308
|
vr: "UN",
|
|
@@ -13213,7 +13312,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13213
13312
|
byteOffset: headerStart,
|
|
13214
13313
|
littleEndian: mode.littleEndian,
|
|
13215
13314
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
13216
|
-
})
|
|
13315
|
+
}),
|
|
13316
|
+
emit
|
|
13217
13317
|
);
|
|
13218
13318
|
cursor.position = fallbackEnd;
|
|
13219
13319
|
continue;
|
|
@@ -13248,8 +13348,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13248
13348
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
13249
13349
|
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13250
13350
|
const finalVr = vr;
|
|
13251
|
-
|
|
13252
|
-
|
|
13351
|
+
defineElement(
|
|
13352
|
+
elements,
|
|
13253
13353
|
new Element({
|
|
13254
13354
|
tag,
|
|
13255
13355
|
vr: finalVr,
|
|
@@ -13261,7 +13361,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13261
13361
|
littleEndian: mode.littleEndian,
|
|
13262
13362
|
...ctx.currentCharset !== void 0 ? { specificCharacterSet: ctx.currentCharset } : {},
|
|
13263
13363
|
...privateCreator !== void 0 ? { privateCreator } : {}
|
|
13264
|
-
})
|
|
13364
|
+
}),
|
|
13365
|
+
emit
|
|
13265
13366
|
);
|
|
13266
13367
|
}
|
|
13267
13368
|
return { elements, endOffset: cursor.position };
|
|
@@ -15093,6 +15194,19 @@ function itemStreamOverrunsSequence(el, encoding) {
|
|
|
15093
15194
|
const headerBytes = encoding === "implicit" ? 0 : EXPLICIT_SQ_HEADER_BYTES;
|
|
15094
15195
|
return el.rawBytes.length - headerBytes > el.length;
|
|
15095
15196
|
}
|
|
15197
|
+
function settledBound(source, encoding) {
|
|
15198
|
+
let index = source.length;
|
|
15199
|
+
let byteOffset;
|
|
15200
|
+
for (const [at, el] of source.entries()) {
|
|
15201
|
+
if (!itemStreamOverrunsSequence(el, encoding) && !isUnauditableSequence(el)) continue;
|
|
15202
|
+
if (index === source.length) index = at + 1;
|
|
15203
|
+
if (byteOffset === void 0 || el.byteOffset < byteOffset) byteOffset = el.byteOffset;
|
|
15204
|
+
}
|
|
15205
|
+
return { index, byteOffset };
|
|
15206
|
+
}
|
|
15207
|
+
function isSettled(el, at, bound) {
|
|
15208
|
+
return at < bound.index && (bound.byteOffset === void 0 || el.byteOffset <= bound.byteOffset);
|
|
15209
|
+
}
|
|
15096
15210
|
function keepsPrivate(el, ctx, creators) {
|
|
15097
15211
|
if (!ctx.active.has("RetainSafePrivate") || ctx.profile === void 0) return false;
|
|
15098
15212
|
if (isPrivateCreatorElement(el.tag)) {
|
|
@@ -15112,10 +15226,11 @@ function processElements(source, ctx, contextPath, reservationsUsable) {
|
|
|
15112
15226
|
undefinedVrElements: [],
|
|
15113
15227
|
warnings: []
|
|
15114
15228
|
};
|
|
15115
|
-
const
|
|
15116
|
-
|
|
15229
|
+
const bound = settledBound(source, ctx.encoding);
|
|
15230
|
+
const creators = creatorsInScope(source.filter((el, at) => isSettled(el, at, bound)));
|
|
15231
|
+
for (const [at, el] of source.entries()) {
|
|
15117
15232
|
if (isPrivateTag(el.tag)) {
|
|
15118
|
-
if (reservationsUsable && keepsPrivate(el, ctx, creators))
|
|
15233
|
+
if (reservationsUsable && isSettled(el, at, bound) && keepsPrivate(el, ctx, creators))
|
|
15119
15234
|
keepOrEmpty(el, ctx, contextPath, out);
|
|
15120
15235
|
else out.removedPrivateTags.push(el.tag);
|
|
15121
15236
|
continue;
|
|
@@ -15244,9 +15359,54 @@ function rebuildFileMeta(fileMeta, ctx) {
|
|
|
15244
15359
|
mediaStorageSOPInstanceUID: ctx.remap.map(fileMeta.mediaStorageSOPInstanceUID)
|
|
15245
15360
|
};
|
|
15246
15361
|
}
|
|
15362
|
+
var DEFAULT_METHOD_PROFILE = "@cosyte/dicom Basic Application Level Confidentiality Profile";
|
|
15247
15363
|
function defaultMethod(active) {
|
|
15248
|
-
const
|
|
15249
|
-
return
|
|
15364
|
+
const options = DEIDENTIFY_OPTIONS.filter((option) => active.has(option));
|
|
15365
|
+
return [DEFAULT_METHOD_PROFILE, ...options].join(String.fromCharCode(VALUE_DELIMITER));
|
|
15366
|
+
}
|
|
15367
|
+
var VALUE_DELIMITER = 92;
|
|
15368
|
+
var MAX_SHORT_FORM_VALUE_BYTES = 65534;
|
|
15369
|
+
function addDeidentificationMethod(existing, method) {
|
|
15370
|
+
const added = trimTrailingPad(Buffer.from(method, "latin1"));
|
|
15371
|
+
const none = { replacedPrior: false, retainedPrior: false, replacedNonLoPrior: false };
|
|
15372
|
+
if (existing === void 0) return { value: added, ...none };
|
|
15373
|
+
if (existing.vr !== "LO") {
|
|
15374
|
+
const lost = trimTrailingPad(existing.rawBytes).length > 0;
|
|
15375
|
+
return { value: added, ...none, replacedNonLoPrior: lost };
|
|
15376
|
+
}
|
|
15377
|
+
const kept = Buffer.from(trimTrailingPad(existing.rawBytes));
|
|
15378
|
+
if (kept.length === 0) return { value: added, ...none };
|
|
15379
|
+
const keptValues = splitValues(kept).map(trimTrailingPad);
|
|
15380
|
+
const missing = added.length === 0 ? [] : splitValues(added).filter((v) => !keptValues.some((k) => k.equals(trimTrailingPad(v))));
|
|
15381
|
+
const parts = [kept];
|
|
15382
|
+
for (const value2 of missing) {
|
|
15383
|
+
parts.push(Buffer.from([VALUE_DELIMITER]), value2);
|
|
15384
|
+
}
|
|
15385
|
+
const value = trimTrailingPad(Buffer.concat(parts));
|
|
15386
|
+
if (value.length > MAX_SHORT_FORM_VALUE_BYTES) {
|
|
15387
|
+
return { value: added, ...none, replacedPrior: true };
|
|
15388
|
+
}
|
|
15389
|
+
return { value, ...none, retainedPrior: true };
|
|
15390
|
+
}
|
|
15391
|
+
function trimTrailingPad(value) {
|
|
15392
|
+
let end = value.length;
|
|
15393
|
+
while (end > 0) {
|
|
15394
|
+
const last = value[end - 1];
|
|
15395
|
+
if (last === 32 || last === 0) end--;
|
|
15396
|
+
else break;
|
|
15397
|
+
}
|
|
15398
|
+
return value.subarray(0, end);
|
|
15399
|
+
}
|
|
15400
|
+
function splitValues(value) {
|
|
15401
|
+
const out = [];
|
|
15402
|
+
let start = 0;
|
|
15403
|
+
for (let i = 0; i <= value.length; i++) {
|
|
15404
|
+
if (i === value.length || value[i] === VALUE_DELIMITER) {
|
|
15405
|
+
out.push(value.subarray(start, i));
|
|
15406
|
+
start = i + 1;
|
|
15407
|
+
}
|
|
15408
|
+
}
|
|
15409
|
+
return out;
|
|
15250
15410
|
}
|
|
15251
15411
|
function hasUncleanedBurnedIn(ds) {
|
|
15252
15412
|
if (!ds.has(TAG_PIXEL_DATA)) return false;
|
|
@@ -15281,11 +15441,28 @@ function deidentify(ds, options = {}) {
|
|
|
15281
15441
|
insertedScalar(TAG_PATIENT_IDENTITY_REMOVED, "CS", Buffer.from("YES", "latin1"), littleEndian)
|
|
15282
15442
|
);
|
|
15283
15443
|
const method = options.deidentificationMethod ?? defaultMethod(active);
|
|
15444
|
+
const priorMethod = elements.get(TAG_DEIDENTIFICATION_METHOD);
|
|
15445
|
+
const deidentMethod = addDeidentificationMethod(priorMethod, method);
|
|
15284
15446
|
elements.set(
|
|
15285
15447
|
TAG_DEIDENTIFICATION_METHOD,
|
|
15286
|
-
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO",
|
|
15448
|
+
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO", deidentMethod.value, littleEndian)
|
|
15287
15449
|
);
|
|
15288
15450
|
const warnings = [...processed.warnings];
|
|
15451
|
+
if (deidentMethod.replacedPrior) {
|
|
15452
|
+
warnings.push(
|
|
15453
|
+
deidentMethodNotAdded({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
|
|
15454
|
+
);
|
|
15455
|
+
}
|
|
15456
|
+
if (deidentMethod.retainedPrior) {
|
|
15457
|
+
warnings.push(
|
|
15458
|
+
deidentMethodPriorRetained({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
|
|
15459
|
+
);
|
|
15460
|
+
}
|
|
15461
|
+
if (deidentMethod.replacedNonLoPrior) {
|
|
15462
|
+
warnings.push(
|
|
15463
|
+
deidentMethodNotLo({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
|
|
15464
|
+
);
|
|
15465
|
+
}
|
|
15289
15466
|
if (hasUncleanedBurnedIn(ds)) {
|
|
15290
15467
|
const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
|
|
15291
15468
|
warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
|