Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
3d8f679
chore refactored proper location of dictionaries and removed redundan…
Aug 19, 2025
b2fa72d
chore refactored the logic for switching to target decoder such that …
Aug 19, 2025
4d55daf
chore Forgot to pass the ignoreErrors flag.
Aug 19, 2025
6b70b60
chore Moved the decoder logic to the base BufferStream class since it…
Aug 19, 2025
5596de1
chore Refactored core encoding decoding logic to be in its own class …
Aug 19, 2025
50327d2
chore Added docstrings to new class to clarify usage and expectations.
Aug 19, 2025
8164601
Merge branch 'master' into 454_incorrect_decoding_of_iso_ir_100
luissantosHCIT Mar 6, 2026
89b7498
chore moving constants into their own files.
luissantosHCIT Mar 6, 2026
b4e15c7
chore removing duplicated constant => encodingMapping
luissantosHCIT Mar 6, 2026
da8812e
chore cleaned up usage of constants and adding new constant file impo…
luissantosHCIT Mar 6, 2026
c91b13a
chore cleaned up class exports.
luissantosHCIT Mar 6, 2026
f2e2b17
chore imported the encodingMapping constant and removed logic directl…
luissantosHCIT Mar 6, 2026
6ff636f
chore refactored the encoding selection logic for clarity and to make…
luissantosHCIT Mar 6, 2026
3bce05a
chore moved the DicomBufferCODEC into its own file and made the encod…
luissantosHCIT Mar 6, 2026
0c60c55
chore converting structs into Maps and Sets.
luissantosHCIT Mar 6, 2026
daa7276
chore changing vars into const and lets for clarity.
luissantosHCIT Mar 6, 2026
d1f03b5
chore moved the log.js source to utilities.
luissantosHCIT Mar 6, 2026
46ec4bf
chore moved a few constants to constants/dicom, switched a few checks…
luissantosHCIT Mar 6, 2026
d1383aa
chore moved selectEncoding to utilities
luissantosHCIT Mar 6, 2026
16ed19c
chore changed vars to consts and lets.
luissantosHCIT Mar 6, 2026
011cdd0
chore checked for key in constant encodingMapping.
luissantosHCIT Mar 6, 2026
591806b
chore changed the ES version to 11 in JSHints to kill false positive …
luissantosHCIT Mar 6, 2026
067783a
fix bug introduced by accidentally setting variables to const.
luissantosHCIT Mar 6, 2026
1ae8d7a
fix added parameter guard check in selectEncoding
luissantosHCIT Mar 6, 2026
46e8d4a
fix added missing has check.
luissantosHCIT Mar 6, 2026
e25344f
fix restored removed new keyword when I was cleaning the project. Oops.
luissantosHCIT Mar 6, 2026
5654097
fix reverted some of the linter suggested changes to const and equali…
luissantosHCIT Mar 6, 2026
39e2fb4
fix reverted some of the linter suggested changes to const and equali…
luissantosHCIT Mar 6, 2026
5f3c70e
chore moved out encoding function into selectEncoding source.
luissantosHCIT Mar 6, 2026
8442409
fix(BufferStream) corrected issues with encoding due to changing stru…
luissantosHCIT Mar 7, 2026
134b3c9
chore(selectEncoding) added note.
luissantosHCIT Mar 7, 2026
c0012d4
fix(DicomMetaDictionary) generation of struct sopClassUIDsByName prop…
luissantosHCIT Mar 7, 2026
690ece8
chore(DicomMetaDictionary) added addEncoding method to allow external…
luissantosHCIT Mar 9, 2026
db96bf6
chore(BufferStream) consolidated the encoding methods into a singular…
luissantosHCIT Mar 9, 2026
487fa4a
chore(Tag) pass through encoding option upon initialization of a writ…
luissantosHCIT Mar 9, 2026
fc7a36e
chore(DicomDict) pass through encoding option upon initialization of …
luissantosHCIT Mar 9, 2026
b565467
fix(BufferStream) missing default endianness for ReadBufferStream.
luissantosHCIT Mar 9, 2026
0e501c2
chore(Tag) const correctness
luissantosHCIT Mar 10, 2026
e09fb64
fix(DicomMessage) calling wrong encoding method (was renamed today in…
luissantosHCIT Mar 10, 2026
b0c8c73
chore(ValueRepresentation) moving common formatting/value filtering l…
luissantosHCIT Mar 10, 2026
f30cc2f
fix(ValueRepresentation) reverted initial changes to ValueRepresentat…
luissantosHCIT Mar 10, 2026
1843eca
fix(tests) renamed the encoding method to the new one.
luissantosHCIT Mar 10, 2026
b140d6a
feature(tests) added basic tests tracking encoded write/reads of Dico…
luissantosHCIT Mar 10, 2026
7f8f808
chore(encodings) renamed latin1 to iso-8859-1 for consistency.
luissantosHCIT Mar 12, 2026
942620c
chore(encodings) added the other two Ox VRs in AsyncDicomReader.
luissantosHCIT Mar 12, 2026
c1a87ac
chore(encodings) renamed DicomBufferCODEC to DicomTextTranscode for a…
luissantosHCIT Mar 12, 2026
b0e8b67
chore(encodings) renamed codec to textTranscoder for consistency with…
luissantosHCIT Mar 12, 2026
4ff3ca4
chore(encodings) forced littleEndian and simplified constructor of de…
luissantosHCIT Mar 13, 2026
f4e7e8e
chore(encodings) corrected the fact that the DicomDataReadBufferStrea…
luissantosHCIT Mar 13, 2026
3d59ff3
chore(encodings) refactored the constructor of WriteStreamBuffer to a…
luissantosHCIT Mar 13, 2026
49d957c
chore(encodings) refactored the tests making use of WriteStreamBuffer…
luissantosHCIT Mar 13, 2026
a1bfa85
chore(encodings) removed options improperly added during the original…
luissantosHCIT Mar 13, 2026
baab404
docs(BufferStream) Added documentation notes on the constructor chang…
luissantosHCIT Mar 15, 2026
608fd52
chore removed unused imports
luissantosHCIT Mar 17, 2026
2e07a30
chore corrected function doc string
luissantosHCIT Mar 17, 2026
df6eabd
chore(sopClassUIDs) moved the name-uid set into the constants source …
luissantosHCIT Mar 18, 2026
133ddf6
chore(sopClassUIDs) Removed the generation of sopClassUIDsByName and …
luissantosHCIT Mar 18, 2026
5e1913a
chore(style) fixed code style with yarn prettier
luissantosHCIT Mar 18, 2026
f44ed90
Merge branch 'master' into 454_incorrect_decoding_of_iso_ir_100
luissantosHCIT May 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"esversion": 6
"esversion": 11
}
55 changes: 55 additions & 0 deletions docs/BreakingChanges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Migration Guide for Breaking Changes

