Skip to content

Commit 66c1aee

Browse files
committed
fix: Instance TSUID is needed to decode images
1 parent 2efe1ae commit 66c1aee

File tree

4 files changed

+73
-10
lines changed

4 files changed

+73
-10
lines changed

.github/workflows/publish-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
publish-package:
10-
runs-on: ubuntu-20.04
10+
runs-on: ubuntu-latest
1111
environment: publish
1212

1313
steps:

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
test:
9-
runs-on: ubuntu-20.04
9+
runs-on: ubuntu-latest
1010

1111
steps:
1212
- name: Checkout repository

src/api.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { multipartEncode, multipartDecode } from './message.js';
2-
3-
1+
import { multipartEncode, multipartDecode, addHeaders } from './message.js';
42

53
function isObject(obj) {
64
return typeof obj === 'object' && obj !== null;
@@ -256,12 +254,16 @@ class DICOMwebClient {
256254
requestInstance.onreadystatechange = () => {
257255
if (requestInstance.readyState === 4) {
258256
if (requestInstance.status === 200) {
259-
const contentType = requestInstance.getResponseHeader('Content-Type');
257+
const contentType = requestInstance.getResponseHeader(
258+
'Content-Type',
259+
);
260+
const headers = requestInstance.getAllResponseHeaders();
260261
// Automatically distinguishes between multipart and singlepart in an array buffer, and
261262
// converts them into a consistent type.
262263
if (contentType && contentType.indexOf('multipart') !== -1) {
263264
resolve(multipartDecode(requestInstance.response));
264265
} else if (requestInstance.responseType === 'arraybuffer') {
266+
addHeaders(requestInstance.response, headers);
265267
resolve([requestInstance.response]);
266268
} else {
267269
resolve(requestInstance.response);

src/message.js

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function identifyBoundary(header) {
3838
const parts = header.split('\r\n');
3939

4040
for (let i = 0; i < parts.length; i++) {
41-
if (parts[i].substr(0, 2) === '--') {
41+
if (parts[i].substring(0, 2) === '--') {
4242
return parts[i];
4343
}
4444
}
@@ -179,16 +179,72 @@ function multipartEncode(
179179
};
180180
}
181181

182+
/**
183+
* Splits the header string into parts and extracts the simple contentType
184+
* and transferSyntaxUID, assigning them, plus the headers map into the destination object.
185+
*
186+
* @param {*} destination
187+
* @param {string} headerString
188+
*/
189+
function addHeaders(destination, headerString) {
190+
if (!headerString) {
191+
return;
192+
}
193+
const headerLines = headerString.split('\r\n').filter(Boolean);
194+
const headers = new Map();
195+
let transferSyntaxUID = null,
196+
contentType = null;
197+
198+
for (const line of headerLines) {
199+
const colon = line.indexOf(':');
200+
if (colon === -1) {
201+
continue;
202+
}
203+
const name = line.substring(0, colon).toLowerCase();
204+
const value = line.substring(colon + 1).trim();
205+
if (headers.has(name)) {
206+
headers.get(name).push(value);
207+
} else {
208+
headers.set(name, [value]);
209+
}
210+
if (name === 'content-type') {
211+
const endSimpleType = value.indexOf(';');
212+
contentType ||= value.substring(
213+
0,
214+
endSimpleType === -1 ? value.length : endSimpleType,
215+
);
216+
const transferSyntaxStart = value.indexOf('transfer-syntax=');
217+
if (transferSyntaxStart !== -1) {
218+
const endTsuid = value.indexOf(';', transferSyntaxStart);
219+
transferSyntaxUID = value.substring(
220+
transferSyntaxStart + 16,
221+
endTsuid === -1 ? value.length : endTsuid,
222+
);
223+
}
224+
}
225+
}
226+
227+
Object.defineProperty(destination, 'headers', { value: headers });
228+
Object.defineProperty(destination, 'contentType', { value: contentType });
229+
Object.defineProperty(destination, 'transferSyntaxUID', {
230+
value: transferSyntaxUID,
231+
});
232+
}
233+
182234
/**
183235
* Decode a Multipart encoded ArrayBuffer and return the components as an Array.
184236
*
185237
* @param {ArrayBuffer} response Data encoded as a 'multipart/related' message
186-
* @returns {Array} The content
238+
* @returns {Uint8Array[]} The content as an array of Uint8Array
239+
* Each item shall have a contentType value, and a transferSyntaxUID if available,
240+
* as well as the headers Map. See parseHeaders for output.
241+
*
187242
*/
188243
function multipartDecode(response) {
189244
// Use the raw data if it is provided in an appropriate format
190-
const message = ArrayBuffer.isView(response) ? response : new Uint8Array(response);
191-
245+
const message = ArrayBuffer.isView(response)
246+
? response
247+
: new Uint8Array(response);
192248
/* Set a maximum length to search for the header boundaries, otherwise
193249
findToken can run for a long time
194250
*/
@@ -211,6 +267,8 @@ function multipartDecode(response) {
211267
const boundaryLength = boundary.length;
212268
const components = [];
213269

270+
const headers = header.substring(boundary.length + 2);
271+
214272
let offset = boundaryLength;
215273

216274
// Loop until we cannot find any more boundaries
@@ -240,6 +298,8 @@ function multipartDecode(response) {
240298
// Extract data from response message, excluding "\r\n"
241299
const spacingLength = 2;
242300
const data = response.slice(offset, boundaryIndex - spacingLength);
301+
// TODO - extract header data on a per frame basis.
302+
addHeaders(data, headers);
243303

244304
// Add the data to the array of results
245305
components.push(data);
@@ -261,4 +321,5 @@ export {
261321
multipartEncode,
262322
multipartDecode,
263323
guid,
324+
addHeaders,
264325
};

0 commit comments

Comments
 (0)