@cosyte/dicom 0.0.14 โ 0.0.15
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 +280 -0
- package/README.md +2 -2
- package/dist/index.cjs +87 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -13
- package/dist/index.d.ts +52 -13
- package/dist/index.mjs +87 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -329,13 +329,31 @@ interface ParseOptions {
|
|
|
329
329
|
*
|
|
330
330
|
* ๐ **THE ESCALATED DIAGNOSTIC CARRIES SOURCE BYTES THAT THE WARNING DOES
|
|
331
331
|
* NOT.** A `DicomParseWarning.message` is a frozen registry string with only
|
|
332
|
-
* structural tokens filled in
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
332
|
+
* structural tokens filled in: `{tag}` renders only a tag PS3.6's element
|
|
333
|
+
* registry carries a literal row for, `{vr}` only one of the 34 VRs PS3.5
|
|
334
|
+
* section 6.2 defines, and a raw length or byte value a header carries is
|
|
335
|
+
* bound out of the factory signature rather than rendered. That now holds on
|
|
336
|
+
* the `deidentify()` codes too:
|
|
337
|
+
* `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` and
|
|
338
|
+
* `DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE` rendered `Element.rawBytes.length`
|
|
339
|
+
* through `0.0.14`, which equals the declared Value Length and was reachable
|
|
340
|
+
* from a fabricated header, and both slots are gone. So is
|
|
341
|
+
* `DICOM_ITEM_CROSSES_SEQUENCE_END`'s remaining-bytes count, because **a raw
|
|
342
|
+
* number shifted by a constant the reader can compute is that raw number**: it
|
|
343
|
+
* was the enclosing sequence's declared Value Length less the bytes of that
|
|
344
|
+
* sequence already consumed. **The exceptions are named in one place that is
|
|
345
|
+
* not a record of a past change, and are deliberately not restated here** -
|
|
346
|
+
* the `WARNING_MESSAGES` docblock in `./warnings.ts`, which this JSDoc used to
|
|
347
|
+
* carry a copy of.
|
|
348
|
+
* **This is a statement about `w.message` and about nothing else**: the two
|
|
349
|
+
* byte counts still exist on `report.undefinedVrElements[].byteLength` and
|
|
350
|
+
* `report.unauditableSequences[].byteLength`, model fields on a type whose own
|
|
351
|
+
* docs say it is not a value-free surface. **No
|
|
352
|
+
* safe-to-log verdict is stated here** - that sentence was corrected twice and
|
|
353
|
+
* is deleted rather than tried a third time; the mechanism is above and the
|
|
354
|
+
* treatment is in the package's troubleshooting docs. The `DicomParseError`
|
|
355
|
+
* this option raises in its place is a different and larger surface: it also
|
|
356
|
+
* carries `snippet`,
|
|
339
357
|
* **16 raw bytes, unredacted** (D-10), read at the warning's own `byteOffset`.
|
|
340
358
|
* A message-only PHI review of the lenient path therefore does not transfer to
|
|
341
359
|
* the strict one. Log `err.code`, `err.byteOffset` and `err.message`; treat
|
|
@@ -2706,12 +2724,21 @@ interface UndefinedVrFinding {
|
|
|
2706
2724
|
*
|
|
2707
2725
|
* **An input-derived count, and on this finding specifically that is not a
|
|
2708
2726
|
* formality.** For the sibling findings the length came from a header the
|
|
2709
|
-
* sender wrote; here the
|
|
2710
|
-
* the tag bytes. What is published is the *number* they decode to,
|
|
2711
|
-
*
|
|
2712
|
-
*
|
|
2713
|
-
*
|
|
2714
|
-
*
|
|
2727
|
+
* sender wrote; here the four length bytes can themselves be value bytes, like
|
|
2728
|
+
* the tag bytes. What is published is the *number* they decode to, and one
|
|
2729
|
+
* `readUInt32LE` puts the bytes back: a fabricated header reading `"SO\0\0"`
|
|
2730
|
+
* publishes `20307`, two letters of a surname. Do not describe this field as
|
|
2731
|
+
* "structural, never a value" the way its siblings are described.
|
|
2732
|
+
*
|
|
2733
|
+
* **๐ฉบ IT IS NO LONGER "the same footing as every `{n}` in the warning
|
|
2734
|
+
* registry", AND THAT SENTENCE WAS THE ONE THIS FIELD SHIPPED WITH.**
|
|
2735
|
+
* `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` rendered exactly this number and
|
|
2736
|
+
* does not any more - it is bound out of `undefinedVrNotAuditable`'s
|
|
2737
|
+
* signature. So this field is now a **model field**, on the same standing
|
|
2738
|
+
* exception as `removedPrivateTags`, `unauditableSequences[].tag`, `uidMap`
|
|
2739
|
+
* and `contextPath`: narrowing it is a product call, because a bound empties
|
|
2740
|
+
* it on every well-formed file, where it is exactly the audit number the field
|
|
2741
|
+
* exists to carry. Deliberately unchanged; see this type's own summary.
|
|
2715
2742
|
*/
|
|
2716
2743
|
readonly byteLength: number;
|
|
2717
2744
|
/**
|
|
@@ -2754,6 +2781,18 @@ interface UndefinedVrFinding {
|
|
|
2754
2781
|
* and still answers it whenever the fabricated VR is outside the 34 PS3.5
|
|
2755
2782
|
* ยง6.2 defines. It cannot when the fabricated VR is one of them, because those
|
|
2756
2783
|
* two files are byte-identical.
|
|
2784
|
+
* - **`undefinedVrElements[].byteLength` and
|
|
2785
|
+
* `unauditableSequences[].byteLength`** - the declared Value Length read off
|
|
2786
|
+
* the element header, so on a fabricated header it is four document bytes
|
|
2787
|
+
* wearing a decimal: `"SO\0\0"` publishes `20307`, two letters of a surname,
|
|
2788
|
+
* put back with one `readUInt32LE`. **They JOINED this list rather than
|
|
2789
|
+
* always having been on it.** Through `0.0.14` the two
|
|
2790
|
+
* `DICOM_DEIDENT_*_NOT_AUDITABLE` **messages** rendered the same number;
|
|
2791
|
+
* binding it out of those factory signatures left these model fields as its
|
|
2792
|
+
* only publisher, which is a smaller surface and not a closed one. Narrowing
|
|
2793
|
+
* them is a product call rather than a defect fix: a bound empties the field
|
|
2794
|
+
* on every well-formed file, where the number is exactly the audit
|
|
2795
|
+
* information it exists to carry.
|
|
2757
2796
|
* - **`contextPath`, on all four findings that carry one** - see
|
|
2758
2797
|
* {@link DeidentifiedAttribute.contextPath}, which holds the measurement. The
|
|
2759
2798
|
* segment tags come off the wire with no table behind them, so a fabricated
|
package/dist/index.d.ts
CHANGED
|
@@ -329,13 +329,31 @@ interface ParseOptions {
|
|
|
329
329
|
*
|
|
330
330
|
* ๐ **THE ESCALATED DIAGNOSTIC CARRIES SOURCE BYTES THAT THE WARNING DOES
|
|
331
331
|
* NOT.** A `DicomParseWarning.message` is a frozen registry string with only
|
|
332
|
-
* structural tokens filled in
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
332
|
+
* structural tokens filled in: `{tag}` renders only a tag PS3.6's element
|
|
333
|
+
* registry carries a literal row for, `{vr}` only one of the 34 VRs PS3.5
|
|
334
|
+
* section 6.2 defines, and a raw length or byte value a header carries is
|
|
335
|
+
* bound out of the factory signature rather than rendered. That now holds on
|
|
336
|
+
* the `deidentify()` codes too:
|
|
337
|
+
* `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` and
|
|
338
|
+
* `DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE` rendered `Element.rawBytes.length`
|
|
339
|
+
* through `0.0.14`, which equals the declared Value Length and was reachable
|
|
340
|
+
* from a fabricated header, and both slots are gone. So is
|
|
341
|
+
* `DICOM_ITEM_CROSSES_SEQUENCE_END`'s remaining-bytes count, because **a raw
|
|
342
|
+
* number shifted by a constant the reader can compute is that raw number**: it
|
|
343
|
+
* was the enclosing sequence's declared Value Length less the bytes of that
|
|
344
|
+
* sequence already consumed. **The exceptions are named in one place that is
|
|
345
|
+
* not a record of a past change, and are deliberately not restated here** -
|
|
346
|
+
* the `WARNING_MESSAGES` docblock in `./warnings.ts`, which this JSDoc used to
|
|
347
|
+
* carry a copy of.
|
|
348
|
+
* **This is a statement about `w.message` and about nothing else**: the two
|
|
349
|
+
* byte counts still exist on `report.undefinedVrElements[].byteLength` and
|
|
350
|
+
* `report.unauditableSequences[].byteLength`, model fields on a type whose own
|
|
351
|
+
* docs say it is not a value-free surface. **No
|
|
352
|
+
* safe-to-log verdict is stated here** - that sentence was corrected twice and
|
|
353
|
+
* is deleted rather than tried a third time; the mechanism is above and the
|
|
354
|
+
* treatment is in the package's troubleshooting docs. The `DicomParseError`
|
|
355
|
+
* this option raises in its place is a different and larger surface: it also
|
|
356
|
+
* carries `snippet`,
|
|
339
357
|
* **16 raw bytes, unredacted** (D-10), read at the warning's own `byteOffset`.
|
|
340
358
|
* A message-only PHI review of the lenient path therefore does not transfer to
|
|
341
359
|
* the strict one. Log `err.code`, `err.byteOffset` and `err.message`; treat
|
|
@@ -2706,12 +2724,21 @@ interface UndefinedVrFinding {
|
|
|
2706
2724
|
*
|
|
2707
2725
|
* **An input-derived count, and on this finding specifically that is not a
|
|
2708
2726
|
* formality.** For the sibling findings the length came from a header the
|
|
2709
|
-
* sender wrote; here the
|
|
2710
|
-
* the tag bytes. What is published is the *number* they decode to,
|
|
2711
|
-
*
|
|
2712
|
-
*
|
|
2713
|
-
*
|
|
2714
|
-
*
|
|
2727
|
+
* sender wrote; here the four length bytes can themselves be value bytes, like
|
|
2728
|
+
* the tag bytes. What is published is the *number* they decode to, and one
|
|
2729
|
+
* `readUInt32LE` puts the bytes back: a fabricated header reading `"SO\0\0"`
|
|
2730
|
+
* publishes `20307`, two letters of a surname. Do not describe this field as
|
|
2731
|
+
* "structural, never a value" the way its siblings are described.
|
|
2732
|
+
*
|
|
2733
|
+
* **๐ฉบ IT IS NO LONGER "the same footing as every `{n}` in the warning
|
|
2734
|
+
* registry", AND THAT SENTENCE WAS THE ONE THIS FIELD SHIPPED WITH.**
|
|
2735
|
+
* `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` rendered exactly this number and
|
|
2736
|
+
* does not any more - it is bound out of `undefinedVrNotAuditable`'s
|
|
2737
|
+
* signature. So this field is now a **model field**, on the same standing
|
|
2738
|
+
* exception as `removedPrivateTags`, `unauditableSequences[].tag`, `uidMap`
|
|
2739
|
+
* and `contextPath`: narrowing it is a product call, because a bound empties
|
|
2740
|
+
* it on every well-formed file, where it is exactly the audit number the field
|
|
2741
|
+
* exists to carry. Deliberately unchanged; see this type's own summary.
|
|
2715
2742
|
*/
|
|
2716
2743
|
readonly byteLength: number;
|
|
2717
2744
|
/**
|
|
@@ -2754,6 +2781,18 @@ interface UndefinedVrFinding {
|
|
|
2754
2781
|
* and still answers it whenever the fabricated VR is outside the 34 PS3.5
|
|
2755
2782
|
* ยง6.2 defines. It cannot when the fabricated VR is one of them, because those
|
|
2756
2783
|
* two files are byte-identical.
|
|
2784
|
+
* - **`undefinedVrElements[].byteLength` and
|
|
2785
|
+
* `unauditableSequences[].byteLength`** - the declared Value Length read off
|
|
2786
|
+
* the element header, so on a fabricated header it is four document bytes
|
|
2787
|
+
* wearing a decimal: `"SO\0\0"` publishes `20307`, two letters of a surname,
|
|
2788
|
+
* put back with one `readUInt32LE`. **They JOINED this list rather than
|
|
2789
|
+
* always having been on it.** Through `0.0.14` the two
|
|
2790
|
+
* `DICOM_DEIDENT_*_NOT_AUDITABLE` **messages** rendered the same number;
|
|
2791
|
+
* binding it out of those factory signatures left these model fields as its
|
|
2792
|
+
* only publisher, which is a smaller surface and not a closed one. Narrowing
|
|
2793
|
+
* them is a product call rather than a defect fix: a bound empties the field
|
|
2794
|
+
* on every well-formed file, where the number is exactly the audit
|
|
2795
|
+
* information it exists to carry.
|
|
2757
2796
|
* - **`contextPath`, on all four findings that carry one** - see
|
|
2758
2797
|
* {@link DeidentifiedAttribute.contextPath}, which holds the measurement. The
|
|
2759
2798
|
* segment tags come off the wire with no table behind them, so a fabricated
|
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.15";
|
|
13
13
|
|
|
14
14
|
// src/dictionary/index.ts
|
|
15
15
|
var dictionary_exports = {};
|
|
@@ -11785,6 +11785,11 @@ function safePrivateCreator(creator, profile) {
|
|
|
11785
11785
|
function renderVr(vr) {
|
|
11786
11786
|
return vr !== void 0 && KNOWN_VRS.has(vr) ? vr : WITHHELD;
|
|
11787
11787
|
}
|
|
11788
|
+
var TAG_SHAPE = /^[0-9A-F]{8}$/u;
|
|
11789
|
+
function renderTag(tag) {
|
|
11790
|
+
if (tag === void 0 || !TAG_SHAPE.test(tag)) return WITHHELD;
|
|
11791
|
+
return lookup(tag) === void 0 ? WITHHELD : tag;
|
|
11792
|
+
}
|
|
11788
11793
|
|
|
11789
11794
|
// src/parser/fatals.ts
|
|
11790
11795
|
var SUPPORTED_TRANSFER_SYNTAXES = "1.2.840.10008.1.2, 1.2.840.10008.1.2.1, 1.2.840.10008.1.2.2, 1.2.840.10008.1.2.1.99";
|
|
@@ -11833,7 +11838,7 @@ var FATAL_MESSAGES = Object.freeze({
|
|
|
11833
11838
|
// four bytes are not what they claim to be.
|
|
11834
11839
|
FILE_META_GROUP_LENGTH_OVERRUNS: {
|
|
11835
11840
|
code: FATAL_CODES.INVALID_FILE_META,
|
|
11836
|
-
message: "(0002,0000) FileMetaInformationGroupLength declares more bytes than remain in the input
|
|
11841
|
+
message: "(0002,0000) FileMetaInformationGroupLength declares more bytes than remain in the input. The declared length and the count of bytes that remain are withheld; the byte offset locates the group."
|
|
11837
11842
|
},
|
|
11838
11843
|
FILE_META_TRANSFER_SYNTAX_MISSING: {
|
|
11839
11844
|
code: FATAL_CODES.INVALID_FILE_META,
|
|
@@ -11876,7 +11881,7 @@ var FATAL_MESSAGES = Object.freeze({
|
|
|
11876
11881
|
},
|
|
11877
11882
|
ELEMENT_LENGTH_EXCEEDS_BUFFER: {
|
|
11878
11883
|
code: FATAL_CODES.INVALID_FILE_META,
|
|
11879
|
-
message: "An element declares a Value Length reaching past the end of the bytes being read
|
|
11884
|
+
message: "An element declares a Value Length reaching past the end of the bytes being read. Its tag, its declared length and the count of bytes that remain are withheld; the byte offset locates it."
|
|
11880
11885
|
},
|
|
11881
11886
|
UNEXPECTED_TAG_INSIDE_SEQUENCE: {
|
|
11882
11887
|
code: FATAL_CODES.INVALID_FILE_META,
|
|
@@ -11947,8 +11952,8 @@ function unsupportedTransferSyntax(buffer, offset, ts) {
|
|
|
11947
11952
|
function fileMetaTruncated(buffer, offset) {
|
|
11948
11953
|
return at("FILE_META_TRUNCATED", buffer, offset);
|
|
11949
11954
|
}
|
|
11950
|
-
function fileMetaGroupLengthOverruns(buffer, offset
|
|
11951
|
-
return at("FILE_META_GROUP_LENGTH_OVERRUNS", buffer, offset
|
|
11955
|
+
function fileMetaGroupLengthOverruns(buffer, offset) {
|
|
11956
|
+
return at("FILE_META_GROUP_LENGTH_OVERRUNS", buffer, offset);
|
|
11952
11957
|
}
|
|
11953
11958
|
function fileMetaTransferSyntaxMissing(buffer, offset) {
|
|
11954
11959
|
return at("FILE_META_TRANSFER_SYNTAX_MISSING", buffer, offset);
|
|
@@ -11977,8 +11982,8 @@ function undefinedLengthOnNonSqExplicit(buffer, offset, vr) {
|
|
|
11977
11982
|
function undefinedLengthOnNonSqImplicit(buffer, offset, vr) {
|
|
11978
11983
|
return at("UNDEFINED_LENGTH_ON_NON_SQ_IMPLICIT", buffer, offset, { vr });
|
|
11979
11984
|
}
|
|
11980
|
-
function elementLengthExceedsBuffer(buffer, offset
|
|
11981
|
-
return at("ELEMENT_LENGTH_EXCEEDS_BUFFER", buffer, offset
|
|
11985
|
+
function elementLengthExceedsBuffer(buffer, offset) {
|
|
11986
|
+
return at("ELEMENT_LENGTH_EXCEEDS_BUFFER", buffer, offset);
|
|
11982
11987
|
}
|
|
11983
11988
|
function unexpectedTagInsideSequence(buffer, offset) {
|
|
11984
11989
|
return at("UNEXPECTED_TAG_INSIDE_SEQUENCE", buffer, offset);
|
|
@@ -12204,19 +12209,38 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
12204
12209
|
DICOM_FILE_META_GROUP_LENGTH_MISSING: "(0002,0000) FileMetaInformationGroupLength missing; scanning forward to first non-(0002,xxxx) element.",
|
|
12205
12210
|
DICOM_FILE_META_GROUP_LENGTH_MISMATCH: "(0002,0000) FileMetaInformationGroupLength declared {n} bytes; actual File Meta group size is {n2} bytes. Trusting actual.",
|
|
12206
12211
|
DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR: "Element ({tag}) uses undefined length (0xFFFFFFFF) under an Explicit VR transfer syntax.",
|
|
12207
|
-
|
|
12212
|
+
// The declared length is gone from this string and the reason is measured, not
|
|
12213
|
+
// cautious: this code fires on ANY element header, including one a lying Value
|
|
12214
|
+
// Length upstream composed out of somebody's value, and it then rendered four
|
|
12215
|
+
// payload bytes as {tag} and four more as the decimal {n} - eight consecutive
|
|
12216
|
+
// bytes of a name in one message. See `oddLengthValuePadded`.
|
|
12217
|
+
DICOM_ODD_LENGTH_VALUE_PADDED: "Element ({tag}) has an odd declared length; cursor advanced by one padding byte to maintain alignment. The declared length is withheld; the byte offset identifies the element.",
|
|
12208
12218
|
DICOM_VR_MISMATCH: "Element ({tag}) on-wire VR is {vr2}; dictionary lists {vr}. Trusting on-wire VR.",
|
|
12209
12219
|
// No tag, and the reason is the one `nonzeroReservedBytes` states: this code's
|
|
12210
12220
|
// three siblings below fire only on an ODD group, and an odd group is the one
|
|
12211
12221
|
// class of tag no closed table this library holds can ever vouch for. See
|
|
12212
12222
|
// `privateTagNoCreator`.
|
|
12213
12223
|
DICOM_PRIVATE_TAG_NO_CREATOR: "A private element has no Private Creator registered for its block; treating as VR=UN. Its tag is withheld; the byte offset identifies the element.",
|
|
12214
|
-
|
|
12224
|
+
// No tag, and it is the membership bound in `renderTag` that made the slot
|
|
12225
|
+
// dead rather than a judgement here: this code fires on a (gggg,0000), the
|
|
12226
|
+
// element number is a constant of the code, and PS3.6 carries a literal row
|
|
12227
|
+
// for exactly one such tag - (0002,0000), which is File Meta and never
|
|
12228
|
+
// reaches this code. So every tag this slot could ever have rendered was one
|
|
12229
|
+
// no closed table names, leaving the group number as sixteen free bits of a
|
|
12230
|
+
// header that may itself be fabricated. See `groupLengthInDataset`.
|
|
12231
|
+
DICOM_GROUP_LENGTH_IN_DATASET: "A retired Group Length element (gggg,0000) was encountered in the dataset; preserved as-is. Its group number is withheld; the byte offset identifies the element.",
|
|
12215
12232
|
// The tag is deliberately absent, and this is the only *parser* message where
|
|
12216
12233
|
// that is true. See `nonzeroReservedBytes`. Kept terse for the same reason the
|
|
12217
12234
|
// de-identify messages are: it is emitted once per element, and element count
|
|
12218
12235
|
// is attacker-chosen.
|
|
12219
|
-
|
|
12236
|
+
// ๐ฉบ THE TWO BYTE VALUES ARE GONE FOR THE REASON THE TAG WAS ALREADY GONE, AND
|
|
12237
|
+
// LEAVING THEM MADE THAT BOUND HALF A BOUND. This code's trigger is "these two
|
|
12238
|
+
// bytes are not what PS3.5 7.1.2 says they must be", so the header may not be
|
|
12239
|
+
// a header - and then the reserved pair is two bytes from inside some
|
|
12240
|
+
// element's value, printed as two decimals that reverse with no work at all.
|
|
12241
|
+
// Measured on a name-bearing payload across six under-declare deltas. See
|
|
12242
|
+
// `nonzeroReservedBytes`.
|
|
12243
|
+
DICOM_NONZERO_RESERVED_BYTES: "Non-zero reserved bytes between VR and length; ignoring. Their two values are withheld with the tag; the byte offset identifies the element.",
|
|
12220
12244
|
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.",
|
|
12221
12245
|
DICOM_UN_PARSED_AS_SQ: "Element ({tag}) has VR=UN with undefined length; descended as Implicit VR LE sequence per CP-246.",
|
|
12222
12246
|
DICOM_EMPTY_ITEM_IN_SEQUENCE: "Sequence ({tag}) contains an empty item (length=0); tolerated.",
|
|
@@ -12251,11 +12275,18 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
12251
12275
|
// No value and no length, for the same reason as the code above: the retained
|
|
12252
12276
|
// text is the file's own. The tag is a constant of this code.
|
|
12253
12277
|
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.",
|
|
12254
|
-
//
|
|
12255
|
-
//
|
|
12256
|
-
//
|
|
12257
|
-
|
|
12258
|
-
|
|
12278
|
+
// BOTH of the disagreeing length fields are deliberately absent, and the
|
|
12279
|
+
// second one is the eighth instance of `DICOM-DIAGNOSTIC-PHI-RESIDUALS`: the
|
|
12280
|
+
// remaining-bytes count this string used to carry was the enclosing sequence's
|
|
12281
|
+
// declared Value Length less the bytes of that sequence already consumed. See
|
|
12282
|
+
// `itemCrossesSequenceEnd`.
|
|
12283
|
+
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. The file's two length fields disagree (PS3.5 7.5.1 and 7.5.2 govern them); the item's is used. Both declared lengths are withheld; the byte offset locates the item.",
|
|
12284
|
+
// Declared but not emitted by this build. The declared length is bound out
|
|
12285
|
+
// anyway: it is a raw 32-bit read off a header, so activating this code later
|
|
12286
|
+
// with the slot still here would ship the leak this slice closed one code
|
|
12287
|
+
// over. `{n2}` stays - it is the product this parser computed from the image
|
|
12288
|
+
// description attributes, not a number it read. The tag is a constant.
|
|
12289
|
+
DICOM_PIXEL_DATA_LENGTH_MISMATCH: "(7FE0,0010) PixelData declared length does not match the computed {n2} bytes. The declared length is withheld; the byte offset identifies the element.",
|
|
12259
12290
|
DICOM_IMPLICIT_VR_FOR_PRIVATE_TAG_WITHOUT_VR: "A private element under Implicit VR LE has no VR override; falling back to UN. Its tag is withheld; the byte offset identifies the element.",
|
|
12260
12291
|
DICOM_PRIVATE_CREATOR_UNKNOWN: "A private element 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. Its tag is withheld; the byte offset identifies the element.",
|
|
12261
12292
|
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.",
|
|
@@ -12267,19 +12298,31 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
12267
12298
|
// its two producers is an element the profile declares a Sequence while the
|
|
12268
12299
|
// wire says UN or OB, so naming a VR here would state a fact about the file
|
|
12269
12300
|
// that the file contradicts - on the one channel this class designates.
|
|
12270
|
-
|
|
12301
|
+
// The recorded byte count is gone from this string, and the reason is the same
|
|
12302
|
+
// one that took the declared length out of `DICOM_ODD_LENGTH_VALUE_PADDED`:
|
|
12303
|
+
// `Element.rawBytes.length` EQUALS the Value Length off the element header, so
|
|
12304
|
+
// when that header is fabricated the decimal is four document bytes. See
|
|
12305
|
+
// `sequenceNotAuditable`. `report.unauditableSequences[].byteLength` still
|
|
12306
|
+
// carries it, on a type documented as not value-free.
|
|
12307
|
+
DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "Element ({tag}) is a Sequence carrier with no parsed items, so its recorded bytes could not be audited (PS3.15 E.1.1); emptied. The byte count is withheld; see report.unauditableSequences.",
|
|
12271
12308
|
// Deliberately short for the same reason as the code above: one per element,
|
|
12272
12309
|
// and the element count is chosen by the input.
|
|
12273
12310
|
//
|
|
12274
|
-
// NEITHER THE TAG NOR THE VR IS ECHOED, and that is
|
|
12275
|
-
// rather than caution. Every other factory's {tag} is
|
|
12276
|
-
// Data Element header; the condition that raises THIS one
|
|
12277
|
-
// was fabricated from bytes inside some element's value, so
|
|
12278
|
-
//
|
|
12279
|
-
//
|
|
12311
|
+
// NEITHER THE TAG NOR THE VR NOR THE BYTE COUNT IS ECHOED, and that is
|
|
12312
|
+
// specific to this code rather than caution. Every other factory's {tag} is
|
|
12313
|
+
// composed from a real Data Element header; the condition that raises THIS one
|
|
12314
|
+
// is that the header was fabricated from bytes inside some element's value, so
|
|
12315
|
+
// its four tag bytes, its two VR bytes AND its four length bytes are document
|
|
12316
|
+
// content. `renderTag` is a membership test against PS3.6's registry and would
|
|
12317
|
+
// refuse this one - but a slot that has to rely on that is a slot a future
|
|
12318
|
+
// call site can still be handed, so the withholding stays at the call site
|
|
12319
|
+
// where it does not depend on a table. The length had no table to fall back on
|
|
12320
|
+
// at all: the message withheld the two fields a renderer could check and then
|
|
12321
|
+
// printed the one it could not, which is the shape this code was already
|
|
12322
|
+
// written to refuse.
|
|
12280
12323
|
// The byte offset locates the element instead, and is a position this parser
|
|
12281
12324
|
// counted rather than anything the document said.
|
|
12282
|
-
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
|
|
12325
|
+
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 value bytes are not a Value Field this library decoded; emptied (PS3.15 E.1.1). Its tag, VR and byte count are withheld: an earlier under-declared length can make them fragments of some element's value. See report.undefinedVrElements.",
|
|
12283
12326
|
DICOM_BOM_IN_TEXT_VR: "Element ({tag}) {vr} value begins with a UTF-8 BOM; stripped on decode.",
|
|
12284
12327
|
DICOM_TRAILING_NULL_IN_TEXT_VR: "Element ({tag}) {vr} value has a trailing NULL pad where SPACE is expected; trimmed.",
|
|
12285
12328
|
DICOM_UI_TRAILING_SPACE: "Element ({tag}) UI value is SPACE-padded; UI requires NULL padding (PS3.5 \xA76.2). Trimmed.",
|
|
@@ -12290,10 +12333,6 @@ var WARNING_MESSAGES = Object.freeze({
|
|
|
12290
12333
|
DICOM_UNSUPPORTED_CHARSET: "(0008,0005) Specific Character Set value {n} names a defined term this build does not support; decoding text as UTF-8 best-effort. The term is not reproduced here - read the element if you need it.",
|
|
12291
12334
|
DICOM_CHARSET_AMBIGUOUS_SEPARATOR: "(0008,0005) Specific Character Set value {n} is ambiguous under the active code extensions; decoding text as UTF-8 best-effort."
|
|
12292
12335
|
});
|
|
12293
|
-
var TAG_SHAPE = /^[0-9A-F]{8}$/u;
|
|
12294
|
-
function renderTag(tag) {
|
|
12295
|
-
return tag !== void 0 && TAG_SHAPE.test(tag) ? tag : WITHHELD;
|
|
12296
|
-
}
|
|
12297
12336
|
function build2(code, position, tokens = {}) {
|
|
12298
12337
|
const message = WARNING_MESSAGES[code].replace("{tag}", renderTag(tokens.tag)).replace("{vr2}", renderVr(tokens.vr2)).replace("{vr}", renderVr(tokens.vr)).replace("{n2}", String(tokens.n2 ?? 0)).replace("{n}", String(tokens.n ?? 0));
|
|
12299
12338
|
return { code, message, position };
|
|
@@ -12313,11 +12352,8 @@ function fileMetaGroupLengthMismatch(position, declared, actual) {
|
|
|
12313
12352
|
function undefinedLengthInExplicitVR(position, tag) {
|
|
12314
12353
|
return build2(WARNING_CODES.DICOM_UNDEFINED_LENGTH_IN_EXPLICIT_VR, position, { tag });
|
|
12315
12354
|
}
|
|
12316
|
-
function oddLengthValuePadded(position, tag
|
|
12317
|
-
return build2(WARNING_CODES.DICOM_ODD_LENGTH_VALUE_PADDED, position, {
|
|
12318
|
-
tag,
|
|
12319
|
-
n: declaredLength
|
|
12320
|
-
});
|
|
12355
|
+
function oddLengthValuePadded(position, tag) {
|
|
12356
|
+
return build2(WARNING_CODES.DICOM_ODD_LENGTH_VALUE_PADDED, position, { tag });
|
|
12321
12357
|
}
|
|
12322
12358
|
function vrMismatch(position, tag, dictVR, fileVR) {
|
|
12323
12359
|
return build2(WARNING_CODES.DICOM_VR_MISMATCH, position, { tag, vr: dictVR, vr2: fileVR });
|
|
@@ -12325,14 +12361,11 @@ function vrMismatch(position, tag, dictVR, fileVR) {
|
|
|
12325
12361
|
function privateTagNoCreator(position) {
|
|
12326
12362
|
return build2(WARNING_CODES.DICOM_PRIVATE_TAG_NO_CREATOR, position);
|
|
12327
12363
|
}
|
|
12328
|
-
function groupLengthInDataset(position
|
|
12329
|
-
return build2(WARNING_CODES.DICOM_GROUP_LENGTH_IN_DATASET, position
|
|
12364
|
+
function groupLengthInDataset(position) {
|
|
12365
|
+
return build2(WARNING_CODES.DICOM_GROUP_LENGTH_IN_DATASET, position);
|
|
12330
12366
|
}
|
|
12331
|
-
function nonzeroReservedBytes(position
|
|
12332
|
-
return build2(WARNING_CODES.DICOM_NONZERO_RESERVED_BYTES, position
|
|
12333
|
-
n: first,
|
|
12334
|
-
n2: second
|
|
12335
|
-
});
|
|
12367
|
+
function nonzeroReservedBytes(position) {
|
|
12368
|
+
return build2(WARNING_CODES.DICOM_NONZERO_RESERVED_BYTES, position);
|
|
12336
12369
|
}
|
|
12337
12370
|
function unParsedAsSQ(position, tag) {
|
|
12338
12371
|
return build2(WARNING_CODES.DICOM_UN_PARSED_AS_SQ, position, { tag });
|
|
@@ -12340,11 +12373,8 @@ function unParsedAsSQ(position, tag) {
|
|
|
12340
12373
|
function sqNotDescended(position, tag) {
|
|
12341
12374
|
return build2(WARNING_CODES.DICOM_SQ_NOT_DESCENDED, position, { tag });
|
|
12342
12375
|
}
|
|
12343
|
-
function itemCrossesSequenceEnd(position, tag
|
|
12344
|
-
return build2(WARNING_CODES.DICOM_ITEM_CROSSES_SEQUENCE_END, position, {
|
|
12345
|
-
tag,
|
|
12346
|
-
n2: availableLength
|
|
12347
|
-
});
|
|
12376
|
+
function itemCrossesSequenceEnd(position, tag) {
|
|
12377
|
+
return build2(WARNING_CODES.DICOM_ITEM_CROSSES_SEQUENCE_END, position, { tag });
|
|
12348
12378
|
}
|
|
12349
12379
|
function emptyItemInSequence(position, tag) {
|
|
12350
12380
|
return build2(WARNING_CODES.DICOM_EMPTY_ITEM_IN_SEQUENCE, position, { tag });
|
|
@@ -12380,15 +12410,11 @@ function embeddedAttributeRemoved(position, tag, vr, count) {
|
|
|
12380
12410
|
n: count
|
|
12381
12411
|
});
|
|
12382
12412
|
}
|
|
12383
|
-
function sequenceNotAuditable(position, tag
|
|
12384
|
-
return build2(WARNING_CODES.DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE, position, {
|
|
12385
|
-
tag,
|
|
12386
|
-
n: byteLength
|
|
12387
|
-
});
|
|
12413
|
+
function sequenceNotAuditable(position, tag) {
|
|
12414
|
+
return build2(WARNING_CODES.DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE, position, { tag });
|
|
12388
12415
|
}
|
|
12389
|
-
function undefinedVrNotAuditable(position
|
|
12416
|
+
function undefinedVrNotAuditable(position) {
|
|
12390
12417
|
return build2(WARNING_CODES.DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE, position, {
|
|
12391
|
-
n: byteLength,
|
|
12392
12418
|
n2: position.byteOffset
|
|
12393
12419
|
});
|
|
12394
12420
|
}
|
|
@@ -12559,7 +12585,7 @@ function readExplicitElementHeader(cursor, _ctx, emit) {
|
|
|
12559
12585
|
const reserved1 = cursor.buffer[cursor.position + 1];
|
|
12560
12586
|
cursor.position += 2;
|
|
12561
12587
|
if ((reserved0 ?? 0) !== 0 || (reserved1 ?? 0) !== 0) {
|
|
12562
|
-
emit(nonzeroReservedBytes({ byteOffset: headerStart }
|
|
12588
|
+
emit(nonzeroReservedBytes({ byteOffset: headerStart }));
|
|
12563
12589
|
}
|
|
12564
12590
|
length = cursor.readUInt32();
|
|
12565
12591
|
headerLength = 12;
|
|
@@ -12599,7 +12625,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
|
|
|
12599
12625
|
if (firstElement.tag === "00020000" && firstElement.vr === "UL" && firstElement.value.length === 4) {
|
|
12600
12626
|
declaredFmLength = firstElement.value.readUInt32LE(0);
|
|
12601
12627
|
if (cursor.position + declaredFmLength > buffer.length) {
|
|
12602
|
-
throw fileMetaGroupLengthOverruns(buffer, fmStart
|
|
12628
|
+
throw fileMetaGroupLengthOverruns(buffer, fmStart);
|
|
12603
12629
|
}
|
|
12604
12630
|
} else {
|
|
12605
12631
|
emit(fileMetaGroupLengthMissing({ byteOffset: fmStart, fileMeta: true }));
|
|
@@ -13238,13 +13264,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
|
|
|
13238
13264
|
cursor.position = inner.endOffset;
|
|
13239
13265
|
} else {
|
|
13240
13266
|
if (opts.explicitLength !== void 0 && endLimit < buffer.length && cursor.position + itemLength > endLimit) {
|
|
13241
|
-
emit(
|
|
13242
|
-
itemCrossesSequenceEnd(
|
|
13243
|
-
{ byteOffset: itemHeaderStart },
|
|
13244
|
-
itemTag,
|
|
13245
|
-
Math.max(0, endLimit - cursor.position)
|
|
13246
|
-
)
|
|
13247
|
-
);
|
|
13267
|
+
emit(itemCrossesSequenceEnd({ byteOffset: itemHeaderStart }, itemTag));
|
|
13248
13268
|
}
|
|
13249
13269
|
if (cursor.position + itemLength > buffer.length) {
|
|
13250
13270
|
throw itemLengthExceedsBuffer(buffer, itemHeaderStart);
|
|
@@ -13442,7 +13462,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13442
13462
|
throw undefinedLengthOnNonSqImplicit(buffer, headerStart, vr);
|
|
13443
13463
|
}
|
|
13444
13464
|
if (cursor.position + length > buffer.length) {
|
|
13445
|
-
throw elementLengthExceedsBuffer(buffer, headerStart
|
|
13465
|
+
throw elementLengthExceedsBuffer(buffer, headerStart);
|
|
13446
13466
|
}
|
|
13447
13467
|
const valueStart = cursor.position;
|
|
13448
13468
|
const valueEnd = valueStart + length;
|
|
@@ -13479,7 +13499,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
|
|
|
13479
13499
|
}
|
|
13480
13500
|
}
|
|
13481
13501
|
if (element === 0 && group !== 2) {
|
|
13482
|
-
emit(groupLengthInDataset(position
|
|
13502
|
+
emit(groupLengthInDataset(position));
|
|
13483
13503
|
}
|
|
13484
13504
|
if (group % 2 === 1 && element >= 16 && element <= 255) {
|
|
13485
13505
|
registerPrivateCreator(tag, valueSlice, ctx);
|
|
@@ -13562,7 +13582,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13562
13582
|
}
|
|
13563
13583
|
}
|
|
13564
13584
|
if (length !== UNDEFINED_LENGTH2 && length % 2 === 1) {
|
|
13565
|
-
emit(oddLengthValuePadded(position, tag
|
|
13585
|
+
emit(oddLengthValuePadded(position, tag));
|
|
13566
13586
|
}
|
|
13567
13587
|
if (vr === "SQ") {
|
|
13568
13588
|
const valueStart2 = cursor.position;
|
|
@@ -13689,7 +13709,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13689
13709
|
throw undefinedLengthOnNonSqExplicit(buffer, headerStart, vr);
|
|
13690
13710
|
}
|
|
13691
13711
|
if (cursor.position + length > buffer.length) {
|
|
13692
|
-
throw elementLengthExceedsBuffer(buffer, headerStart
|
|
13712
|
+
throw elementLengthExceedsBuffer(buffer, headerStart);
|
|
13693
13713
|
}
|
|
13694
13714
|
const valueStart = cursor.position;
|
|
13695
13715
|
const valueEnd = valueStart + length;
|
|
@@ -13697,7 +13717,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
|
|
|
13697
13717
|
cursor.position = valueEnd;
|
|
13698
13718
|
const elementHexNum = parseInt(tag.slice(4, 8), 16);
|
|
13699
13719
|
if (elementHexNum === 0 && groupNum !== 2) {
|
|
13700
|
-
emit(groupLengthInDataset(position
|
|
13720
|
+
emit(groupLengthInDataset(position));
|
|
13701
13721
|
}
|
|
13702
13722
|
if (groupNum % 2 === 1 && elementHexNum >= 16 && elementHexNum <= 255) {
|
|
13703
13723
|
registerPrivateCreator(tag, valueSlice, ctx);
|
|
@@ -15497,7 +15517,7 @@ function emptyUndefinedVrElement(el, ctx, contextPath, out) {
|
|
|
15497
15517
|
byteLength: el.rawBytes.length,
|
|
15498
15518
|
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15499
15519
|
});
|
|
15500
|
-
out.warnings.push(undefinedVrNotAuditable({ byteOffset: el.byteOffset }
|
|
15520
|
+
out.warnings.push(undefinedVrNotAuditable({ byteOffset: el.byteOffset }));
|
|
15501
15521
|
}
|
|
15502
15522
|
function isUnauditableSequence(el) {
|
|
15503
15523
|
return el.vr === "SQ" && el.items === void 0;
|
|
@@ -15514,9 +15534,7 @@ function emptyUnauditableCarrier(el, ctx, contextPath, out, replacement) {
|
|
|
15514
15534
|
byteLength: el.rawBytes.length,
|
|
15515
15535
|
...contextPath.length > 0 ? { contextPath: [...contextPath] } : {}
|
|
15516
15536
|
});
|
|
15517
|
-
out.warnings.push(
|
|
15518
|
-
sequenceNotAuditable({ byteOffset: el.byteOffset }, el.tag, el.rawBytes.length)
|
|
15519
|
-
);
|
|
15537
|
+
out.warnings.push(sequenceNotAuditable({ byteOffset: el.byteOffset }, el.tag));
|
|
15520
15538
|
}
|
|
15521
15539
|
function creatorsInScope(source) {
|
|
15522
15540
|
const byBlock = /* @__PURE__ */ new Map();
|