## v 0.50.1 to higher

After version 0.50.1, we introduced a breaking change to `WriteBufferStream` and `ReadBufferStream`.

### What Changed

- `ReadBufferStream`: Constructor signature changed from
```js
constructor(
buffer,
littleEndian = false,
options = {
start: null,
stop: null,
noCopy: false
},
encoding = defaultDICOMEncoding
)
```
to
```js
constructor(
buffer,
options = {
start: null,
stop: null,
encoding: defaultDICOMEncoding,
noCopy: false,
littleEndian: true
}
)
```

- `WriteBufferStream`: Constructor signature changed from
```js
constructor(defaultSize, options = null)
```
to
```js
constructor(options = null)
```

### Notes

Essentially, the options that used to be separate parameters were moved into the flexible `options` object.

For reading situations, the `littleEndian` and `encoding` options are now in that options object. Moving forward, any
new parameters will be introduced as options here.

For writing situations, the `defaultSize` parameter was moved into the options object.

In all situations, the legacy usage of the constructor will fire a warning reminding you of the change to prompt you to
update your usage of the library.
24 changes: 15 additions & 9 deletions src/AsyncDicomReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import {
VM_DELIMITER,
UNDEFINED_LENGTH,
TagHex,
encodingMapping,
UNDEFINED_LENGTH_FIX,
VALID_VRS,
singleVRs,
isVideoTransferSyntax
} from "./constants/dicom";
import { encodingMapping } from "./constants/encodings";
import { Tag } from "./Tag";
import { DicomMessage, singleVRs } from "./DicomMessage";
import { DicomMessage } from "./DicomMessage";
import { DicomMetaDictionary } from "./DicomMetaDictionary";
import { DicomMetadataListener } from "./utilities/DicomMetadataListener.js";
import { log } from "./log.js";
import { log } from "./utilities/log.js";

