dcmjs 0.49.3 → 0.50.0
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/README.md +50 -0
- package/build/dcmjs.es.js +1071 -112
- package/build/dcmjs.es.js.map +1 -1
- package/build/dcmjs.js +1071 -112
- package/build/dcmjs.js.map +1 -1
- package/build/dcmjs.min.js +2 -2
- package/build/dcmjs.min.js.map +1 -1
- package/generate/dictionary.mjs +56029 -0
- package/package.json +18 -2
- package/.babelrc +0 -9
- package/.github/workflows/lint-and-format.yml +0 -27
- package/.github/workflows/publish-package.yml +0 -45
- package/.github/workflows/tests.yml +0 -24
- package/.prettierrc +0 -5
- package/.vscode/extensions.json +0 -7
- package/.vscode/settings.json +0 -8
- package/changelog.md +0 -31
- package/docs/ArrayBufferExpanderListener.md +0 -303
- package/docs/AsyncDicomReader-skill.md +0 -730
- package/eslint.config.mjs +0 -30
- package/generate-dictionary.js +0 -145
- package/jest.setup.js +0 -39
- package/netlify.toml +0 -22
- package/rollup.config.mjs +0 -57
- package/test/ArrayBufferExpanderListener.test.js +0 -365
- package/test/DICOMWEB.test.js +0 -1
- package/test/DicomMetaDictionary.test.js +0 -73
- package/test/SequenceOfItems.test.js +0 -86
- package/test/adapters.test.js +0 -43
- package/test/anonymizer.test.js +0 -176
- package/test/arrayItem.json +0 -351
- package/test/async-data.test.js +0 -575
- package/test/data-encoding.test.js +0 -59
- package/test/data-options.test.js +0 -199
- package/test/data.test.js +0 -1776
- package/test/derivations.test.js +0 -1
- package/test/helper/DicomDataReadBufferStreamBuilder.js +0 -89
- package/test/information-filter.test.js +0 -165
- package/test/integration/DicomMessage.readFile.test.js +0 -50
- package/test/lossless-read-write.test.js +0 -1407
- package/test/mocks/minimal_fields_dataset.json +0 -17
- package/test/mocks/null_number_vrs_dataset.json +0 -102
- package/test/normalizers.test.js +0 -38
- package/test/odd-frame-bit-data.js +0 -138
- package/test/rawTags.js +0 -170
- package/test/readBufferStream.test.js +0 -158
- package/test/sample-dicom.json +0 -904
- package/test/sample-op.lei +0 -0
- package/test/sample-sr.json +0 -997
- package/test/sr-tid.test.js +0 -251
- package/test/testUtils.js +0 -85
- package/test/utilities/deepEqual.test.js +0 -87
- package/test/utilities.test.js +0 -205
- package/test/video-test-dict.js +0 -40
- package/test/writeBufferStream.test.js +0 -149
package/package.json
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dcmjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Javascript implementation of DICOM manipulation",
|
|
5
5
|
"main": "build/dcmjs.js",
|
|
6
6
|
"module": "build/dcmjs.es.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./build/dcmjs.es.js",
|
|
10
|
+
"require": "./build/dcmjs.js"
|
|
11
|
+
},
|
|
12
|
+
"./dictionary": "./generate/dictionary.mjs"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"build",
|
|
16
|
+
"generate/dictionary.mjs",
|
|
17
|
+
"README.md",
|
|
18
|
+
"License.txt"
|
|
19
|
+
],
|
|
7
20
|
"directories": {
|
|
8
21
|
"example": "examples"
|
|
9
22
|
},
|
|
@@ -15,7 +28,10 @@
|
|
|
15
28
|
"dev": "rollup -c -w",
|
|
16
29
|
"format": "prettier --write 'src/**/*.js' 'test/**/*.js'",
|
|
17
30
|
"format:check": "prettier --check 'src/**/*.js' 'test/**/*.js'",
|
|
18
|
-
"lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'"
|
|
31
|
+
"lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'",
|
|
32
|
+
"generate-dictionary": "node generate/generate-dictionary.js",
|
|
33
|
+
"pack-dictionary": "node generate/pack_dicom.mjs",
|
|
34
|
+
"bench:dictionary": "bun run generate/bench-dictionary-load.mjs"
|
|
19
35
|
},
|
|
20
36
|
"repository": {
|
|
21
37
|
"type": "git",
|
package/.babelrc
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
name: Run lint and format check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
- push
|
|
5
|
-
- pull_request
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
lint-and-format:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
|
-
steps:
|
|
12
|
-
- name: Checkout repository
|
|
13
|
-
uses: actions/checkout@v4
|
|
14
|
-
|
|
15
|
-
- name: Setup Node.js
|
|
16
|
-
uses: actions/setup-node@v4
|
|
17
|
-
with:
|
|
18
|
-
node-version: 18
|
|
19
|
-
|
|
20
|
-
- name: Install packages
|
|
21
|
-
run: yarn install --frozen-lockfile
|
|
22
|
-
|
|
23
|
-
- name: Run ESLint
|
|
24
|
-
run: yarn lint
|
|
25
|
-
|
|
26
|
-
- name: Check Prettier formatting
|
|
27
|
-
run: yarn format:check
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
name: Publish package to NPM
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
issues: write
|
|
11
|
-
pull-requests: write
|
|
12
|
-
id-token: write
|
|
13
|
-
|
|
14
|
-
jobs:
|
|
15
|
-
publish-package:
|
|
16
|
-
runs-on: ubuntu-latest
|
|
17
|
-
environment: publish
|
|
18
|
-
|
|
19
|
-
steps:
|
|
20
|
-
- name: Checkout repository
|
|
21
|
-
uses: actions/checkout@v4
|
|
22
|
-
with:
|
|
23
|
-
persist-credentials: false
|
|
24
|
-
|
|
25
|
-
- name: Setup Node.js
|
|
26
|
-
uses: actions/setup-node@v4
|
|
27
|
-
with:
|
|
28
|
-
node-version: 24
|
|
29
|
-
registry-url: 'https://registry.npmjs.org'
|
|
30
|
-
|
|
31
|
-
- name: Install packages
|
|
32
|
-
uses: bahmutov/npm-install@v1
|
|
33
|
-
|
|
34
|
-
- name: Run tests
|
|
35
|
-
run: npm run test
|
|
36
|
-
|
|
37
|
-
- name: Run build
|
|
38
|
-
run: npm run build
|
|
39
|
-
|
|
40
|
-
- name: Semantic release
|
|
41
|
-
uses: cycjimmy/semantic-release-action@v6
|
|
42
|
-
env:
|
|
43
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
45
|
-
NPM_CONFIG_PROVENANCE: true
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: Run tests
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
- push
|
|
5
|
-
- pull_request
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
test:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
|
-
steps:
|
|
12
|
-
- name: Checkout repository
|
|
13
|
-
uses: actions/checkout@v4
|
|
14
|
-
|
|
15
|
-
- name: Setup Node.js
|
|
16
|
-
uses: actions/setup-node@v4
|
|
17
|
-
with:
|
|
18
|
-
node-version: 18
|
|
19
|
-
|
|
20
|
-
- name: Install packages
|
|
21
|
-
run: yarn install --frozen-lockfile
|
|
22
|
-
|
|
23
|
-
- name: Run tests
|
|
24
|
-
run: yarn test
|
package/.prettierrc
DELETED
package/.vscode/extensions.json
DELETED
package/.vscode/settings.json
DELETED
package/changelog.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
All notable changes to this project will be documented in this file.
|
|
3
|
-
|
|
4
|
-
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
|
-
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
|
-
|
|
7
|
-
## 2026-01-19
|
|
8
|
-
Added multiple measurements for a single annotation in an SR object
|
|
9
|
-
|
|
10
|
-
## [0.2.1] - 2018-10-17
|
|
11
|
-
### Added
|
|
12
|
-
- Added Adapters and Utilities to support translation between common imaging toolkits (Cornerstone, VTK.js) and DICOM Structured Reports. Utilities are tied to the DICOM Standard and help build compliant files. Adapters are specific to the toolkits in question and help make it easier for developers to use the Utilities.
|
|
13
|
-
|
|
14
|
-
Note: These are generally still a work in progress. We are currently only confident in the Cornerstone Length adapter, and the Utilities (TID1500, TID1501, TID300, Length) which back it.
|
|
15
|
-
|
|
16
|
-
## [0.2.0] - 2018-10-02
|
|
17
|
-
### Added
|
|
18
|
-
- Example using [VTK.js with DICOM Segmentation](https://dcmjs-org.github.io/dcmjs/examples/vtkDisplay/index.html)
|
|
19
|
-
|
|
20
|
-
### Changed
|
|
21
|
-
- BitArray class provides static methods
|
|
22
|
-
to pack and unpack bit and bytes to support
|
|
23
|
-
dicom SEG encoding.
|
|
24
|
-
|
|
25
|
-
## [0.1.5] - 2018-08-23
|
|
26
|
-
### Fixed
|
|
27
|
-
- Fixed dcmjs compatibility with IE11
|
|
28
|
-
|
|
29
|
-
## [0.1.4] - 2018-08-23
|
|
30
|
-
### Added
|
|
31
|
-
- Added Webpack and babel to replace Rollup
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
# ArrayBufferExpanderFilter
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The `ArrayBufferExpanderFilter` is a filter for `DicomMetadataListener` that converts `ArrayBuffer[]` child values into expanded `listener.startObject([])` and `value(fragment)` calls. This is particularly useful when working with compressed or fragmented pixel data that may be delivered as an array of ArrayBuffers.
|
|
6
|
-
|
|
7
|
-
## Purpose
|
|
8
|
-
|
|
9
|
-
When the `AsyncDicomReader` reads compressed pixel data, it can deliver frame data in two formats:
|
|
10
|
-
|
|
11
|
-
1. **Compact format**: A single `ArrayBuffer[]` value passed to `listener.value()`
|
|
12
|
-
2. **Expanded format**: A sequence of calls:
|
|
13
|
-
- `listener.startObject([])`
|
|
14
|
-
- Multiple `listener.value(fragment)` calls (one per ArrayBuffer)
|
|
15
|
-
- `listener.pop()`
|
|
16
|
-
|
|
17
|
-
The `ArrayBufferExpanderFilter` automatically converts format #1 into format #2, ensuring consistent handling regardless of how the reader delivers the data.
|
|
18
|
-
|
|
19
|
-
**Important**:
|
|
20
|
-
- Each frame is ALWAYS delivered as an array (ArrayBuffer[]), even for single frames
|
|
21
|
-
- Video transfer syntaxes are handled as though they were a single frame
|
|
22
|
-
- Binary data can be delivered fragmented - a single frame may be split across multiple ArrayBuffer fragments
|
|
23
|
-
- Multiple fragments are combined into one array per frame
|
|
24
|
-
- When the `AsyncDicomReader` delivers `ArrayBuffer[]` directly to `value()`, it does NOT call `startObject([])`/`pop()` around them. The expander filter makes those calls to properly represent the array structure, then captures the result and assigns it to the tag's `Value` field.
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
### Basic Usage
|
|
29
|
-
|
|
30
|
-
```javascript
|
|
31
|
-
import { AsyncDicomReader } from 'dcmjs';
|
|
32
|
-
import { DicomMetadataListener, ArrayBufferExpanderFilter } from 'dcmjs/utilities';
|
|
33
|
-
|
|
34
|
-
async function readDicomWithExpansion(arrayBuffer) {
|
|
35
|
-
const reader = new AsyncDicomReader();
|
|
36
|
-
reader.stream.setData(arrayBuffer);
|
|
37
|
-
|
|
38
|
-
// Create listener with the expander filter
|
|
39
|
-
const listener = new DicomMetadataListener(ArrayBufferExpanderFilter);
|
|
40
|
-
|
|
41
|
-
// Read the file using the listener
|
|
42
|
-
await reader.readFile({ listener });
|
|
43
|
-
|
|
44
|
-
// Access the result
|
|
45
|
-
return listener.dict;
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Combining with Custom Filters
|
|
50
|
-
|
|
51
|
-
You can combine the expander with custom filters to process fragments individually:
|
|
52
|
-
|
|
53
|
-
```javascript
|
|
54
|
-
// Create a custom filter to track fragments
|
|
55
|
-
const fragmentTrackingFilter = {
|
|
56
|
-
fragmentCount: 0,
|
|
57
|
-
fragmentSizes: [],
|
|
58
|
-
|
|
59
|
-
value(next, val) {
|
|
60
|
-
if (val instanceof ArrayBuffer || ArrayBuffer.isView(val)) {
|
|
61
|
-
this.fragmentCount++;
|
|
62
|
-
this.fragmentSizes.push(val.byteLength);
|
|
63
|
-
console.log(`Processing fragment ${this.fragmentCount}, size: ${val.byteLength}`);
|
|
64
|
-
}
|
|
65
|
-
return next(val);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
async function readWithFragmentTracking(arrayBuffer) {
|
|
70
|
-
const reader = new AsyncDicomReader();
|
|
71
|
-
reader.stream.setData(arrayBuffer);
|
|
72
|
-
|
|
73
|
-
// Create listener with both filters
|
|
74
|
-
// ArrayBufferExpanderFilter runs first to expand, then fragmentTrackingFilter sees individual fragments
|
|
75
|
-
const listener = new DicomMetadataListener(
|
|
76
|
-
ArrayBufferExpanderFilter,
|
|
77
|
-
fragmentTrackingFilter
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
await reader.readFile({ listener });
|
|
81
|
-
|
|
82
|
-
console.log(`Total fragments: ${listener.filters[1].fragmentCount}`);
|
|
83
|
-
console.log(`Fragment sizes:`, listener.filters[1].fragmentSizes);
|
|
84
|
-
|
|
85
|
-
return listener.dict;
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Processing Each Frame Fragment
|
|
90
|
-
|
|
91
|
-
This is useful for streaming or progressive decoding:
|
|
92
|
-
|
|
93
|
-
```javascript
|
|
94
|
-
// Create a streaming filter to process fragments as they arrive
|
|
95
|
-
function createStreamingFilter(onFrameFragment) {
|
|
96
|
-
let inPixelData = false;
|
|
97
|
-
let currentFrameFragments = [];
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
addTag(next, tag, tagInfo) {
|
|
101
|
-
// Track when we're processing pixel data
|
|
102
|
-
inPixelData = tag === '7FE00010'; // Pixel Data tag
|
|
103
|
-
return next(tag, tagInfo);
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
startObject(next, dest) {
|
|
107
|
-
if (inPixelData && Array.isArray(dest)) {
|
|
108
|
-
// Starting a new frame with multiple fragments
|
|
109
|
-
currentFrameFragments = [];
|
|
110
|
-
}
|
|
111
|
-
return next(dest);
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
value(next, val) {
|
|
115
|
-
if (inPixelData && (val instanceof ArrayBuffer || ArrayBuffer.isView(val))) {
|
|
116
|
-
// Process each fragment as it arrives
|
|
117
|
-
onFrameFragment(val);
|
|
118
|
-
currentFrameFragments.push(val);
|
|
119
|
-
}
|
|
120
|
-
return next(val);
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
pop(next) {
|
|
124
|
-
if (inPixelData && currentFrameFragments.length > 0) {
|
|
125
|
-
// Finished processing all fragments for this frame
|
|
126
|
-
console.log(`Completed frame with ${currentFrameFragments.length} fragments`);
|
|
127
|
-
currentFrameFragments = [];
|
|
128
|
-
}
|
|
129
|
-
return next();
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function streamFrameFragments(arrayBuffer) {
|
|
135
|
-
const reader = new AsyncDicomReader();
|
|
136
|
-
reader.stream.setData(arrayBuffer);
|
|
137
|
-
|
|
138
|
-
// Create streaming filter
|
|
139
|
-
const streamingFilter = createStreamingFilter((fragment) => {
|
|
140
|
-
console.log(`Received fragment of ${fragment.byteLength} bytes`);
|
|
141
|
-
// Process fragment immediately (e.g., send to decoder)
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Combine expander with streaming filter
|
|
145
|
-
const listener = new DicomMetadataListener(
|
|
146
|
-
ArrayBufferExpanderFilter,
|
|
147
|
-
streamingFilter
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
await reader.readFile({ listener });
|
|
151
|
-
|
|
152
|
-
return listener.dict;
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## How It Works
|
|
157
|
-
|
|
158
|
-
The `ArrayBufferExpanderFilter` is a filter object that integrates with `DicomMetadataListener`'s filter chain. It provides a `value()` filter method that:
|
|
159
|
-
|
|
160
|
-
1. When `value(next, v)` is called, it checks if the value is an array of ArrayBuffers
|
|
161
|
-
2. If yes:
|
|
162
|
-
- Saves the current tag context (via `this.current`)
|
|
163
|
-
- Calls `this.startObject([])` to create a new array context
|
|
164
|
-
- Calls `this.value(fragment)` for each ArrayBuffer in the array
|
|
165
|
-
- Calls `this.pop()` to get the resulting array structure
|
|
166
|
-
- Assigns that array to the tag's `Value` field
|
|
167
|
-
3. If no, passes the value through to the next filter by calling `next(v)`
|
|
168
|
-
|
|
169
|
-
When used as a filter in `DicomMetadataListener`, `this` refers to the listener instance, giving the filter access to all listener methods and properties.
|
|
170
|
-
|
|
171
|
-
This ensures that any subsequent filters or processing logic always receives individual fragments through the proper `startObject([])/value/pop` sequence, regardless of how the `AsyncDicomReader` delivers them.
|
|
172
|
-
|
|
173
|
-
The resulting data structure will have a `Value` array containing the individual fragments. Note that each frame is always delivered as an array, and binary data can be fragmented across multiple ArrayBuffer chunks.
|
|
174
|
-
|
|
175
|
-
## Detection Logic
|
|
176
|
-
|
|
177
|
-
The listener considers a value to be an `ArrayBuffer[]` if:
|
|
178
|
-
- It is an `Array`
|
|
179
|
-
- All elements are either `ArrayBuffer` instances or typed array views (like `Uint8Array`)
|
|
180
|
-
|
|
181
|
-
This is checked using the `_isArrayBufferArray()` helper method:
|
|
182
|
-
|
|
183
|
-
```javascript
|
|
184
|
-
_isArrayBufferArray(arr) {
|
|
185
|
-
return arr.every(
|
|
186
|
-
item => item instanceof ArrayBuffer || ArrayBuffer.isView(item)
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Use Cases
|
|
192
|
-
|
|
193
|
-
### 1. Fragmented Compressed Frames
|
|
194
|
-
Some compressed formats split single frames into multiple fragments:
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
// Without expander: might receive ArrayBuffer[]
|
|
198
|
-
// With expander: always receives individual ArrayBuffer calls
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### 2. Multiframe Images with Offset Table
|
|
202
|
-
When reading multiframe compressed images with an offset table, child elements may be delivered as arrays.
|
|
203
|
-
|
|
204
|
-
### 3. Video Sequences
|
|
205
|
-
Video frames stored as arrays of fragments can be processed consistently.
|
|
206
|
-
|
|
207
|
-
### 4. Bulkdata Streaming
|
|
208
|
-
When implementing custom bulkdata storage, individual fragments can be written to separate files or streams.
|
|
209
|
-
|
|
210
|
-
## API Reference
|
|
211
|
-
|
|
212
|
-
### Filter Object
|
|
213
|
-
|
|
214
|
-
`ArrayBufferExpanderFilter` is a plain JavaScript object (not a class) that can be passed to the `DicomMetadataListener` constructor.
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
const listener = new DicomMetadataListener(ArrayBufferExpanderFilter);
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Filter Method
|
|
221
|
-
|
|
222
|
-
The filter provides one method:
|
|
223
|
-
|
|
224
|
-
#### `value(next, v)`
|
|
225
|
-
|
|
226
|
-
Filter method that intercepts value calls.
|
|
227
|
-
|
|
228
|
-
**Parameters:**
|
|
229
|
-
- `next` (Function): The next function in the filter chain to call for pass-through
|
|
230
|
-
- `v` (any): The value being set
|
|
231
|
-
|
|
232
|
-
**Behavior:**
|
|
233
|
-
- If `v` is an `ArrayBuffer[]`, expands it into `startObject([])/value/pop` sequence
|
|
234
|
-
- Otherwise, calls `next(v)` to pass through to the next filter
|
|
235
|
-
|
|
236
|
-
**Context:**
|
|
237
|
-
When called, `this` refers to the `DicomMetadataListener` instance, providing access to:
|
|
238
|
-
- `this.current` - Current parsing state
|
|
239
|
-
- `this.startObject([])` - Start array method (arrays are created via `startObject([])`)
|
|
240
|
-
- `this.value()` - Value method
|
|
241
|
-
- `this.pop()` - Pop method
|
|
242
|
-
- `this.fmi` - File Meta Information
|
|
243
|
-
- `this.dict` - Dataset dictionary
|
|
244
|
-
- All other listener methods and properties
|
|
245
|
-
|
|
246
|
-
## Combining with Other Patterns
|
|
247
|
-
|
|
248
|
-
### With Multiple Filters
|
|
249
|
-
|
|
250
|
-
The `ArrayBufferExpanderFilter` can be easily combined with other filters in the `DicomMetadataListener`:
|
|
251
|
-
|
|
252
|
-
```javascript
|
|
253
|
-
const loggingFilter = {
|
|
254
|
-
value(next, v) {
|
|
255
|
-
console.log('Processing value:', v);
|
|
256
|
-
return next(v);
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// Filters are applied in order: expander runs first, then logging
|
|
261
|
-
const listener = new DicomMetadataListener(
|
|
262
|
-
ArrayBufferExpanderFilter,
|
|
263
|
-
loggingFilter
|
|
264
|
-
);
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### Filter Ordering
|
|
268
|
-
|
|
269
|
-
The order of filters matters. Filters are executed in the order they are passed to the constructor:
|
|
270
|
-
|
|
271
|
-
```javascript
|
|
272
|
-
// ArrayBufferExpanderFilter runs first to expand arrays,
|
|
273
|
-
// then customFilter sees the individual fragments
|
|
274
|
-
const listener = new DicomMetadataListener(
|
|
275
|
-
ArrayBufferExpanderFilter,
|
|
276
|
-
customFilter
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// If you want customFilter to see the original ArrayBuffer[],
|
|
280
|
-
// put it before the expander
|
|
281
|
-
const listener2 = new DicomMetadataListener(
|
|
282
|
-
customFilter,
|
|
283
|
-
ArrayBufferExpanderFilter
|
|
284
|
-
);
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Performance Considerations
|
|
288
|
-
|
|
289
|
-
- **Minimal Overhead**: The expander only adds overhead when checking array types
|
|
290
|
-
- **Memory Efficient**: No additional memory allocation; just changes the calling sequence
|
|
291
|
-
- **Streaming Friendly**: Allows downstream listeners to process fragments immediately
|
|
292
|
-
|
|
293
|
-
## Limitations
|
|
294
|
-
|
|
295
|
-
1. **Only Expands ArrayBuffer Arrays**: Other array types (e.g., `[string, string]`) are not expanded
|
|
296
|
-
2. **One Level Deep**: Does not recursively expand nested structures
|
|
297
|
-
3. **Requires Complete Array**: Cannot partially expand; receives entire array at once
|
|
298
|
-
|
|
299
|
-
## See Also
|
|
300
|
-
|
|
301
|
-
- [AsyncDicomReader Skill Guide](./AsyncDicomReader-skill.md)
|
|
302
|
-
- [DicomMetadataListener](../src/utilities/DicomMetadataListener.js)
|
|
303
|
-
- DICOM Standard PS3.5 (Encapsulated Format)
|