@cosyte/dicom 0.0.10 โ 0.0.11
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 +519 -7
- package/README.md +9 -3
- package/dist/index.cjs +191 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -5
- package/dist/index.d.ts +70 -5
- package/dist/index.mjs +191 -33
- 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.11";
|
|
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,10 @@ 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_PRIOR_RETAINED: "DICOM_DEIDENT_METHOD_PRIOR_RETAINED",
|
|
11750
|
+
// emitted by deidentify(), never by the parser
|
|
11744
11751
|
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE",
|
|
11745
11752
|
// emitted by deidentify(), never by the parser
|
|
11746
11753
|
DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE",
|
|
@@ -11765,6 +11772,36 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
11765
11772
|
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
11773
|
DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
|
|
11767
11774
|
DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
|
|
11775
|
+
// Neither the tag nor the replaced element's value is here, and the tag's
|
|
11776
|
+
// absence is specific to this code rather than caution. See
|
|
11777
|
+
// `duplicateTagInDataSet`. Short by measurement rather than by intention: one
|
|
11778
|
+
// is raised per collision and the collision count is chosen by the input, so a
|
|
11779
|
+
// 400-character draft was measured at 50 M characters over a 1 MiB file and
|
|
11780
|
+
// cut. The reasoning lives in the factory's JSDoc, not in the string.
|
|
11781
|
+
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.",
|
|
11782
|
+
// The tag is deliberately absent for the same reason as the code above, and
|
|
11783
|
+
// the surviving copy is named because the File Meta group resolves a repeat
|
|
11784
|
+
// the OPPOSITE way round to a Data Set: first copy wins here, last read wins
|
|
11785
|
+
// there. Short for the same measured reason - one is raised per repeat and the
|
|
11786
|
+
// repeat count is chosen by the input.
|
|
11787
|
+
// ๐ NO SPEC CITATION HERE, AND ITS ABSENCE IS DELIBERATE. A draft ended this
|
|
11788
|
+
// string "PS3.5 7.1.", which asserts that section's "at most once in a Data
|
|
11789
|
+
// Set" governs the File Meta group - the applicability this slice states it
|
|
11790
|
+
// cannot establish, because PS3.10 governs that group and is not vendored
|
|
11791
|
+
// here. Under `{ strict: true }` this string is also the thrown message, so an
|
|
11792
|
+
// unqualified citation there is the claim, not a footnote. Do not put it back.
|
|
11793
|
+
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.",
|
|
11794
|
+
// No value and no length: the prior value is the file's own text. The tag is a
|
|
11795
|
+
// constant of this code rather than composed from input, as in the two
|
|
11796
|
+
// (0002,0000) messages above.
|
|
11797
|
+
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.",
|
|
11798
|
+
// No value and no length, for the same reason as the code above: the retained
|
|
11799
|
+
// text is the file's own. The tag is a constant of this code.
|
|
11800
|
+
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.",
|
|
11801
|
+
// The Item's own declared length is deliberately absent. See
|
|
11802
|
+
// `itemCrossesSequenceEnd`; `{n2}` stays because the emit site's
|
|
11803
|
+
// `endLimit < buffer.length` conjunct bounds it by the buffer.
|
|
11804
|
+
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
11805
|
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length {n} does not match computed {n2} bytes.",
|
|
11769
11806
|
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "Private element ({tag}) under Implicit VR LE has no VR override; falling back to UN.",
|
|
11770
11807
|
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 +11886,21 @@ function unParsedAsSQ(position, tag) {
|
|
|
11849
11886
|
function sqNotDescended(position, tag) {
|
|
11850
11887
|
return build(WARNING_CODES.DICOM_SQ_NOT_DESCENDED, position, { tag });
|
|
11851
11888
|
}
|
|
11889
|
+
function itemCrossesSequenceEnd(position, tag, availableLength) {
|
|
11890
|
+
return build(WARNING_CODES.DICOM_ITEM_CROSSES_SEQUENCE_END, position, {
|
|
11891
|
+
tag,
|
|
11892
|
+
n2: availableLength
|
|
11893
|
+
});
|
|
11894
|
+
}
|
|
11852
11895
|
function emptyItemInSequence(position, tag) {
|
|
11853
11896
|
return build(WARNING_CODES.DICOM_EMPTY_ITEM_IN_SEQUENCE, position, { tag });
|
|
11854
11897
|
}
|
|
11898
|
+
function duplicateTagInDataSet(position) {
|
|
11899
|
+
return build(WARNING_CODES.DICOM_DUPLICATE_TAG_IN_DATA_SET, position);
|
|
11900
|
+
}
|
|
11901
|
+
function duplicateFileMetaElement(position) {
|
|
11902
|
+
return build(WARNING_CODES.DICOM_DUPLICATE_FILE_META_ELEMENT, position);
|
|
11903
|
+
}
|
|
11855
11904
|
function implicitVRForPrivateTagWithoutVR(position, tag) {
|
|
11856
11905
|
return build(WARNING_CODES.DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR, position, { tag });
|
|
11857
11906
|
}
|
|
@@ -11861,6 +11910,12 @@ function privateCreatorUnknown(position, tag) {
|
|
|
11861
11910
|
function burnedInAnnotationNotRemoved(position) {
|
|
11862
11911
|
return build(WARNING_CODES.DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED, position);
|
|
11863
11912
|
}
|
|
11913
|
+
function deidentMethodNotAdded(position) {
|
|
11914
|
+
return build(WARNING_CODES.DICOM_DEIDENT_METHOD_NOT_ADDED, position);
|
|
11915
|
+
}
|
|
11916
|
+
function deidentMethodPriorRetained(position) {
|
|
11917
|
+
return build(WARNING_CODES.DICOM_DEIDENT_METHOD_PRIOR_RETAINED, position);
|
|
11918
|
+
}
|
|
11864
11919
|
function embeddedAttributeRemoved(position, tag, vr, count) {
|
|
11865
11920
|
return build(WARNING_CODES.DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED, position, {
|
|
11866
11921
|
tag,
|
|
@@ -12155,6 +12210,16 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
|
|
|
12155
12210
|
fmElements.push(next);
|
|
12156
12211
|
}
|
|
12157
12212
|
}
|
|
12213
|
+
const seenModeledTags = /* @__PURE__ */ new Set();
|
|
12214
|
+
if (declaredFmLength !== void 0) seenModeledTags.add("00020000");
|
|
12215
|
+
for (const e of fmElements) {
|
|
12216
|
+
if (!MODELED_FM_TAGS.has(e.tag)) continue;
|
|
12217
|
+
if (seenModeledTags.has(e.tag)) {
|
|
12218
|
+
emit(duplicateFileMetaElement({ byteOffset: e.byteOffset, fileMeta: true }));
|
|
12219
|
+
continue;
|
|
12220
|
+
}
|
|
12221
|
+
seenModeledTags.add(e.tag);
|
|
12222
|
+
}
|
|
12158
12223
|
const tsElement = fmElements.find((e) => e.tag === "00020010");
|
|
12159
12224
|
if (tsElement === void 0 || tsElement.vr !== "UI") {
|
|
12160
12225
|
throw new DicomParseError(
|
|
@@ -12201,7 +12266,7 @@ function readExplicitLeElement(cursor) {
|
|
|
12201
12266
|
if (bytesConsumed !== headerLength + length) {
|
|
12202
12267
|
throw new RangeError("File Meta element accounting mismatch");
|
|
12203
12268
|
}
|
|
12204
|
-
return { tag, vr, value, bytesConsumed };
|
|
12269
|
+
return { tag, vr, value, bytesConsumed, byteOffset: headerStart };
|
|
12205
12270
|
}
|
|
12206
12271
|
function trimUI(buf) {
|
|
12207
12272
|
let end = buf.length;
|
|
@@ -12641,6 +12706,14 @@ var Element = class {
|
|
|
12641
12706
|
}
|
|
12642
12707
|
};
|
|
12643
12708
|
|
|
12709
|
+
// src/parser/data-set-map.ts
|
|
12710
|
+
function defineElement(elements, element, emit) {
|
|
12711
|
+
if (elements.has(element.tag)) {
|
|
12712
|
+
emit(duplicateTagInDataSet({ byteOffset: element.byteOffset }));
|
|
12713
|
+
}
|
|
12714
|
+
elements.set(element.tag, element);
|
|
12715
|
+
}
|
|
12716
|
+
|
|
12644
12717
|
// src/dataset/item.ts
|
|
12645
12718
|
var Item = class extends Dataset {
|
|
12646
12719
|
index;
|
|
@@ -12762,6 +12835,15 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
12762
12835
|
items.push(new Item({ warnings: [], elements: inner.elements, index: itemIndex }));
|
|
12763
12836
|
cursor.position = inner.endOffset;
|
|
12764
12837
|
} else {
|
|
12838
|
+
if (opts.explicitLength !== void 0 && endLimit < buffer.length && cursor.position + itemLength > endLimit) {
|
|
12839
|
+
emit(
|
|
12840
|
+
itemCrossesSequenceEnd(
|
|
12841
|
+
{ byteOffset: itemHeaderStart },
|
|
12842
|
+
itemTag,
|
|
12843
|
+
Math.max(0, endLimit - cursor.position)
|
|
12844
|
+
)
|
|
12845
|
+
);
|
|
12846
|
+
}
|
|
12765
12847
|
if (cursor.position + itemLength > buffer.length) {
|
|
12766
12848
|
throw new DicomParseError(
|
|
12767
12849
|
FATAL_CODES.INVALID_FILE_META,
|
|
@@ -12897,8 +12979,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12897
12979
|
cursor.position = seq.endOffset;
|
|
12898
12980
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12899
12981
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12900
|
-
|
|
12901
|
-
|
|
12982
|
+
defineElement(
|
|
12983
|
+
elements,
|
|
12902
12984
|
new Element({
|
|
12903
12985
|
tag,
|
|
12904
12986
|
vr,
|
|
@@ -12909,7 +12991,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12909
12991
|
littleEndian: true,
|
|
12910
12992
|
items: seq.items,
|
|
12911
12993
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12912
|
-
})
|
|
12994
|
+
}),
|
|
12995
|
+
emit
|
|
12913
12996
|
);
|
|
12914
12997
|
continue;
|
|
12915
12998
|
}
|
|
@@ -12928,8 +13011,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12928
13011
|
cursor.position = cp246.endOffset;
|
|
12929
13012
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(valueRawStart, cursor.position)) : buffer.subarray(valueRawStart, cursor.position);
|
|
12930
13013
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12931
|
-
|
|
12932
|
-
|
|
13014
|
+
defineElement(
|
|
13015
|
+
elements,
|
|
12933
13016
|
new Element({
|
|
12934
13017
|
tag,
|
|
12935
13018
|
vr: "SQ",
|
|
@@ -12942,7 +13025,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12942
13025
|
littleEndian: true,
|
|
12943
13026
|
items: cp246.items,
|
|
12944
13027
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12945
|
-
})
|
|
13028
|
+
}),
|
|
13029
|
+
emit
|
|
12946
13030
|
);
|
|
12947
13031
|
continue;
|
|
12948
13032
|
}
|
|
@@ -12978,8 +13062,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12978
13062
|
);
|
|
12979
13063
|
if (seq.success) {
|
|
12980
13064
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
12981
|
-
|
|
12982
|
-
|
|
13065
|
+
defineElement(
|
|
13066
|
+
elements,
|
|
12983
13067
|
new Element({
|
|
12984
13068
|
tag,
|
|
12985
13069
|
vr,
|
|
@@ -12990,7 +13074,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
12990
13074
|
littleEndian: true,
|
|
12991
13075
|
items: seq.items,
|
|
12992
13076
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
12993
|
-
})
|
|
13077
|
+
}),
|
|
13078
|
+
emit
|
|
12994
13079
|
);
|
|
12995
13080
|
continue;
|
|
12996
13081
|
}
|
|
@@ -13003,8 +13088,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13003
13088
|
}
|
|
13004
13089
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
13005
13090
|
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13006
|
-
|
|
13007
|
-
|
|
13091
|
+
defineElement(
|
|
13092
|
+
elements,
|
|
13008
13093
|
new Element({
|
|
13009
13094
|
tag,
|
|
13010
13095
|
vr,
|
|
@@ -13016,7 +13101,8 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13016
13101
|
littleEndian: true,
|
|
13017
13102
|
...ctx.currentCharset !== void 0 ? { specificCharacterSet: ctx.currentCharset } : {},
|
|
13018
13103
|
...privateCreator !== void 0 ? { privateCreator } : {}
|
|
13019
|
-
})
|
|
13104
|
+
}),
|
|
13105
|
+
emit
|
|
13020
13106
|
);
|
|
13021
13107
|
}
|
|
13022
13108
|
return { elements, endOffset: cursor.position };
|
|
@@ -13123,8 +13209,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13123
13209
|
cursor.position = seqResult.endOffset;
|
|
13124
13210
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13125
13211
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13126
|
-
|
|
13127
|
-
|
|
13212
|
+
defineElement(
|
|
13213
|
+
elements,
|
|
13128
13214
|
new Element({
|
|
13129
13215
|
tag,
|
|
13130
13216
|
vr: "SQ",
|
|
@@ -13135,7 +13221,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13135
13221
|
littleEndian: mode.littleEndian,
|
|
13136
13222
|
items: seqResult.items,
|
|
13137
13223
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
13138
|
-
})
|
|
13224
|
+
}),
|
|
13225
|
+
emit
|
|
13139
13226
|
);
|
|
13140
13227
|
continue;
|
|
13141
13228
|
}
|
|
@@ -13150,8 +13237,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13150
13237
|
const result = parseSequence(buffer, valueStart2, ctx, emit, seqOpts);
|
|
13151
13238
|
cursor.position = result.endOffset;
|
|
13152
13239
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13153
|
-
|
|
13154
|
-
|
|
13240
|
+
defineElement(
|
|
13241
|
+
elements,
|
|
13155
13242
|
new Element({
|
|
13156
13243
|
tag,
|
|
13157
13244
|
vr: "OB",
|
|
@@ -13162,7 +13249,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13162
13249
|
byteOffset: headerStart,
|
|
13163
13250
|
littleEndian: mode.littleEndian,
|
|
13164
13251
|
items: result.items
|
|
13165
|
-
})
|
|
13252
|
+
}),
|
|
13253
|
+
emit
|
|
13166
13254
|
);
|
|
13167
13255
|
continue;
|
|
13168
13256
|
}
|
|
@@ -13181,8 +13269,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13181
13269
|
cursor.position = cp246.endOffset;
|
|
13182
13270
|
const rawBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, cursor.position)) : buffer.subarray(headerStart, cursor.position);
|
|
13183
13271
|
const privateCreator3 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13184
|
-
|
|
13185
|
-
|
|
13272
|
+
defineElement(
|
|
13273
|
+
elements,
|
|
13186
13274
|
new Element({
|
|
13187
13275
|
tag,
|
|
13188
13276
|
vr: "SQ",
|
|
@@ -13195,15 +13283,16 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13195
13283
|
littleEndian: mode.littleEndian,
|
|
13196
13284
|
items: cp246.items,
|
|
13197
13285
|
...privateCreator3 !== void 0 ? { privateCreator: privateCreator3 } : {}
|
|
13198
|
-
})
|
|
13286
|
+
}),
|
|
13287
|
+
emit
|
|
13199
13288
|
);
|
|
13200
13289
|
continue;
|
|
13201
13290
|
}
|
|
13202
13291
|
const privateCreator2 = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13203
13292
|
const fallbackEnd = buffer.length;
|
|
13204
13293
|
const fallbackBytes = ctx.copyValues ? copyValueBytes(buffer.subarray(headerStart, fallbackEnd)) : buffer.subarray(headerStart, fallbackEnd);
|
|
13205
|
-
|
|
13206
|
-
|
|
13294
|
+
defineElement(
|
|
13295
|
+
elements,
|
|
13207
13296
|
new Element({
|
|
13208
13297
|
tag,
|
|
13209
13298
|
vr: "UN",
|
|
@@ -13213,7 +13302,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13213
13302
|
byteOffset: headerStart,
|
|
13214
13303
|
littleEndian: mode.littleEndian,
|
|
13215
13304
|
...privateCreator2 !== void 0 ? { privateCreator: privateCreator2 } : {}
|
|
13216
|
-
})
|
|
13305
|
+
}),
|
|
13306
|
+
emit
|
|
13217
13307
|
);
|
|
13218
13308
|
cursor.position = fallbackEnd;
|
|
13219
13309
|
continue;
|
|
@@ -13248,8 +13338,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13248
13338
|
applySpecificCharacterSet(tag, valueSlice, ctx, emit, position);
|
|
13249
13339
|
const privateCreator = safeModelCreator(resolvePrivateCreator(tag, ctx), ctx);
|
|
13250
13340
|
const finalVr = vr;
|
|
13251
|
-
|
|
13252
|
-
|
|
13341
|
+
defineElement(
|
|
13342
|
+
elements,
|
|
13253
13343
|
new Element({
|
|
13254
13344
|
tag,
|
|
13255
13345
|
vr: finalVr,
|
|
@@ -13261,7 +13351,8 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13261
13351
|
littleEndian: mode.littleEndian,
|
|
13262
13352
|
...ctx.currentCharset !== void 0 ? { specificCharacterSet: ctx.currentCharset } : {},
|
|
13263
13353
|
...privateCreator !== void 0 ? { privateCreator } : {}
|
|
13264
|
-
})
|
|
13354
|
+
}),
|
|
13355
|
+
emit
|
|
13265
13356
|
);
|
|
13266
13357
|
}
|
|
13267
13358
|
return { elements, endOffset: cursor.position };
|
|
@@ -15093,6 +15184,19 @@ function itemStreamOverrunsSequence(el, encoding) {
|
|
|
15093
15184
|
const headerBytes = encoding === "implicit" ? 0 : EXPLICIT_SQ_HEADER_BYTES;
|
|
15094
15185
|
return el.rawBytes.length - headerBytes > el.length;
|
|
15095
15186
|
}
|
|
15187
|
+
function settledBound(source, encoding) {
|
|
15188
|
+
let index = source.length;
|
|
15189
|
+
let byteOffset;
|
|
15190
|
+
for (const [at, el] of source.entries()) {
|
|
15191
|
+
if (!itemStreamOverrunsSequence(el, encoding) && !isUnauditableSequence(el)) continue;
|
|
15192
|
+
if (index === source.length) index = at + 1;
|
|
15193
|
+
if (byteOffset === void 0 || el.byteOffset < byteOffset) byteOffset = el.byteOffset;
|
|
15194
|
+
}
|
|
15195
|
+
return { index, byteOffset };
|
|
15196
|
+
}
|
|
15197
|
+
function isSettled(el, at, bound) {
|
|
15198
|
+
return at < bound.index && (bound.byteOffset === void 0 || el.byteOffset <= bound.byteOffset);
|
|
15199
|
+
}
|
|
15096
15200
|
function keepsPrivate(el, ctx, creators) {
|
|
15097
15201
|
if (!ctx.active.has("RetainSafePrivate") || ctx.profile === void 0) return false;
|
|
15098
15202
|
if (isPrivateCreatorElement(el.tag)) {
|
|
@@ -15112,10 +15216,11 @@ function processElements(source, ctx, contextPath, reservationsUsable) {
|
|
|
15112
15216
|
undefinedVrElements: [],
|
|
15113
15217
|
warnings: []
|
|
15114
15218
|
};
|
|
15115
|
-
const
|
|
15116
|
-
|
|
15219
|
+
const bound = settledBound(source, ctx.encoding);
|
|
15220
|
+
const creators = creatorsInScope(source.filter((el, at) => isSettled(el, at, bound)));
|
|
15221
|
+
for (const [at, el] of source.entries()) {
|
|
15117
15222
|
if (isPrivateTag(el.tag)) {
|
|
15118
|
-
if (reservationsUsable && keepsPrivate(el, ctx, creators))
|
|
15223
|
+
if (reservationsUsable && isSettled(el, at, bound) && keepsPrivate(el, ctx, creators))
|
|
15119
15224
|
keepOrEmpty(el, ctx, contextPath, out);
|
|
15120
15225
|
else out.removedPrivateTags.push(el.tag);
|
|
15121
15226
|
continue;
|
|
@@ -15248,6 +15353,47 @@ function defaultMethod(active) {
|
|
|
15248
15353
|
const base = "Cosyte @cosyte/dicom: PS3.15 Basic Application Level Confidentiality Profile";
|
|
15249
15354
|
return active.size === 0 ? base : `${base} + ${[...active].join(", ")}`;
|
|
15250
15355
|
}
|
|
15356
|
+
var VALUE_DELIMITER = 92;
|
|
15357
|
+
var MAX_SHORT_FORM_VALUE_BYTES = 65534;
|
|
15358
|
+
function addDeidentificationMethod(existing, method) {
|
|
15359
|
+
const added = trimTrailingPad(Buffer.from(method, "latin1"));
|
|
15360
|
+
if (existing === void 0 || existing.vr !== "LO") {
|
|
15361
|
+
return { value: added, replacedPrior: false, retainedPrior: false };
|
|
15362
|
+
}
|
|
15363
|
+
const kept = Buffer.from(trimTrailingPad(existing.rawBytes));
|
|
15364
|
+
if (kept.length === 0) return { value: added, replacedPrior: false, retainedPrior: false };
|
|
15365
|
+
const keptValues = splitValues(kept).map(trimTrailingPad);
|
|
15366
|
+
const missing = added.length === 0 ? [] : splitValues(added).filter((v) => !keptValues.some((k) => k.equals(trimTrailingPad(v))));
|
|
15367
|
+
const parts = [kept];
|
|
15368
|
+
for (const value2 of missing) {
|
|
15369
|
+
parts.push(Buffer.from([VALUE_DELIMITER]), value2);
|
|
15370
|
+
}
|
|
15371
|
+
const value = trimTrailingPad(Buffer.concat(parts));
|
|
15372
|
+
if (value.length > MAX_SHORT_FORM_VALUE_BYTES) {
|
|
15373
|
+
return { value: added, replacedPrior: true, retainedPrior: false };
|
|
15374
|
+
}
|
|
15375
|
+
return { value, replacedPrior: false, retainedPrior: true };
|
|
15376
|
+
}
|
|
15377
|
+
function trimTrailingPad(value) {
|
|
15378
|
+
let end = value.length;
|
|
15379
|
+
while (end > 0) {
|
|
15380
|
+
const last = value[end - 1];
|
|
15381
|
+
if (last === 32 || last === 0) end--;
|
|
15382
|
+
else break;
|
|
15383
|
+
}
|
|
15384
|
+
return value.subarray(0, end);
|
|
15385
|
+
}
|
|
15386
|
+
function splitValues(value) {
|
|
15387
|
+
const out = [];
|
|
15388
|
+
let start = 0;
|
|
15389
|
+
for (let i = 0; i <= value.length; i++) {
|
|
15390
|
+
if (i === value.length || value[i] === VALUE_DELIMITER) {
|
|
15391
|
+
out.push(value.subarray(start, i));
|
|
15392
|
+
start = i + 1;
|
|
15393
|
+
}
|
|
15394
|
+
}
|
|
15395
|
+
return out;
|
|
15396
|
+
}
|
|
15251
15397
|
function hasUncleanedBurnedIn(ds) {
|
|
15252
15398
|
if (!ds.has(TAG_PIXEL_DATA)) return false;
|
|
15253
15399
|
const flag = ds.get(TAG_BURNED_IN_ANNOTATION);
|
|
@@ -15281,11 +15427,23 @@ function deidentify(ds, options = {}) {
|
|
|
15281
15427
|
insertedScalar(TAG_PATIENT_IDENTITY_REMOVED, "CS", Buffer.from("YES", "latin1"), littleEndian)
|
|
15282
15428
|
);
|
|
15283
15429
|
const method = options.deidentificationMethod ?? defaultMethod(active);
|
|
15430
|
+
const priorMethod = elements.get(TAG_DEIDENTIFICATION_METHOD);
|
|
15431
|
+
const deidentMethod = addDeidentificationMethod(priorMethod, method);
|
|
15284
15432
|
elements.set(
|
|
15285
15433
|
TAG_DEIDENTIFICATION_METHOD,
|
|
15286
|
-
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO",
|
|
15434
|
+
insertedScalar(TAG_DEIDENTIFICATION_METHOD, "LO", deidentMethod.value, littleEndian)
|
|
15287
15435
|
);
|
|
15288
15436
|
const warnings = [...processed.warnings];
|
|
15437
|
+
if (deidentMethod.replacedPrior) {
|
|
15438
|
+
warnings.push(
|
|
15439
|
+
deidentMethodNotAdded({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
|
|
15440
|
+
);
|
|
15441
|
+
}
|
|
15442
|
+
if (deidentMethod.retainedPrior) {
|
|
15443
|
+
warnings.push(
|
|
15444
|
+
deidentMethodPriorRetained({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
|
|
15445
|
+
);
|
|
15446
|
+
}
|
|
15289
15447
|
if (hasUncleanedBurnedIn(ds)) {
|
|
15290
15448
|
const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
|
|
15291
15449
|
warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
|