const readLog = log.getLogger("AsyncDicomReader");

Expand All @@ -35,7 +36,8 @@ export class AsyncDicomReader {
this.isLittleEndian = options?.isLittleEndian;
// Default maxFragmentSize is 128 MB (128 * 1024 * 1024 bytes)
this.maxFragmentSize = options?.maxFragmentSize ?? 128 * 1024 * 1024;
this.stream = new ReadBufferStream(null, this.isLittleEndian, {
this.stream = new ReadBufferStream(null, {
littleEncoding: this.isLittleEndian,
clearBuffers: true,
...options
});
Expand Down Expand Up @@ -720,13 +722,18 @@ export class AsyncDicomReader {
}
} else {
const value = vr.read(stream, length, syntax)?.value;
if (!vr.isBinary() && singleVRs.indexOf(vr.type) == -1) {
if (!vr.isBinary() && !singleVRs.has(vr.type)) {
values = value;
if (typeof value === "string") {
const delimiterChar = String.fromCharCode(VM_DELIMITER);
values = vr.dropPadByte(value.split(delimiterChar));
}
} else if (vr.type == "OW" || vr.type == "OB") {
} else if (
vr.type === "OW" ||
vr.type === "OB" ||
vr.type === "OD" ||
vr.type === "OF"
) {
values = value;
} else {
Array.isArray(value) ? (values = value) : values.push(value);
Expand All @@ -737,9 +744,8 @@ export class AsyncDicomReader {
if (values.length > 0) {
let [coding] = values;
coding = coding.replace(/[_ ]/g, "-").toLowerCase();
if (coding in encodingMapping) {
coding = encodingMapping[coding];
this.stream.setDecoder(new TextDecoder(coding));
if (encodingMapping.has(coding)) {
this.stream.setEncoding(coding);
} else if (options?.ignoreErrors) {
log.warn(
`Unsupported character set: ${coding}, using default character set`
Expand Down
94 changes: 71 additions & 23 deletions src/BufferStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import pako from "pako";
import SplitDataView from "./SplitDataView";
import { toFloat } from "./utilities/toFloat";
import { toInt } from "./utilities/toInt";
import { DicomTextTranscode } from "./DicomTextTranscode";
import { defaultDICOMEncoding } from "./constants/encodings";
import { log } from "./utilities/log";

export class BufferStream {
offset = 0;
startOffset = 0;
isLittleEndian = false;
isLittleEndian = true;
size = 0;
view = new SplitDataView();
/** The available listeners are those waiting for a query response */
Expand All @@ -18,12 +21,14 @@ export class BufferStream {
/** A flag to set to indicate to clear buffers as they get consumed */
clearBuffers = false;

encoder = new TextEncoder("utf-8");
textTranscoder = new DicomTextTranscode();

constructor(options = null) {
this.isLittleEndian = options?.littleEndian || this.isLittleEndian;
this.isLittleEndian = options?.littleEndian ?? this.isLittleEndian;
this.view.defaultSize = options?.defaultSize ?? this.view.defaultSize;
this.clearBuffers = options.clearBuffers || false;
this.clearBuffers = options?.clearBuffers || false;

this.setEncoding(options?.encoding || defaultDICOMEncoding);
}

/**
Expand Down Expand Up @@ -68,8 +73,20 @@ export class BufferStream {
return true;
}

setEndian(isLittle) {
this.isLittleEndian = isLittle;
setEncoding(dicomEncoding, ignoreErrors) {
this.textTranscoder.setEncoding(dicomEncoding, ignoreErrors);
}

setEndian(isLittleEndian = true) {
this.isLittleEndian = isLittleEndian;
}

setLittleEndian() {
this.isLittleEndian = true;
}

setBigEndian() {
this.isLittleEndian = false;
}

slice(start = this.startOffset, end = this.endOffset) {
Expand Down Expand Up @@ -175,7 +192,7 @@ export class BufferStream {
}

writeUTF8String(value) {
const encodedString = this.encoder.encode(value);
const encodedString = this.textTranscoder.encode(value);
this.checkSize(encodedString.byteLength);
this.view.writeBuffer(encodedString, this.offset);
return this.increment(encodedString.byteLength);
Expand Down Expand Up @@ -312,7 +329,7 @@ export class BufferStream {
const view = new DataView(
this.slice(this.offset, this.offset + length)
);
const result = this.decoder.decode(view);
const result = this.textTranscoder.decode(view);
this.increment(length);
return result;
}
Expand Down Expand Up @@ -465,35 +482,49 @@ export class BufferStream {
export class ReadBufferStream extends BufferStream {
constructor(
buffer,
littleEndian,
options = {
start: null,
stop: null,
noCopy: false
encoding: defaultDICOMEncoding,
noCopy: false,
littleEndian: true
}
) {
super({ ...options, littleEndian });
this.noCopy = options.noCopy;
this.decoder = new TextDecoder("latin1");
options instanceof Object
? {}
: log.warn(
"The constructor API for ReadBufferStream has changed to include the" +
" littleEndian option as part of the options object. Please, update your usage of the class. We are " +
"using defaults now. See docs/BreakingChanges.md for more details."
);
const optionsOptions =
options instanceof Object
? options
: {
start: null,
stop: null,
encoding: defaultDICOMEncoding,
noCopy: false,
littleEndian: options
};
super(optionsOptions);
this.noCopy = optionsOptions.noCopy;

if (buffer instanceof BufferStream) {
this.view.from(buffer.view, options);
this.view.from(buffer.view, optionsOptions);
this.isComplete = true;
} else if (buffer) {
this.view.addBuffer(buffer);
this.isComplete = true;
}
this.offset = options.start ?? buffer?.offset ?? 0;
this.size = options.stop || buffer?.size || buffer?.byteLength || 0;
this.offset = optionsOptions.start ?? buffer?.offset ?? 0;
this.size =
optionsOptions.stop || buffer?.size || buffer?.byteLength || 0;

this.startOffset = this.offset;
this.endOffset = this.size;
}

setDecoder(decoder) {
this.decoder = decoder;
}

reset() {
this.offset = this.startOffset;
return this;
Expand Down Expand Up @@ -571,13 +602,30 @@ export class DeflatedReadBufferStream extends ReadBufferStream {
const inflatedBuffer = pako.inflateRaw(
stream.getBuffer(stream.offset, stream.size)
);
super(inflatedBuffer.buffer, stream.littleEndian, options);
super(inflatedBuffer.buffer, {
littleEndian: stream.littleEndian,
...options
});
}
}

export class WriteBufferStream extends BufferStream {
constructor(defaultSize, littleEndian) {
super({ defaultSize, littleEndian });
constructor(options = null) {
options instanceof Object
? {}
: log.warn(
"The constructor API for WriteBufferStream has changed to include the" +
" littleEndian option as part of the options object. Please, update your usage of the class. We are " +
"using defaults now. See docs/BreakingChanges.md for more details."
);
const optionsOptions =
options instanceof Object
? options
: {
encoding: defaultDICOMEncoding,
littleEndian: options
};
super(optionsOptions);
this.size = 0;
}
}
21 changes: 16 additions & 5 deletions src/DicomDict.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,24 @@ class DicomDict {
}
}

write(writeOptions = { allowInvalidVRLength: false }) {
var metaSyntax = EXPLICIT_LITTLE_ENDIAN;
var fileStream = new WriteBufferStream(4096, true);
write(
Comment thread
luissantosHCIT marked this conversation as resolved.
writeOptions = {
allowInvalidVRLength: false
}
) {
const metaSyntax = EXPLICIT_LITTLE_ENDIAN;
const fileStream = new WriteBufferStream({
defaultSize: 4096,
littleEndian: writeOptions.littleEndian
});

fileStream.writeUint8Repeat(0, 128);
fileStream.writeAsciiString("DICM");

var metaStream = new WriteBufferStream(1024);
const metaStream = new WriteBufferStream({
defaultSize: 1024,
littleEndian: writeOptions.littleEndian
});
if (!this.meta[TagHex.TransferSyntaxUID]) {
this.meta[TagHex.TransferSyntaxUID] = {
vr: "UI",
Expand All @@ -46,7 +57,7 @@ class DicomDict {
);
fileStream.concat(metaStream);

var useSyntax = this.meta[TagHex.TransferSyntaxUID].Value[0];
const useSyntax = this.meta[TagHex.TransferSyntaxUID].Value[0];
DicomMessage.write(this.dict, fileStream, useSyntax, writeOptions);
return fileStream.getBuffer();
}
Expand Down
Loading
Loading