From 1bcf4fc02d0499af1dc09e3fde2e3eea0602acce Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Thu, 5 Nov 2015 19:57:07 +0100 Subject: [PATCH] Update dicomParser. Small bug fix in hangingProtocol to avoid errors on non-image files --- Packages/cornerstone/client/dicomParser.js | 3095 +++++++++-------- .../cornerstone/client/hangingProtocol.js | 8 + 2 files changed, 1580 insertions(+), 1523 deletions(-) diff --git a/Packages/cornerstone/client/dicomParser.js b/Packages/cornerstone/client/dicomParser.js index bf6230245..67f934eb0 100644 --- a/Packages/cornerstone/client/dicomParser.js +++ b/Packages/cornerstone/client/dicomParser.js @@ -1,183 +1,160 @@ -/*! dicom-parser - v1.1.5 - 2015-08-17 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */ +/*! dicom-parser - v1.2.0 - 2015-11-02 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */ (function (root, factory) { - // node.js - if (typeof module !== 'undefined' && module.exports) { - module.exports = factory(); - } - else if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); - } else { - // Browser globals - if(typeof cornerstone === 'undefined'){ - dicomParser = {}; - - // meteor - if (typeof Package !== 'undefined') { - root.dicomParser = dicomParser; - } + // node.js + if (typeof module !== 'undefined' && module.exports) { + module.exports = factory(); + } + else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else { + // Browser globals + if(typeof cornerstone === 'undefined'){ + dicomParser = {}; + + // meteor + if (typeof Package !== 'undefined') { + root.dicomParser = dicomParser; + } + } + dicomParser = factory(); } - dicomParser = factory(); - } }(this, function () { - /** - * Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options - * argument is supplied and it contains the untilTag property, parsing will stop once that - * tag is encoutered. This can be used to parse partial byte streams. - * - * @param byteArray the byte array - * @param options object to control parsing behavior (optional) - * @returns {DataSet} - * @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the - * elements successfully parsed before the error. - */ - var dicomParser = (function(dicomParser) { + /** + * Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options + * argument is supplied and it contains the untilTag property, parsing will stop once that + * tag is encoutered. This can be used to parse partial byte streams. + * + * @param byteArray the byte array + * @param options object to control parsing behavior (optional) + * @returns {DataSet} + * @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the + * elements successfully parsed before the error. + */ +var dicomParser = (function(dicomParser) { if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } dicomParser.parseDicom = function(byteArray, options) { - if(byteArray === undefined) - { - throw "dicomParser.parseDicom: missing required parameter 'byteArray'"; - } - - var littleEndianByteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); - - function readPrefix() - { - littleEndianByteStream.seek(128); - var prefix = littleEndianByteStream.readFixedString(4); - if(prefix !== "DICM") + if(byteArray === undefined) { - throw "dicomParser.parseDicom: DICM prefix not found at location 132"; + throw "dicomParser.parseDicom: missing required parameter 'byteArray'"; } - } - function readPart10Header() - { - // Per the DICOM standard, the header is always encoded in Explicit VR Little Endian (see PS3.10, section 7.1) - // so use littleEndianByteStream throughout this method regardless of the transfer syntax - readPrefix(); - - var warnings = []; - var elements = {}; - while(littleEndianByteStream.position < littleEndianByteStream.byteArray.length) { - var position = littleEndianByteStream.position; - var element = dicomParser.readDicomElementExplicit(littleEndianByteStream, warnings); - if(element.tag > 'x0002ffff') { - littleEndianByteStream.position = position; - break; - } - // Cache the littleEndianByteArrayParser for meta header elements, since the rest of the data set may be big endian - // and this parser will be needed later if the meta header values are to be read. - element.parser = dicomParser.littleEndianByteArrayParser; - elements[element.tag] = element; + function readTransferSyntax(metaHeaderDataSet) { + if(metaHeaderDataSet.elements.x00020010 === undefined) { + throw 'dicomParser.parseDicom: missing required meta header attribute 0002,0010'; + } + var transferSyntaxElement = metaHeaderDataSet.elements.x00020010; + return dicomParser.readFixedString(byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length); } - var metaHeaderDataSet = new dicomParser.DataSet(littleEndianByteStream.byteArrayParser, littleEndianByteStream.byteArray, elements); - metaHeaderDataSet.warnings = littleEndianByteStream.warnings; - return metaHeaderDataSet; - } - function readTransferSyntax(metaHeaderDataSet) { - if(metaHeaderDataSet.elements.x00020010 === undefined) { - throw 'dicomParser.parseDicom: missing required meta header attribute 0002,0010'; + function isExplicit(transferSyntax) { + if(transferSyntax === '1.2.840.10008.1.2') // implicit little endian + { + return false; + } + // all other transfer syntaxes should be explicit + return true; } - var transferSyntaxElement = metaHeaderDataSet.elements.x00020010; - return dicomParser.readFixedString(littleEndianByteStream.byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length); - } - function isExplicit(transferSyntax) { - if(transferSyntax === '1.2.840.10008.1.2') // implicit little endian + function getDataSetByteStream(transferSyntax, position) { + if(transferSyntax === '1.2.840.10008.1.2.1.99') + { + if(typeof(pako) === "undefined") { + throw 'dicomParser.parseDicom: deflated transfer syntax encountered but pako not loaded'; + } + var deflated = byteArray.slice(position); + var inflated = pako.inflateRaw(deflated); + + // create a single byte array with the full header bytes and the inflated bytes + var fullByteArray = new Uint8Array(inflated.length + position); + fullByteArray.set(byteArray.slice(0, position), 0); + fullByteArray.set(inflated, position); + return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, fullByteArray, 0); + } + if(transferSyntax === '1.2.840.10008.1.2.2') // explicit big endian + { + return new dicomParser.ByteStream(dicomParser.bigEndianByteArrayParser, byteArray, position); + } + else + { + // all other transfer syntaxes are little endian; only the pixel encoding differs + // make a new stream so the metaheader warnings don't come along for the ride + return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray, position); + } + } + + function mergeDataSets(metaHeaderDataSet, instanceDataSet) { - return false; + for (var propertyName in metaHeaderDataSet.elements) + { + if(metaHeaderDataSet.elements.hasOwnProperty(propertyName)) + { + instanceDataSet.elements[propertyName] = metaHeaderDataSet.elements[propertyName]; + } + } + if (metaHeaderDataSet.warnings !== undefined) { + instanceDataSet.warnings = metaHeaderDataSet.warnings.concat(instanceDataSet.warnings); + } + return instanceDataSet; } - // all other transfer syntaxes should be explicit - return true; - } - function getDataSetByteStream(transferSyntax) { - if(transferSyntax === '1.2.840.10008.1.2.2') // explicit big endian + function readDataSet(metaHeaderDataSet) { - return new dicomParser.ByteStream(dicomParser.bigEndianByteArrayParser, byteArray, littleEndianByteStream.position); + var transferSyntax = readTransferSyntax(metaHeaderDataSet); + var explicit = isExplicit(transferSyntax); + var dataSetByteStream = getDataSetByteStream(transferSyntax, metaHeaderDataSet.position); + + var elements = {}; + var dataSet = new dicomParser.DataSet(dataSetByteStream.byteArrayParser, dataSetByteStream.byteArray, elements); + dataSet.warnings = dataSetByteStream.warnings; + + try{ + if(explicit) { + dicomParser.parseDicomDataSetExplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); + } + else + { + dicomParser.parseDicomDataSetImplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); + } + } + catch(e) { + var ex = { + exception: e, + dataSet: dataSet + }; + throw ex; + } + return dataSet; } - else - { - // all other transfer syntaxes are little endian; only the pixel encoding differs - // make a new stream so the metaheader warnings don't come along for the ride - return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray, littleEndianByteStream.position); + + // main function here + function parseTheByteStream() { + var metaHeaderDataSet = dicomParser.readPart10Header(byteArray, options); + + var dataSet = readDataSet(metaHeaderDataSet); + + return mergeDataSets(metaHeaderDataSet, dataSet); } - } - function mergeDataSets(metaHeaderDataSet, instanceDataSet) - { - for (var propertyName in metaHeaderDataSet.elements) - { - if(metaHeaderDataSet.elements.hasOwnProperty(propertyName)) - { - instanceDataSet.elements[propertyName] = metaHeaderDataSet.elements[propertyName]; - } - } - if (metaHeaderDataSet.warnings !== undefined) { - instanceDataSet.warnings = metaHeaderDataSet.warnings.concat(instanceDataSet.warnings); - } - return instanceDataSet; - } - - function readDataSet(metaHeaderDataSet) - { - var transferSyntax = readTransferSyntax(metaHeaderDataSet); - var explicit = isExplicit(transferSyntax); - var dataSetByteStream = getDataSetByteStream(transferSyntax); - - var elements = {}; - var dataSet = new dicomParser.DataSet(dataSetByteStream.byteArrayParser, dataSetByteStream.byteArray, elements); - dataSet.warnings = dataSetByteStream.warnings; - - try{ - if(explicit) { - dicomParser.parseDicomDataSetExplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); - } - else - { - dicomParser.parseDicomDataSetImplicit(dataSet, dataSetByteStream, dataSetByteStream.byteArray.length, options); - } - } - catch(e) { - var ex = { - exception: e, - dataSet: dataSet - }; - throw ex; - } - return dataSet; - } - - // main function here - function parseTheByteStream() { - var metaHeaderDataSet = readPart10Header(); - - var dataSet = readDataSet(metaHeaderDataSet); - - return mergeDataSets(metaHeaderDataSet, dataSet); - } - - // This is where we actually start parsing - return parseTheByteStream(); + // This is where we actually start parsing + return parseTheByteStream(); }; return dicomParser; - })(dicomParser); +})(dicomParser); - var dicomParser = (function (dicomParser) { +var dicomParser = (function (dicomParser) { "use strict"; if (dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -187,64 +164,64 @@ */ dicomParser.explicitDataSetToJS = function (dataSet, options) { - if(dataSet === undefined) { - throw 'dicomParser.explicitDataSetToJS: missing required parameter dataSet'; - } - - options = options || { - omitPrivateAttibutes: true, // true if private elements should be omitted - maxElementLength : 128 // maximum element length to try and convert to string format - }; - - var result = { - - }; - - for(var tag in dataSet.elements) { - var element = dataSet.elements[tag]; - - // skip this element if it a private element and our options specify that we should - if(options.omitPrivateAttibutes === true && dicomParser.isPrivateTag(tag)) - { - continue; + if(dataSet === undefined) { + throw 'dicomParser.explicitDataSetToJS: missing required parameter dataSet'; } - if(element.items) { - // handle sequences - var sequenceItems = []; - for(var i=0; i < element.items.length; i++) { - sequenceItems.push(dicomParser.explicitDataSetToJS(element.items[i].dataSet, options)); - } - result[tag] = sequenceItems; - } else { - var asString; - asString = undefined; - if(element.length < options.maxElementLength) { - asString = dicomParser.explicitElementToString(dataSet, element); - } + options = options || { + omitPrivateAttibutes: true, // true if private elements should be omitted + maxElementLength : 128 // maximum element length to try and convert to string format + }; - if(asString !== undefined) { - result[tag] = asString; - } else { - result[tag] = { - dataOffset: element.dataOffset, - length : element.length - }; - } + var result = { + + }; + + for(var tag in dataSet.elements) { + var element = dataSet.elements[tag]; + + // skip this element if it a private element and our options specify that we should + if(options.omitPrivateAttibutes === true && dicomParser.isPrivateTag(tag)) + { + continue; + } + + if(element.items) { + // handle sequences + var sequenceItems = []; + for(var i=0; i < element.items.length; i++) { + sequenceItems.push(dicomParser.explicitDataSetToJS(element.items[i].dataSet, options)); + } + result[tag] = sequenceItems; + } else { + var asString; + asString = undefined; + if(element.length < options.maxElementLength) { + asString = dicomParser.explicitElementToString(dataSet, element); + } + + if(asString !== undefined) { + result[tag] = asString; + } else { + result[tag] = { + dataOffset: element.dataOffset, + length : element.length + }; + } + } } - } - return result; + return result; }; return dicomParser; - }(dicomParser)); - var dicomParser = (function (dicomParser) { +}(dicomParser)); +var dicomParser = (function (dicomParser) { "use strict"; if (dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -256,244 +233,244 @@ */ dicomParser.explicitElementToString = function(dataSet, element) { - if(dataSet === undefined || element === undefined) { - throw 'dicomParser.explicitElementToString: missing required parameters'; - } - if(element.vr === undefined) { - throw 'dicomParser.explicitElementToString: cannot convert implicit element to string'; - } - var vr = element.vr; - var tag = element.tag; - - var textResult; - - function multiElementToString(numItems, func) { - var result = ""; - for(var i=0; i < numItems; i++) { - if(i !== 0) { - result += '/'; - } - result += func.call(dataSet, tag).toString(); + if(dataSet === undefined || element === undefined) { + throw 'dicomParser.explicitElementToString: missing required parameters'; } - return result; - } - - if(dicomParser.isStringVr(vr) === true) - { - textResult = dataSet.string(tag); - } - else if (vr == 'AT') { - var num = dataSet.uint32(tag); - if(num === undefined) { - return undefined; + if(element.vr === undefined) { + throw 'dicomParser.explicitElementToString: cannot convert implicit element to string'; } - if (num < 0) + var vr = element.vr; + var tag = element.tag; + + var textResult; + + function multiElementToString(numItems, func) { + var result = ""; + for(var i=0; i < numItems; i++) { + if(i !== 0) { + result += '/'; + } + result += func.call(dataSet, tag).toString(); + } + return result; + } + + if(dicomParser.isStringVr(vr) === true) { - num = 0xFFFFFFFF + num + 1; + textResult = dataSet.string(tag); + } + else if (vr == 'AT') { + var num = dataSet.uint32(tag); + if(num === undefined) { + return undefined; + } + if (num < 0) + { + num = 0xFFFFFFFF + num + 1; + } + + return 'x' + num.toString(16).toUpperCase(); + } + else if (vr == 'US') + { + textResult = multiElementToString(element.length / 2, dataSet.uint16); + } + else if(vr === 'SS') + { + textResult = multiElementToString(element.length / 2, dataSet.int16); + } + else if (vr == 'UL') + { + textResult = multiElementToString(element.length / 4, dataSet.uint32); + } + else if(vr === 'SL') + { + textResult = multiElementToString(element.length / 4, dataSet.int32); + } + else if(vr == 'FD') + { + textResult = multiElementToString(element.length / 8, dataSet.double); + } + else if(vr == 'FL') + { + textResult = multiElementToString(element.length / 4, dataSet.float); } - return 'x' + num.toString(16).toUpperCase(); - } - else if (vr == 'US') - { - textResult = multiElementToString(element.length / 2, dataSet.uint16); - } - else if(vr === 'SS') - { - textResult = multiElementToString(element.length / 2, dataSet.int16); - } - else if (vr == 'UL') - { - textResult = multiElementToString(element.length / 4, dataSet.uint32); - } - else if(vr === 'SL') - { - textResult = multiElementToString(element.length / 4, dataSet.int32); - } - else if(vr == 'FD') - { - textResult = multiElementToString(element.length / 8, dataSet.double); - } - else if(vr == 'FL') - { - textResult = multiElementToString(element.length / 4, dataSet.float); - } - - return textResult; + return textResult; }; return dicomParser; - }(dicomParser)); - /** - * Utility functions for dealing with DICOM - */ +}(dicomParser)); +/** + * Utility functions for dealing with DICOM + */ - var dicomParser = (function (dicomParser) +var dicomParser = (function (dicomParser) +{ + "use strict"; + + if(dicomParser === undefined) { - "use strict"; + dicomParser = {}; + } - if(dicomParser === undefined) - { - dicomParser = {}; + // algorithm based on http://stackoverflow.com/questions/1433030/validate-number-of-days-in-a-given-month + function daysInMonth(m, y) { // m is 0 indexed: 0-11 + switch (m) { + case 2 : + return (y % 4 == 0 && y % 100) || y % 400 == 0 ? 29 : 28; + case 9 : case 4 : case 6 : case 11 : + return 30; + default : + return 31 } + } - // algorithm based on http://stackoverflow.com/questions/1433030/validate-number-of-days-in-a-given-month - function daysInMonth(m, y) { // m is 0 indexed: 0-11 - switch (m) { - case 2 : - return (y % 4 == 0 && y % 100) || y % 400 == 0 ? 29 : 28; - case 9 : case 4 : case 6 : case 11 : - return 30; - default : - return 31 - } - } - - function isValidDate(d, m, y) { - // make year is a number - if(isNaN(y)) { - return false; - } - return m > 0 && m <= 12 && d > 0 && d <= daysInMonth(m, y); + function isValidDate(d, m, y) { + // make year is a number + if(isNaN(y)) { + return false; } + return m > 0 && m <= 12 && d > 0 && d <= daysInMonth(m, y); + } - /** - * Parses a DA formatted string into a Javascript object - * @param {string} date a string in the DA VR format - * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid - * @returns {*} Javascript object with properties year, month and day or undefined if not present or not 8 bytes long - */ - dicomParser.parseDA = function(date, validate) - { - if(date && date.length === 8) - { - var yyyy = parseInt(date.substring(0, 4), 10); - var mm = parseInt(date.substring(4, 6), 10); - var dd = parseInt(date.substring(6, 8), 10); - - if(validate) { - if (isValidDate(dd, mm, yyyy) !== true) { - throw "invalid DA '" + date + "'"; - } - } - return { - year: yyyy, - month: mm, - day: dd - }; - } - if(validate) { - throw "invalid DA '" + date + "'"; - } - return undefined; - }; - - return dicomParser; - }(dicomParser)); /** - * Utility functions for dealing with DICOM + * Parses a DA formatted string into a Javascript object + * @param {string} date a string in the DA VR format + * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid + * @returns {*} Javascript object with properties year, month and day or undefined if not present or not 8 bytes long */ - - var dicomParser = (function (dicomParser) + dicomParser.parseDA = function(date, validate) { - "use strict"; - - if(dicomParser === undefined) + if(date && date.length === 8) { - dicomParser = {}; - } - - /** - * Parses a TM formatted string into a javascript object with properties for hours, minutes, seconds and fractionalSeconds - * @param {string} time - a string in the TM VR format - * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid - * @returns {*} javascript object with properties for hours, minutes, seconds and fractionalSeconds or undefined if no element or data. Missing fields are set to undefined - */ - dicomParser.parseTM = function(time, validate) { - - if (time.length >= 2) // must at least have HH - { - // 0123456789 - // HHMMSS.FFFFFF - var hh = parseInt(time.substring(0, 2), 10); - var mm = time.length >= 4 ? parseInt(time.substring(2, 4), 10) : undefined; - var ss = time.length >= 6 ? parseInt(time.substring(4, 6), 10) : undefined; - var ffffff = time.length >= 8 ? parseInt(time.substring(7, 13), 10) : undefined; - - if(validate) { - if((isNaN(hh)) || - (mm !== undefined && isNaN(mm)) || - (ss !== undefined && isNaN(ss)) || - (ffffff !== undefined && isNaN(ffffff)) || - (hh < 0 || hh > 23) || - (mm && (mm <0 || mm > 59)) || - (ss && (ss <0 || ss > 59)) || - (ffffff && (ffffff <0 || ffffff > 999999))) - { - throw "invalid TM '" + time + "'"; - } - } - - return { - hours: hh, - minutes: mm, - seconds: ss, - fractionalSeconds: ffffff - }; - } + var yyyy = parseInt(date.substring(0, 4), 10); + var mm = parseInt(date.substring(4, 6), 10); + var dd = parseInt(date.substring(6, 8), 10); if(validate) { - throw "invalid TM '" + time + "'"; + if (isValidDate(dd, mm, yyyy) !== true) { + throw "invalid DA '" + date + "'"; + } + } + return { + year: yyyy, + month: mm, + day: dd + }; + } + if(validate) { + throw "invalid DA '" + date + "'"; + } + return undefined; + }; + + return dicomParser; +}(dicomParser)); +/** + * Utility functions for dealing with DICOM + */ + +var dicomParser = (function (dicomParser) +{ + "use strict"; + + if(dicomParser === undefined) + { + dicomParser = {}; + } + + /** + * Parses a TM formatted string into a javascript object with properties for hours, minutes, seconds and fractionalSeconds + * @param {string} time - a string in the TM VR format + * @param {boolean} [validate] - true if an exception should be thrown if the date is invalid + * @returns {*} javascript object with properties for hours, minutes, seconds and fractionalSeconds or undefined if no element or data. Missing fields are set to undefined + */ + dicomParser.parseTM = function(time, validate) { + + if (time.length >= 2) // must at least have HH + { + // 0123456789 + // HHMMSS.FFFFFF + var hh = parseInt(time.substring(0, 2), 10); + var mm = time.length >= 4 ? parseInt(time.substring(2, 4), 10) : undefined; + var ss = time.length >= 6 ? parseInt(time.substring(4, 6), 10) : undefined; + var ffffff = time.length >= 8 ? parseInt(time.substring(7, 13), 10) : undefined; + + if(validate) { + if((isNaN(hh)) || + (mm !== undefined && isNaN(mm)) || + (ss !== undefined && isNaN(ss)) || + (ffffff !== undefined && isNaN(ffffff)) || + (hh < 0 || hh > 23) || + (mm && (mm <0 || mm > 59)) || + (ss && (ss <0 || ss > 59)) || + (ffffff && (ffffff <0 || ffffff > 999999))) + { + throw "invalid TM '" + time + "'"; + } } - return undefined; - }; + return { + hours: hh, + minutes: mm, + seconds: ss, + fractionalSeconds: ffffff + }; + } - return dicomParser; - }(dicomParser)); - /** - * Utility functions for dealing with DICOM - */ + if(validate) { + throw "invalid TM '" + time + "'"; + } - var dicomParser = (function (dicomParser) - { + return undefined; + }; + + return dicomParser; +}(dicomParser)); +/** + * Utility functions for dealing with DICOM + */ + +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } var stringVrs = { - AE: true, - AS: true, - AT: false, - CS: true, - DA: true, - DS: true, - DT: true, - FL: false, - FD: false, - IS: true, - LO: true, - LT: true, - OB: false, - OD: false, - OF: false, - OW: false, - PN: true, - SH: true, - SL: false, - SQ: false, - SS: false, - ST: true, - TM: true, - UI: true, - UL: false, - UN: undefined, // dunno - UR: true, - US: false, - UT: true + AE: true, + AS: true, + AT: false, + CS: true, + DA: true, + DS: true, + DT: true, + FL: false, + FD: false, + IS: true, + LO: true, + LT: true, + OB: false, + OD: false, + OF: false, + OW: false, + PN: true, + SH: true, + SL: false, + SQ: false, + SS: false, + ST: true, + TM: true, + UI: true, + UL: false, + UN: undefined, // dunno + UR: true, + US: false, + UT: true }; /** @@ -503,7 +480,7 @@ */ dicomParser.isStringVr = function(vr) { - return stringVrs[vr]; + return stringVrs[vr]; }; /** @@ -513,9 +490,9 @@ */ dicomParser.isPrivateTag = function(tag) { - var lastGroupDigit = parseInt(tag[4]); - var groupIsOdd = (lastGroupDigit % 2) === 1; - return groupIsOdd; + var lastGroupDigit = parseInt(tag[4]); + var groupIsOdd = (lastGroupDigit % 2) === 1; + return groupIsOdd; }; /** @@ -525,109 +502,112 @@ * @returns {*} javascript object with properties for givenName, familyName, middleName, prefix and suffix or undefined if no element or data */ dicomParser.parsePN = function(personName) { - if(personName === undefined) { - return undefined; - } - var stringValues = personName.split('^'); - return { - familyName: stringValues[0], - givenName: stringValues[1], - middleName: stringValues[2], - prefix: stringValues[3], - suffix: stringValues[4] - }; + if(personName === undefined) { + return undefined; + } + var stringValues = personName.split('^'); + return { + familyName: stringValues[0], + givenName: stringValues[1], + middleName: stringValues[2], + prefix: stringValues[3], + suffix: stringValues[4] + }; }; return dicomParser; - }(dicomParser)); - /** - * Functionality for extracting encapsulated pixel data - */ +}(dicomParser)); +/** + * Functionality for extracting encapsulated pixel data + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } function getPixelDataFromFragments(byteStream, fragments, bufferSize) { - // if there is only one fragment, return a view on this array to avoid copying - if(fragments.length === 1) { - return new Uint8Array(byteStream.byteArray.buffer, fragments[0].dataOffset, fragments[0].length); - } - - // more than one fragment, combine all of the fragments into one buffer - var pixelData = new Uint8Array(bufferSize); - var pixelDataIndex = 0; - for(var i=0; i < fragments.length; i++) { - var fragmentOffset = fragments[i].dataOffset; - for(var j=0; j < fragments[i].length; j++) { - pixelData[pixelDataIndex++] = byteStream.byteArray[fragmentOffset++]; + // if there is only one fragment, return a view on this array to avoid copying + if(fragments.length === 1) { + return new Uint8Array(byteStream.byteArray.buffer, fragments[0].dataOffset, fragments[0].length); } - } - return pixelData; + // more than one fragment, combine all of the fragments into one buffer + var pixelData = new Uint8Array(bufferSize); + var pixelDataIndex = 0; + for(var i=0; i < fragments.length; i++) { + var fragmentOffset = fragments[i].dataOffset; + for(var j=0; j < fragments[i].length; j++) { + pixelData[pixelDataIndex++] = byteStream.byteArray[fragmentOffset++]; + } + } + + return pixelData; } function readFragmentsUntil(byteStream, endOfFrame) { - // Read fragments until we reach endOfFrame - var fragments = []; - var bufferSize = 0; - while(byteStream.position < endOfFrame && byteStream.position < byteStream.byteArray.length) { - var fragment = dicomParser.readSequenceItem(byteStream); - // NOTE: we only encounter this for the sequence delimiter tag when extracting the last frame - if(fragment.tag === 'xfffee0dd') { - break; + // Read fragments until we reach endOfFrame + var fragments = []; + var bufferSize = 0; + while(byteStream.position < endOfFrame && byteStream.position < byteStream.byteArray.length) { + var fragment = dicomParser.readSequenceItem(byteStream); + // NOTE: we only encounter this for the sequence delimiter tag when extracting the last frame + if(fragment.tag === 'xfffee0dd') { + break; + } + fragments.push(fragment); + byteStream.seek(fragment.length); + bufferSize += fragment.length; } - fragments.push(fragment); - byteStream.seek(fragment.length); - bufferSize += fragment.length; - } - // Convert the fragments into a single pixelData buffer - var pixelData = getPixelDataFromFragments(byteStream, fragments, bufferSize); - return pixelData; + // Convert the fragments into a single pixelData buffer + var pixelData = getPixelDataFromFragments(byteStream, fragments, bufferSize); + return pixelData; } function readEncapsulatedPixelDataWithBasicOffsetTable(pixelDataElement, byteStream, frame) { - // validate that we have an offset for this frame - var numFrames = pixelDataElement.basicOffsetTable.length; - if(frame > numFrames) { - throw "dicomParser.readEncapsulatedPixelData: parameter frame exceeds number of frames in basic offset table"; - } + // validate that we have an offset for this frame + var numFrames = pixelDataElement.basicOffsetTable.length; + if(frame > numFrames) { + throw "dicomParser.readEncapsulatedPixelData: parameter frame exceeds number of frames in basic offset table"; + } - // move to the start of this frame - var frameOffset = pixelDataElement.basicOffsetTable[frame]; - byteStream.seek(frameOffset); + // move to the start of this frame + var frameOffset = pixelDataElement.basicOffsetTable[frame]; + var firstFragment = byteStream.position; + byteStream.seek(frameOffset); - // Find the end of this frame - var endOfFrameOffset = pixelDataElement.basicOffsetTable[frame + 1]; - if(endOfFrameOffset === undefined) { // special case for last frame - endOfFrameOffset = byteStream.position + pixelDataElement.length; - } + // Find the end of this frame + var endOfFrameOffset = pixelDataElement.basicOffsetTable[frame + 1]; + if(endOfFrameOffset === undefined) { // special case for last frame + endOfFrameOffset = byteStream.position + pixelDataElement.length; + } else { + endOfFrameOffset += firstFragment; + } - // read this frame - var pixelData = readFragmentsUntil(byteStream, endOfFrameOffset); - return pixelData; + // read this frame + var pixelData = readFragmentsUntil(byteStream, endOfFrameOffset); + return pixelData; } function readEncapsulatedDataNoBasicOffsetTable(pixelDataElement, byteStream, frame) { - // if the basic offset table is empty, this is a single frame so make sure the requested - // frame is 0 - if(frame !== 0) { - throw 'dicomParser.readEncapsulatedPixelData: non zero frame specified for single frame encapsulated pixel data'; - } + // if the basic offset table is empty, this is a single frame so make sure the requested + // frame is 0 + if(frame !== 0) { + throw 'dicomParser.readEncapsulatedPixelData: non zero frame specified for single frame encapsulated pixel data'; + } - // read this frame - var endOfFrame = byteStream.position + pixelDataElement.length; - var pixelData = readFragmentsUntil(byteStream, endOfFrame); - return pixelData; + // read this frame + var endOfFrame = byteStream.position + pixelDataElement.length; + var pixelData = readFragmentsUntil(byteStream, endOfFrame); + return pixelData; } /** @@ -640,243 +620,243 @@ */ dicomParser.readEncapsulatedPixelData = function(dataSet, pixelDataElement, frame) { - if(dataSet === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'dataSet'"; - } - if(pixelDataElement === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'element'"; - } - if(frame === undefined) { - throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'frame'"; - } - if(pixelDataElement.tag !== 'x7fe00010') { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to non pixel data tag (expected tag = x7fe00010'"; - } - if(pixelDataElement.encapsulatedPixelData !== true) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.hadUndefinedLength !== true) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.basicOffsetTable === undefined) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(pixelDataElement.fragments === undefined) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; - } - if(frame < 0) { - throw "dicomParser.readEncapsulatedPixelData: parameter 'frame' must be >= 0"; - } + if(dataSet === undefined) { + throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'dataSet'"; + } + if(pixelDataElement === undefined) { + throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'element'"; + } + if(frame === undefined) { + throw "dicomParser.readEncapsulatedPixelData: missing required parameter 'frame'"; + } + if(pixelDataElement.tag !== 'x7fe00010') { + throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to non pixel data tag (expected tag = x7fe00010'"; + } + if(pixelDataElement.encapsulatedPixelData !== true) { + throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; + } + if(pixelDataElement.hadUndefinedLength !== true) { + throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; + } + if(pixelDataElement.basicOffsetTable === undefined) { + throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; + } + if(pixelDataElement.fragments === undefined) { + throw "dicomParser.readEncapsulatedPixelData: parameter 'element' refers to pixel data element that does not have encapsulated pixel data"; + } + if(frame < 0) { + throw "dicomParser.readEncapsulatedPixelData: parameter 'frame' must be >= 0"; + } - // seek past the basic offset table (no need to parse it again since we already have) - var byteStream = new dicomParser.ByteStream(dataSet.byteArrayParser, dataSet.byteArray, pixelDataElement.dataOffset); - var basicOffsetTable = dicomParser.readSequenceItem(byteStream); - if(basicOffsetTable.tag !== 'xfffee000') - { - throw "dicomParser.readEncapsulatedPixelData: missing basic offset table xfffee000"; - } - byteStream.seek(basicOffsetTable.length); + // seek past the basic offset table (no need to parse it again since we already have) + var byteStream = new dicomParser.ByteStream(dataSet.byteArrayParser, dataSet.byteArray, pixelDataElement.dataOffset); + var basicOffsetTable = dicomParser.readSequenceItem(byteStream); + if(basicOffsetTable.tag !== 'xfffee000') + { + throw "dicomParser.readEncapsulatedPixelData: missing basic offset table xfffee000"; + } + byteStream.seek(basicOffsetTable.length); - // If the basic offset table is empty (no entries), it is a single frame. If it is not empty, - // it has at least one frame so use the basic offset table to find the bytes - if(pixelDataElement.basicOffsetTable.length !== 0) - { - return readEncapsulatedPixelDataWithBasicOffsetTable(pixelDataElement, byteStream, frame); - } - else - { - return readEncapsulatedDataNoBasicOffsetTable(pixelDataElement, byteStream, frame); - } + // If the basic offset table is empty (no entries), it is a single frame. If it is not empty, + // it has at least one frame so use the basic offset table to find the bytes + if(pixelDataElement.basicOffsetTable.length !== 0) + { + return readEncapsulatedPixelDataWithBasicOffsetTable(pixelDataElement, byteStream, frame); + } + else + { + return readEncapsulatedDataNoBasicOffsetTable(pixelDataElement, byteStream, frame); + } }; return dicomParser; - }(dicomParser)); +}(dicomParser)); - /** - * Internal helper functions for parsing different types from a big-endian byte array - */ +/** + * Internal helper functions for parsing different types from a big-endian byte array + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } dicomParser.bigEndianByteArrayParser = { - /** - * - * Parses an unsigned int 16 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 16 - * @throws error if buffer overread would occur - * @access private - */ - readUint16: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readUint16: position cannot be less than 0'; + /** + * + * Parses an unsigned int 16 from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed unsigned int 16 + * @throws error if buffer overread would occur + * @access private + */ + readUint16: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readUint16: position cannot be less than 0'; + } + if (position + 2 > byteArray.length) { + throw 'bigEndianByteArrayParser.readUint16: attempt to read past end of buffer'; + } + return (byteArray[position] << 8) + byteArray[position + 1]; + }, + + /** + * + * Parses a signed int 16 from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed signed int 16 + * @throws error if buffer overread would occur + * @access private + */ + readInt16: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readInt16: position cannot be less than 0'; + } + if (position + 2 > byteArray.length) { + throw 'bigEndianByteArrayParser.readInt16: attempt to read past end of buffer'; + } + var int16 = (byteArray[position] << 8) + byteArray[position + 1]; + // fix sign + if (int16 & 0x8000) { + int16 = int16 - 0xFFFF - 1; + } + return int16; + }, + + /** + * Parses an unsigned int 32 from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed unsigned int 32 + * @throws error if buffer overread would occur + * @access private + */ + readUint32: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readUint32: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'bigEndianByteArrayParser.readUint32: attempt to read past end of buffer'; + } + + var uint32 = (256 * (256 * (256 * byteArray[position] + + byteArray[position + 1]) + + byteArray[position + 2]) + + byteArray[position + 3]); + + return uint32; + }, + + /** + * Parses a signed int 32 from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed signed int 32 + * @throws error if buffer overread would occur + * @access private + */ + readInt32: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readInt32: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'bigEndianByteArrayParser.readInt32: attempt to read past end of buffer'; + } + + var int32 = ((byteArray[position] << 24) + + (byteArray[position + 1] << 16) + + (byteArray[position + 2] << 8) + + byteArray[position + 3]); + + return int32; + }, + + /** + * Parses 32-bit float from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed 32-bit float + * @throws error if buffer overread would occur + * @access private + */ + readFloat: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readFloat: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'bigEndianByteArrayParser.readFloat: attempt to read past end of buffer'; + } + + // I am sure there is a better way than this but this should be safe + var byteArrayForParsingFloat = new Uint8Array(4); + byteArrayForParsingFloat[3] = byteArray[position]; + byteArrayForParsingFloat[2] = byteArray[position + 1]; + byteArrayForParsingFloat[1] = byteArray[position + 2]; + byteArrayForParsingFloat[0] = byteArray[position + 3]; + var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); + return floatArray[0]; + }, + + /** + * Parses 64-bit float from a big-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed 64-bit float + * @throws error if buffer overread would occur + * @access private + */ + readDouble: function (byteArray, position) { + if (position < 0) { + throw 'bigEndianByteArrayParser.readDouble: position cannot be less than 0'; + } + + if (position + 8 > byteArray.length) { + throw 'bigEndianByteArrayParser.readDouble: attempt to read past end of buffer'; + } + + // I am sure there is a better way than this but this should be safe + var byteArrayForParsingFloat = new Uint8Array(8); + byteArrayForParsingFloat[7] = byteArray[position]; + byteArrayForParsingFloat[6] = byteArray[position + 1]; + byteArrayForParsingFloat[5] = byteArray[position + 2]; + byteArrayForParsingFloat[4] = byteArray[position + 3]; + byteArrayForParsingFloat[3] = byteArray[position + 4]; + byteArrayForParsingFloat[2] = byteArray[position + 5]; + byteArrayForParsingFloat[1] = byteArray[position + 6]; + byteArrayForParsingFloat[0] = byteArray[position + 7]; + var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); + return floatArray[0]; } - if (position + 2 > byteArray.length) { - throw 'bigEndianByteArrayParser.readUint16: attempt to read past end of buffer'; - } - return (byteArray[position] << 8) + byteArray[position + 1]; - }, - - /** - * - * Parses a signed int 16 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 16 - * @throws error if buffer overread would occur - * @access private - */ - readInt16: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readInt16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'bigEndianByteArrayParser.readInt16: attempt to read past end of buffer'; - } - var int16 = (byteArray[position] << 8) + byteArray[position + 1]; - // fix sign - if (int16 & 0x8000) { - int16 = int16 - 0xFFFF - 1; - } - return int16; - }, - - /** - * Parses an unsigned int 32 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readUint32: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readUint32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readUint32: attempt to read past end of buffer'; - } - - var uint32 = (256 * (256 * (256 * byteArray[position] + - byteArray[position + 1]) + - byteArray[position + 2]) + - byteArray[position + 3]); - - return uint32; - }, - - /** - * Parses a signed int 32 from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 32 - * @throws error if buffer overread would occur - * @access private - */ - readInt32: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readInt32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readInt32: attempt to read past end of buffer'; - } - - var int32 = ((byteArray[position] << 24) + - (byteArray[position + 1] << 16) + - (byteArray[position + 2] << 8) + - byteArray[position + 3]); - - return int32; - }, - - /** - * Parses 32-bit float from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 32-bit float - * @throws error if buffer overread would occur - * @access private - */ - readFloat: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readFloat: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'bigEndianByteArrayParser.readFloat: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(4); - byteArrayForParsingFloat[3] = byteArray[position]; - byteArrayForParsingFloat[2] = byteArray[position + 1]; - byteArrayForParsingFloat[1] = byteArray[position + 2]; - byteArrayForParsingFloat[0] = byteArray[position + 3]; - var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - }, - - /** - * Parses 64-bit float from a big-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 64-bit float - * @throws error if buffer overread would occur - * @access private - */ - readDouble: function (byteArray, position) { - if (position < 0) { - throw 'bigEndianByteArrayParser.readDouble: position cannot be less than 0'; - } - - if (position + 8 > byteArray.length) { - throw 'bigEndianByteArrayParser.readDouble: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(8); - byteArrayForParsingFloat[7] = byteArray[position]; - byteArrayForParsingFloat[6] = byteArray[position + 1]; - byteArrayForParsingFloat[5] = byteArray[position + 2]; - byteArrayForParsingFloat[4] = byteArray[position + 3]; - byteArrayForParsingFloat[3] = byteArray[position + 4]; - byteArrayForParsingFloat[2] = byteArray[position + 5]; - byteArrayForParsingFloat[1] = byteArray[position + 6]; - byteArrayForParsingFloat[0] = byteArray[position + 7]; - var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - } }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions common to parsing byte arrays of any type - */ +}(dicomParser)); +/** + * Internal helper functions common to parsing byte arrays of any type + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -893,48 +873,48 @@ */ dicomParser.readFixedString = function(byteArray, position, length) { - if(length < 0) - { - throw 'readFixedString - length cannot be less than 0'; - } - - if(position + length > byteArray.length) { - throw 'dicomParser.readFixedString: attempt to read past end of buffer'; - } - - var result = ""; - for(var i=0; i < length; i++) - { - var byte = byteArray[position + i]; - if(byte === 0) { - position += length; - return result; + if(length < 0) + { + throw 'readFixedString - length cannot be less than 0'; } - result += String.fromCharCode(byte); - } - return result; + if(position + length > byteArray.length) { + throw 'dicomParser.readFixedString: attempt to read past end of buffer'; + } + + var result = ""; + for(var i=0; i < length; i++) + { + var byte = byteArray[position + i]; + if(byte === 0) { + position += length; + return result; + } + result += String.fromCharCode(byte); + } + + return result; }; return dicomParser; - }(dicomParser)); - /** - * - * Internal helper class to assist with parsing. Supports reading from a byte - * stream contained in a Uint8Array. Example usage: - * - * var byteArray = new Uint8Array(32); - * var byteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); - * - * */ - var dicomParser = (function (dicomParser) - { +}(dicomParser)); +/** + * + * Internal helper class to assist with parsing. Supports reading from a byte + * stream contained in a Uint8Array. Example usage: + * + * var byteArray = new Uint8Array(32); + * var byteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); + * + * */ +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -948,30 +928,30 @@ * @throws will throw an error if the position parameter is not inside the byte array */ dicomParser.ByteStream = function(byteArrayParser, byteArray, position) { - if(byteArrayParser === undefined) - { - throw "dicomParser.ByteStream: missing required parameter 'byteArrayParser'"; - } - if(byteArray === undefined) - { - throw "dicomParser.ByteStream: missing required parameter 'byteArray'"; - } - if((byteArray instanceof Uint8Array) === false) { - throw 'dicomParser.ByteStream: parameter byteArray is not of type Uint8Array'; - } - if(position < 0) - { - throw "dicomParser.ByteStream: parameter 'position' cannot be less than 0"; - } - if(position >= byteArray.length) - { - throw "dicomParser.ByteStream: parameter 'position' cannot be greater than or equal to 'byteArray' length"; + if(byteArrayParser === undefined) + { + throw "dicomParser.ByteStream: missing required parameter 'byteArrayParser'"; + } + if(byteArray === undefined) + { + throw "dicomParser.ByteStream: missing required parameter 'byteArray'"; + } + if((byteArray instanceof Uint8Array) === false) { + throw 'dicomParser.ByteStream: parameter byteArray is not of type Uint8Array'; + } + if(position < 0) + { + throw "dicomParser.ByteStream: parameter 'position' cannot be less than 0"; + } + if(position >= byteArray.length) + { + throw "dicomParser.ByteStream: parameter 'position' cannot be greater than or equal to 'byteArray' length"; - } - this.byteArrayParser = byteArrayParser; - this.byteArray = byteArray; - this.position = position ? position : 0; - this.warnings = []; // array of string warnings encountered while parsing + } + this.byteArrayParser = byteArrayParser; + this.byteArray = byteArray; + this.position = position ? position : 0; + this.warnings = []; // array of string warnings encountered while parsing }; /** @@ -982,11 +962,11 @@ */ dicomParser.ByteStream.prototype.seek = function(offset) { - if(this.position + offset < 0) - { - throw "cannot seek to position < 0"; - } - this.position += offset; + if(this.position + offset < 0) + { + throw "cannot seek to position < 0"; + } + this.position += offset; }; /** @@ -997,12 +977,12 @@ */ dicomParser.ByteStream.prototype.readByteStream = function(numBytes) { - if(this.position + numBytes > this.byteArray.length) { - throw 'readByteStream - buffer overread'; - } - var byteArrayView = new Uint8Array(this.byteArray.buffer, this.position, numBytes); - this.position += numBytes; - return new dicomParser.ByteStream(this.byteArrayParser, byteArrayView); + if(this.position + numBytes > this.byteArray.length) { + throw 'readByteStream - buffer overread'; + } + var byteArrayView = new Uint8Array(this.byteArray.buffer, this.position, numBytes); + this.position += numBytes; + return new dicomParser.ByteStream(this.byteArrayParser, byteArrayView); }; /** @@ -1015,9 +995,9 @@ */ dicomParser.ByteStream.prototype.readUint16 = function() { - var result = this.byteArrayParser.readUint16(this.byteArray, this.position); - this.position += 2; - return result; + var result = this.byteArrayParser.readUint16(this.byteArray, this.position); + this.position += 2; + return result; }; /** @@ -1029,9 +1009,9 @@ */ dicomParser.ByteStream.prototype.readUint32 = function() { - var result = this.byteArrayParser.readUint32(this.byteArray, this.position); - this.position += 4; - return result; + var result = this.byteArrayParser.readUint32(this.byteArray, this.position); + this.position += 4; + return result; }; /** @@ -1044,45 +1024,45 @@ */ dicomParser.ByteStream.prototype.readFixedString = function(length) { - var result = dicomParser.readFixedString(this.byteArray, this.position, length); - this.position += length; - return result; + var result = dicomParser.readFixedString(this.byteArray, this.position, length); + this.position += length; + return result; }; return dicomParser; - }(dicomParser)); - /** - * - * The DataSet class encapsulates a collection of DICOM Elements and provides various functions - * to access the data in those elements - * - * Rules for handling padded spaces: - * DS = Strip leading and trailing spaces - * DT = Strip trailing spaces - * IS = Strip leading and trailing spaces - * PN = Strip trailing spaces - * TM = Strip trailing spaces - * AE = Strip leading and trailing spaces - * CS = Strip leading and trailing spaces - * SH = Strip leading and trailing spaces - * LO = Strip leading and trailing spaces - * LT = Strip trailing spaces - * ST = Strip trailing spaces - * UT = Strip trailing spaces - * - */ - var dicomParser = (function (dicomParser) - { +}(dicomParser)); +/** + * + * The DataSet class encapsulates a collection of DICOM Elements and provides various functions + * to access the data in those elements + * + * Rules for handling padded spaces: + * DS = Strip leading and trailing spaces + * DT = Strip trailing spaces + * IS = Strip leading and trailing spaces + * PN = Strip trailing spaces + * TM = Strip trailing spaces + * AE = Strip leading and trailing spaces + * CS = Strip leading and trailing spaces + * SH = Strip leading and trailing spaces + * LO = Strip leading and trailing spaces + * LT = Strip trailing spaces + * ST = Strip trailing spaces + * UT = Strip trailing spaces + * + */ +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } function getByteArrayParser(element, defaultParser) { - return (element.parser !== undefined ? element.parser : defaultParser); + return (element.parser !== undefined ? element.parser : defaultParser); } /** @@ -1094,9 +1074,9 @@ */ dicomParser.DataSet = function(byteArrayParser, byteArray, elements) { - this.byteArrayParser = byteArrayParser; - this.byteArray = byteArray; - this.elements = elements; + this.byteArrayParser = byteArrayParser; + this.byteArray = byteArray; + this.elements = elements; }; /** @@ -1107,13 +1087,13 @@ */ dicomParser.DataSet.prototype.uint16 = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readUint16(this.byteArray, element.dataOffset + (index *2)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readUint16(this.byteArray, element.dataOffset + (index *2)); + } + return undefined; }; /** @@ -1124,13 +1104,13 @@ */ dicomParser.DataSet.prototype.int16 = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readInt16(this.byteArray, element.dataOffset + (index * 2)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readInt16(this.byteArray, element.dataOffset + (index * 2)); + } + return undefined; }; /** @@ -1141,13 +1121,13 @@ */ dicomParser.DataSet.prototype.uint32 = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readUint32(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readUint32(this.byteArray, element.dataOffset + (index * 4)); + } + return undefined; }; /** @@ -1158,13 +1138,13 @@ */ dicomParser.DataSet.prototype.int32 = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readInt32(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readInt32(this.byteArray, element.dataOffset + (index * 4)); + } + return undefined; }; /** @@ -1175,13 +1155,13 @@ */ dicomParser.DataSet.prototype.float = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readFloat(this.byteArray, element.dataOffset + (index * 4)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readFloat(this.byteArray, element.dataOffset + (index * 4)); + } + return undefined; }; /** @@ -1192,13 +1172,13 @@ */ dicomParser.DataSet.prototype.double = function(tag, index) { - var element = this.elements[tag]; - index = (index !== undefined) ? index : 0; - if(element && element.length !== 0) - { - return getByteArrayParser(element, this.byteArrayParser).readDouble(this.byteArray, element.dataOffset + (index * 8)); - } - return undefined; + var element = this.elements[tag]; + index = (index !== undefined) ? index : 0; + if(element && element.length !== 0) + { + return getByteArrayParser(element, this.byteArrayParser).readDouble(this.byteArray, element.dataOffset + (index * 8)); + } + return undefined; }; /** @@ -1208,18 +1188,18 @@ */ dicomParser.DataSet.prototype.numStringValues = function(tag) { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - var numMatching = fixedString.match(/\\/g); - if(numMatching === null) + var element = this.elements[tag]; + if(element && element.length > 0) { - return 1; + var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); + var numMatching = fixedString.match(/\\/g); + if(numMatching === null) + { + return 1; + } + return numMatching.length + 1; } - return numMatching.length + 1; - } - return undefined; + return undefined; }; /** @@ -1235,23 +1215,23 @@ */ dicomParser.DataSet.prototype.string = function(tag, index) { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - if(index >= 0) + var element = this.elements[tag]; + if(element && element.length > 0) { - var values = fixedString.split('\\'); - // trim trailing spaces - return values[index].trim(); + var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); + if(index >= 0) + { + var values = fixedString.split('\\'); + // trim trailing spaces + return values[index].trim(); + } + else + { + // trim trailing spaces + return fixedString.trim(); + } } - else - { - // trim trailing spaces - return fixedString.trim(); - } - } - return undefined; + return undefined; }; /** @@ -1265,21 +1245,21 @@ */ dicomParser.DataSet.prototype.text = function(tag, index) { - var element = this.elements[tag]; - if(element && element.length > 0) - { - var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); - if(index >= 0) + var element = this.elements[tag]; + if(element && element.length > 0) { - var values = fixedString.split('\\'); - return values[index].replace(/ +$/, ''); + var fixedString = dicomParser.readFixedString(this.byteArray, element.dataOffset, element.length); + if(index >= 0) + { + var values = fixedString.split('\\'); + return values[index].replace(/ +$/, ''); + } + else + { + return fixedString.replace(/ +$/, ''); + } } - else - { - return fixedString.replace(/ +$/, ''); - } - } - return undefined; + return undefined; }; /** @@ -1291,16 +1271,16 @@ */ dicomParser.DataSet.prototype.floatString = function(tag, index) { - var element = this.elements[tag]; - if(element && element.length > 0) - { - index = (index !== undefined) ? index : 0; - var value = this.string(tag, index); - if(value !== undefined) { - return parseFloat(value); + var element = this.elements[tag]; + if(element && element.length > 0) + { + index = (index !== undefined) ? index : 0; + var value = this.string(tag, index); + if(value !== undefined) { + return parseFloat(value); + } } - } - return undefined; + return undefined; }; /** @@ -1312,32 +1292,32 @@ */ dicomParser.DataSet.prototype.intString = function(tag, index) { - var element = this.elements[tag]; - if(element && element.length > 0) { - index = (index !== undefined) ? index : 0; - var value = this.string(tag, index); - if(value !== undefined) { - return parseInt(value); + var element = this.elements[tag]; + if(element && element.length > 0) { + index = (index !== undefined) ? index : 0; + var value = this.string(tag, index); + if(value !== undefined) { + return parseInt(value); + } } - } - return undefined; + return undefined; }; //dicomParser.DataSet = DataSet; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -1349,89 +1329,89 @@ */ dicomParser.findEndOfEncapsulatedElement = function(byteStream, element, warnings) { - if(byteStream === undefined) - { - throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'element'"; - } - - element.encapsulatedPixelData = true; - element.basicOffsetTable = []; - element.fragments = []; - var basicOffsetTableItemTag = dicomParser.readTag(byteStream); - if(basicOffsetTableItemTag !== 'xfffee000') { - throw "dicomParser.findEndOfEncapsulatedElement: basic offset table not found"; - } - var basicOffsetTableItemlength = byteStream.readUint32(); - var numFragments = basicOffsetTableItemlength / 4; - for(var i =0; i < numFragments; i++) { - var offset = byteStream.readUint32(); - element.basicOffsetTable.push(offset); - } - var baseOffset = byteStream.position; - - while(byteStream.position < byteStream.byteArray.length) - { - var tag = dicomParser.readTag(byteStream); - var length = byteStream.readUint32(); - if(tag === 'xfffee0dd') + if(byteStream === undefined) { - byteStream.seek(length); - element.length = byteStream.position - element.dataOffset; - return; + throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'byteStream'"; } - else if(tag === 'xfffee000') + if(element === undefined) { - element.fragments.push({ - offset: byteStream.position - baseOffset - 8, - position : byteStream.position, - length : length - }); - } - else { - if(warnings) { - warnings.push('unexpected tag ' + tag + ' while searching for end of pixel data element with undefined length'); - } - if(length > byteStream.byteArray.length - byteStream.position) - { - // fix length - length = byteStream.byteArray.length - byteStream.position; - } - element.fragments.push({ - offset: byteStream.position - baseOffset - 8, - position : byteStream.position, - length : length - }); - byteStream.seek(length); - element.length = byteStream.position - element.dataOffset; - return; + throw "dicomParser.findEndOfEncapsulatedElement: missing required parameter 'element'"; } - byteStream.seek(length); - } + element.encapsulatedPixelData = true; + element.basicOffsetTable = []; + element.fragments = []; + var basicOffsetTableItemTag = dicomParser.readTag(byteStream); + if(basicOffsetTableItemTag !== 'xfffee000') { + throw "dicomParser.findEndOfEncapsulatedElement: basic offset table not found"; + } + var basicOffsetTableItemlength = byteStream.readUint32(); + var numFragments = basicOffsetTableItemlength / 4; + for(var i =0; i < numFragments; i++) { + var offset = byteStream.readUint32(); + element.basicOffsetTable.push(offset); + } + var baseOffset = byteStream.position; - if(warnings) { - warnings.push("pixel data element " + element.tag + " missing sequence delimiter tag xfffee0dd"); - } + while(byteStream.position < byteStream.byteArray.length) + { + var tag = dicomParser.readTag(byteStream); + var length = byteStream.readUint32(); + if(tag === 'xfffee0dd') + { + byteStream.seek(length); + element.length = byteStream.position - element.dataOffset; + return; + } + else if(tag === 'xfffee000') + { + element.fragments.push({ + offset: byteStream.position - baseOffset - 8, + position : byteStream.position, + length : length + }); + } + else { + if(warnings) { + warnings.push('unexpected tag ' + tag + ' while searching for end of pixel data element with undefined length'); + } + if(length > byteStream.byteArray.length - byteStream.position) + { + // fix length + length = byteStream.byteArray.length - byteStream.position; + } + element.fragments.push({ + offset: byteStream.position - baseOffset - 8, + position : byteStream.position, + length : length + }); + byteStream.seek(length); + element.length = byteStream.position - element.dataOffset; + return; + } + + byteStream.seek(length); + } + + if(warnings) { + warnings.push("pixel data element " + element.tag + " missing sequence delimiter tag xfffee0dd"); + } }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -1442,228 +1422,228 @@ */ dicomParser.findItemDelimitationItemAndSetElementLength = function(byteStream, element) { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; - } - - var itemDelimitationItemLength = 8; // group, element, length - var maxPosition = byteStream.byteArray.length - itemDelimitationItemLength; - while(byteStream.position <= maxPosition) - { - var groupNumber = byteStream.readUint16(); - if(groupNumber === 0xfffe) + if(byteStream === undefined) { - var elementNumber = byteStream.readUint16(); - if(elementNumber === 0xe00d) - { - // NOTE: It would be better to also check for the length to be 0 as part of the check above - // but we will just log a warning for now - var itemDelimiterLength = byteStream.readUint32(); // the length - if(itemDelimiterLength !== 0) { - byteStream.warnings('encountered non zero length following item delimiter at position' + byteStream.position - 4 + " while reading element of undefined length with tag ' + element.tag"); - } - element.length = byteStream.position - element.dataOffset; - return; - } + throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; } - } - // No item delimitation item - silently set the length to the end of the buffer and set the position past the end of the buffer - element.length = byteStream.byteArray.length - element.dataOffset; - byteStream.seek(byteStream.byteArray.length - byteStream.position); + var itemDelimitationItemLength = 8; // group, element, length + var maxPosition = byteStream.byteArray.length - itemDelimitationItemLength; + while(byteStream.position <= maxPosition) + { + var groupNumber = byteStream.readUint16(); + if(groupNumber === 0xfffe) + { + var elementNumber = byteStream.readUint16(); + if(elementNumber === 0xe00d) + { + // NOTE: It would be better to also check for the length to be 0 as part of the check above + // but we will just log a warning for now + var itemDelimiterLength = byteStream.readUint32(); // the length + if(itemDelimiterLength !== 0) { + byteStream.warnings('encountered non zero length following item delimiter at position' + byteStream.position - 4 + " while reading element of undefined length with tag ' + element.tag"); + } + element.length = byteStream.position - element.dataOffset; + return; + } + } + } + + // No item delimitation item - silently set the length to the end of the buffer and set the position past the end of the buffer + element.length = byteStream.byteArray.length - element.dataOffset; + byteStream.seek(byteStream.byteArray.length - byteStream.position); }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing different types from a little-endian byte array - */ +}(dicomParser)); +/** + * Internal helper functions for parsing different types from a little-endian byte array + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } dicomParser.littleEndianByteArrayParser = { - /** - * - * Parses an unsigned int 16 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 16 - * @throws error if buffer overread would occur - * @access private - */ - readUint16: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readUint16: position cannot be less than 0'; + /** + * + * Parses an unsigned int 16 from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed unsigned int 16 + * @throws error if buffer overread would occur + * @access private + */ + readUint16: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readUint16: position cannot be less than 0'; + } + if (position + 2 > byteArray.length) { + throw 'littleEndianByteArrayParser.readUint16: attempt to read past end of buffer'; + } + return byteArray[position] + (byteArray[position + 1] * 256); + }, + + /** + * + * Parses a signed int 16 from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed signed int 16 + * @throws error if buffer overread would occur + * @access private + */ + readInt16: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readInt16: position cannot be less than 0'; + } + if (position + 2 > byteArray.length) { + throw 'littleEndianByteArrayParser.readInt16: attempt to read past end of buffer'; + } + var int16 = byteArray[position] + (byteArray[position + 1] << 8); + // fix sign + if (int16 & 0x8000) { + int16 = int16 - 0xFFFF - 1; + } + return int16; + }, + + + /** + * Parses an unsigned int 32 from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed unsigned int 32 + * @throws error if buffer overread would occur + * @access private + */ + readUint32: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readUint32: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'littleEndianByteArrayParser.readUint32: attempt to read past end of buffer'; + } + + var uint32 = (byteArray[position] + + (byteArray[position + 1] * 256) + + (byteArray[position + 2] * 256 * 256) + + (byteArray[position + 3] * 256 * 256 * 256 )); + + return uint32; + }, + + /** + * Parses a signed int 32 from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed unsigned int 32 + * @throws error if buffer overread would occur + * @access private + */ + readInt32: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readInt32: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'littleEndianByteArrayParser.readInt32: attempt to read past end of buffer'; + } + + var int32 = (byteArray[position] + + (byteArray[position + 1] << 8) + + (byteArray[position + 2] << 16) + + (byteArray[position + 3] << 24)); + + return int32; + + }, + + /** + * Parses 32-bit float from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed 32-bit float + * @throws error if buffer overread would occur + * @access private + */ + readFloat: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readFloat: position cannot be less than 0'; + } + + if (position + 4 > byteArray.length) { + throw 'littleEndianByteArrayParser.readFloat: attempt to read past end of buffer'; + } + + // I am sure there is a better way than this but this should be safe + var byteArrayForParsingFloat = new Uint8Array(4); + byteArrayForParsingFloat[0] = byteArray[position]; + byteArrayForParsingFloat[1] = byteArray[position + 1]; + byteArrayForParsingFloat[2] = byteArray[position + 2]; + byteArrayForParsingFloat[3] = byteArray[position + 3]; + var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); + return floatArray[0]; + }, + + /** + * Parses 64-bit float from a little-endian byte array + * + * @param byteArray the byte array to read from + * @param position the position in the byte array to read from + * @returns {*} the parsed 64-bit float + * @throws error if buffer overread would occur + * @access private + */ + readDouble: function (byteArray, position) { + if (position < 0) { + throw 'littleEndianByteArrayParser.readDouble: position cannot be less than 0'; + } + + if (position + 8 > byteArray.length) { + throw 'littleEndianByteArrayParser.readDouble: attempt to read past end of buffer'; + } + + // I am sure there is a better way than this but this should be safe + var byteArrayForParsingFloat = new Uint8Array(8); + byteArrayForParsingFloat[0] = byteArray[position]; + byteArrayForParsingFloat[1] = byteArray[position + 1]; + byteArrayForParsingFloat[2] = byteArray[position + 2]; + byteArrayForParsingFloat[3] = byteArray[position + 3]; + byteArrayForParsingFloat[4] = byteArray[position + 4]; + byteArrayForParsingFloat[5] = byteArray[position + 5]; + byteArrayForParsingFloat[6] = byteArray[position + 6]; + byteArrayForParsingFloat[7] = byteArray[position + 7]; + var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); + return floatArray[0]; } - if (position + 2 > byteArray.length) { - throw 'littleEndianByteArrayParser.readUint16: attempt to read past end of buffer'; - } - return byteArray[position] + (byteArray[position + 1] * 256); - }, - - /** - * - * Parses a signed int 16 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed signed int 16 - * @throws error if buffer overread would occur - * @access private - */ - readInt16: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readInt16: position cannot be less than 0'; - } - if (position + 2 > byteArray.length) { - throw 'littleEndianByteArrayParser.readInt16: attempt to read past end of buffer'; - } - var int16 = byteArray[position] + (byteArray[position + 1] << 8); - // fix sign - if (int16 & 0x8000) { - int16 = int16 - 0xFFFF - 1; - } - return int16; - }, - - - /** - * Parses an unsigned int 32 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readUint32: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readUint32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readUint32: attempt to read past end of buffer'; - } - - var uint32 = (byteArray[position] + - (byteArray[position + 1] * 256) + - (byteArray[position + 2] * 256 * 256) + - (byteArray[position + 3] * 256 * 256 * 256 )); - - return uint32; - }, - - /** - * Parses a signed int 32 from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed unsigned int 32 - * @throws error if buffer overread would occur - * @access private - */ - readInt32: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readInt32: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readInt32: attempt to read past end of buffer'; - } - - var int32 = (byteArray[position] + - (byteArray[position + 1] << 8) + - (byteArray[position + 2] << 16) + - (byteArray[position + 3] << 24)); - - return int32; - - }, - - /** - * Parses 32-bit float from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 32-bit float - * @throws error if buffer overread would occur - * @access private - */ - readFloat: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readFloat: position cannot be less than 0'; - } - - if (position + 4 > byteArray.length) { - throw 'littleEndianByteArrayParser.readFloat: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(4); - byteArrayForParsingFloat[0] = byteArray[position]; - byteArrayForParsingFloat[1] = byteArray[position + 1]; - byteArrayForParsingFloat[2] = byteArray[position + 2]; - byteArrayForParsingFloat[3] = byteArray[position + 3]; - var floatArray = new Float32Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - }, - - /** - * Parses 64-bit float from a little-endian byte array - * - * @param byteArray the byte array to read from - * @param position the position in the byte array to read from - * @returns {*} the parsed 64-bit float - * @throws error if buffer overread would occur - * @access private - */ - readDouble: function (byteArray, position) { - if (position < 0) { - throw 'littleEndianByteArrayParser.readDouble: position cannot be less than 0'; - } - - if (position + 8 > byteArray.length) { - throw 'littleEndianByteArrayParser.readDouble: attempt to read past end of buffer'; - } - - // I am sure there is a better way than this but this should be safe - var byteArrayForParsingFloat = new Uint8Array(8); - byteArrayForParsingFloat[0] = byteArray[position]; - byteArrayForParsingFloat[1] = byteArray[position + 1]; - byteArrayForParsingFloat[2] = byteArray[position + 2]; - byteArrayForParsingFloat[3] = byteArray[position + 3]; - byteArrayForParsingFloat[4] = byteArray[position + 4]; - byteArrayForParsingFloat[5] = byteArray[position + 5]; - byteArrayForParsingFloat[6] = byteArray[position + 6]; - byteArrayForParsingFloat[7] = byteArray[position + 7]; - var floatArray = new Float64Array(byteArrayForParsingFloat.buffer); - return floatArray[0]; - } }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing implicit and explicit DICOM data sets - */ +}(dicomParser)); +/** + * Internal helper functions for parsing implicit and explicit DICOM data sets + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -1673,30 +1653,30 @@ */ dicomParser.parseDicomDataSetExplicit = function (dataSet, byteStream, maxPosition, options) { - maxPosition = (maxPosition === undefined) ? byteStream.byteArray.length : maxPosition ; - options = options || {}; + maxPosition = (maxPosition === undefined) ? byteStream.byteArray.length : maxPosition ; + options = options || {}; - if(byteStream === undefined) - { - throw "dicomParser.parseDicomDataSetExplicit: missing required parameter 'byteStream'"; - } - if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) - { - throw "dicomParser.parseDicomDataSetExplicit: invalid value for parameter 'maxPosition'"; - } - var elements = dataSet.elements; - - while(byteStream.position < maxPosition) - { - var element = dicomParser.readDicomElementExplicit(byteStream, dataSet.warnings, options.untilTag); - elements[element.tag] = element; - if(element.tag === options.untilTag) { - return; + if(byteStream === undefined) + { + throw "dicomParser.parseDicomDataSetExplicit: missing required parameter 'byteStream'"; + } + if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) + { + throw "dicomParser.parseDicomDataSetExplicit: invalid value for parameter 'maxPosition'"; + } + var elements = dataSet.elements; + + while(byteStream.position < maxPosition) + { + var element = dicomParser.readDicomElementExplicit(byteStream, dataSet.warnings, options.untilTag); + elements[element.tag] = element; + if(element.tag === options.untilTag) { + return; + } + } + if(byteStream.position > maxPosition) { + throw "dicomParser:parseDicomDataSetExplicit: buffer overrun"; } - } - if(byteStream.position > maxPosition) { - throw "dicomParser:parseDicomDataSetExplicit: buffer overrun"; - } }; /** @@ -1706,385 +1686,454 @@ */ dicomParser.parseDicomDataSetImplicit = function(dataSet, byteStream, maxPosition, options) { - maxPosition = (maxPosition === undefined) ? dataSet.byteArray.length : maxPosition ; - options = options || {}; + maxPosition = (maxPosition === undefined) ? dataSet.byteArray.length : maxPosition ; + options = options || {}; - if(byteStream === undefined) - { - throw "dicomParser.parseDicomDataSetImplicit: missing required parameter 'byteStream'"; - } - if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) - { - throw "dicomParser.parseDicomDataSetImplicit: invalid value for parameter 'maxPosition'"; - } - - var elements = dataSet.elements; - - while(byteStream.position < maxPosition) - { - var element = dicomParser.readDicomElementImplicit(byteStream, options.untilTag); - elements[element.tag] = element; - if(element.tag === options.untilTag) { - return; + if(byteStream === undefined) + { + throw "dicomParser.parseDicomDataSetImplicit: missing required parameter 'byteStream'"; + } + if(maxPosition < byteStream.position || maxPosition > byteStream.byteArray.length) + { + throw "dicomParser.parseDicomDataSetImplicit: invalid value for parameter 'maxPosition'"; + } + + var elements = dataSet.elements; + + while(byteStream.position < maxPosition) + { + var element = dicomParser.readDicomElementImplicit(byteStream, options.untilTag); + elements[element.tag] = element; + if(element.tag === options.untilTag) { + return; + } } - } }; return dicomParser; - }(dicomParser)); +}(dicomParser)); - /** - * Internal helper functions for for parsing DICOM elements - */ +/** + * Internal helper functions for for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } function getDataLengthSizeInBytesForVR(vr) { - if( vr === 'OB' || - vr === 'OW' || - vr === 'SQ' || - vr === 'OF' || - vr === 'UT' || - vr === 'UN') - { - return 4; - } - else - { - return 2; - } + if( vr === 'OB' || + vr === 'OW' || + vr === 'SQ' || + vr === 'OF' || + vr === 'UT' || + vr === 'UN') + { + return 4; + } + else + { + return 2; + } } dicomParser.readDicomElementExplicit = function(byteStream, warnings, untilTag) { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementExplicit: missing required parameter 'byteStream'"; - } - - var element = { - tag : dicomParser.readTag(byteStream), - vr : byteStream.readFixedString(2) - // length set below based on VR - // dataOffset set below based on VR and size of length - }; - - var dataLengthSizeBytes = getDataLengthSizeInBytesForVR(element.vr); - if(dataLengthSizeBytes === 2) - { - element.length = byteStream.readUint16(); - element.dataOffset = byteStream.position; - } - else - { - byteStream.seek(2); - element.length = byteStream.readUint32(); - element.dataOffset = byteStream.position; - } - - if(element.length === 4294967295) - { - element.hadUndefinedLength = true; - } - - if(element.tag === untilTag) { - return element; - } - - // if VR is SQ, parse the sequence items - if(element.vr === 'SQ') - { - dicomParser.readSequenceItemsExplicit(byteStream, element, warnings); - return element; - } - if(element.length === 4294967295) - { - if(element.tag === 'x7fe00010') { - dicomParser.findEndOfEncapsulatedElement(byteStream, element, warnings); - return element; - } else { - dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); - return element; + if(byteStream === undefined) + { + throw "dicomParser.readDicomElementExplicit: missing required parameter 'byteStream'"; } - } - byteStream.seek(element.length); - return element; + var element = { + tag : dicomParser.readTag(byteStream), + vr : byteStream.readFixedString(2) + // length set below based on VR + // dataOffset set below based on VR and size of length + }; + + var dataLengthSizeBytes = getDataLengthSizeInBytesForVR(element.vr); + if(dataLengthSizeBytes === 2) + { + element.length = byteStream.readUint16(); + element.dataOffset = byteStream.position; + } + else + { + byteStream.seek(2); + element.length = byteStream.readUint32(); + element.dataOffset = byteStream.position; + } + + if(element.length === 4294967295) + { + element.hadUndefinedLength = true; + } + + if(element.tag === untilTag) { + return element; + } + + // if VR is SQ, parse the sequence items + if(element.vr === 'SQ') + { + dicomParser.readSequenceItemsExplicit(byteStream, element, warnings); + return element; + } + if(element.length === 4294967295) + { + if(element.tag === 'x7fe00010') { + dicomParser.findEndOfEncapsulatedElement(byteStream, element, warnings); + return element; + } else { + dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); + return element; + } + } + + byteStream.seek(element.length); + return element; }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } dicomParser.readDicomElementImplicit = function(byteStream, untilTag) { - if(byteStream === undefined) - { - throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; - } - - var element = { - tag : dicomParser.readTag(byteStream), - length: byteStream.readUint32(), - dataOffset : byteStream.position - }; - - if(element.length === 4294967295) - { - element.hadUndefinedLength = true; - } - - if(element.tag === untilTag) { - return element; - } - - // peek ahead at the next tag to see if it looks like a sequence. This is not 100% - // safe because a non sequence item could have data that has these bytes, but this - // is how to do it without a data dictionary. - if ((byteStream.position + 4) <= byteStream.byteArray.length) { - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - - // zero length sequence - if (element.hadUndefinedLength && nextTag === 'xfffee0dd') { - element.length = 0; - byteStream.seek(8); - return element; + if(byteStream === undefined) + { + throw "dicomParser.readDicomElementImplicit: missing required parameter 'byteStream'"; } - if (nextTag === 'xfffee000') { - // parse the sequence - dicomParser.readSequenceItemsImplicit(byteStream, element); - //element.length = byteStream.byteArray.length - element.dataOffset; - return element; + var element = { + tag : dicomParser.readTag(byteStream), + length: byteStream.readUint32(), + dataOffset : byteStream.position + }; + + if(element.length === 4294967295) + { + element.hadUndefinedLength = true; } - } - // if element is not a sequence and has undefined length, we have to - // scan the data for a magic number to figure out when it ends. - if(element.length === 4294967295) - { - dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); + if(element.tag === untilTag) { + return element; + } + + // peek ahead at the next tag to see if it looks like a sequence. This is not 100% + // safe because a non sequence item could have data that has these bytes, but this + // is how to do it without a data dictionary. + if ((byteStream.position + 4) <= byteStream.byteArray.length) { + var nextTag = dicomParser.readTag(byteStream); + byteStream.seek(-4); + + // zero length sequence + if (element.hadUndefinedLength && nextTag === 'xfffee0dd') { + element.length = 0; + byteStream.seek(8); + return element; + } + + if (nextTag === 'xfffee000') { + // parse the sequence + dicomParser.readSequenceItemsImplicit(byteStream, element); + //element.length = byteStream.byteArray.length - element.dataOffset; + return element; + } + } + + // if element is not a sequence and has undefined length, we have to + // scan the data for a magic number to figure out when it ends. + if(element.length === 4294967295) + { + dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element); + return element; + } + + // non sequence element with known length, skip over the data part + byteStream.seek(element.length); return element; - } - - // non sequence element with known length, skip over the data part - byteStream.seek(element.length); - return element; }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ - - var dicomParser = (function (dicomParser) +}(dicomParser)); +/** + * Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options + * argument is supplied and it contains the untilTag property, parsing will stop once that + * tag is encoutered. This can be used to parse partial byte streams. + * + * @param byteArray the byte array + * @param options object to control parsing behavior (optional) + * @returns {DataSet} + * @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the + * elements successfully parsed before the error. + */ +var dicomParser = (function(dicomParser) { + if(dicomParser === undefined) { + dicomParser = {}; + } + + dicomParser.readPart10Header = function(byteArray, options) { + + if(byteArray === undefined) + { + throw "dicomParser.readPart10Header: missing required parameter 'byteArray'"; + } + + var littleEndianByteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray); + + function readPrefix() + { + littleEndianByteStream.seek(128); + var prefix = littleEndianByteStream.readFixedString(4); + if(prefix !== "DICM") + { + throw "dicomParser.readPart10Header: DICM prefix not found at location 132"; + } + } + + // main function here + function readTheHeader() { + // Per the DICOM standard, the header is always encoded in Explicit VR Little Endian (see PS3.10, section 7.1) + // so use littleEndianByteStream throughout this method regardless of the transfer syntax + readPrefix(); + + var warnings = []; + var elements = {}; + while(littleEndianByteStream.position < littleEndianByteStream.byteArray.length) { + var position = littleEndianByteStream.position; + var element = dicomParser.readDicomElementExplicit(littleEndianByteStream, warnings); + if(element.tag > 'x0002ffff') { + littleEndianByteStream.position = position; + break; + } + // Cache the littleEndianByteArrayParser for meta header elements, since the rest of the data set may be big endian + // and this parser will be needed later if the meta header values are to be read. + element.parser = dicomParser.littleEndianByteArrayParser; + elements[element.tag] = element; + } + var metaHeaderDataSet = new dicomParser.DataSet(littleEndianByteStream.byteArrayParser, littleEndianByteStream.byteArray, elements); + metaHeaderDataSet.warnings = littleEndianByteStream.warnings; + metaHeaderDataSet.position = littleEndianByteStream.position; + return metaHeaderDataSet; + } + + // This is where we actually start parsing + return readTheHeader(); + }; + + return dicomParser; +})(dicomParser); + +/** + * Internal helper functions for parsing DICOM elements + */ + +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } function readDicomDataSetExplicitUndefinedLength(byteStream, warnings) { - var elements = {}; + var elements = {}; - while(byteStream.position < byteStream.byteArray.length) - { - var element = dicomParser.readDicomElementExplicit(byteStream, warnings); - elements[element.tag] = element; - - // we hit an item delimiter tag, return the current offset to mark - // the end of this sequence item - if(element.tag === 'xfffee00d') + while(byteStream.position < byteStream.byteArray.length) { - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + var element = dicomParser.readDicomElementExplicit(byteStream, warnings); + elements[element.tag] = element; + + // we hit an item delimiter tag, return the current offset to mark + // the end of this sequence item + if(element.tag === 'xfffee00d') + { + return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + } + } - } - - // eof encountered - log a warning and return what we have for the element - byteStream.warnings.push('eof encountered before finding sequence delimitation item while reading sequence item of undefined length'); - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + // eof encountered - log a warning and return what we have for the element + byteStream.warnings.push('eof encountered before finding sequence delimitation item while reading sequence item of undefined length'); + return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); } function readSequenceItemExplicit(byteStream, warnings) { - var item = dicomParser.readSequenceItem(byteStream); + var item = dicomParser.readSequenceItem(byteStream); - if(item.length === 4294967295) - { - item.hadUndefinedLength = true; - item.dataSet = readDicomDataSetExplicitUndefinedLength(byteStream, warnings); - item.length = byteStream.position - item.dataOffset; - } - else - { - item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); - dicomParser.parseDicomDataSetExplicit(item.dataSet, byteStream, byteStream.position + item.length); - } - return item; + if(item.length === 4294967295) + { + item.hadUndefinedLength = true; + item.dataSet = readDicomDataSetExplicitUndefinedLength(byteStream, warnings); + item.length = byteStream.position - item.dataOffset; + } + else + { + item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); + dicomParser.parseDicomDataSetExplicit(item.dataSet, byteStream, byteStream.position + item.length); + } + return item; } function readSQElementUndefinedLengthExplicit(byteStream, element, warnings) { - while(byteStream.position < byteStream.byteArray.length) - { - // end reading this sequence if the next tag is the sequence delimitation item - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - if (nextTag === 'xfffee0dd') { - // set the correct length - element.length = byteStream.position - element.dataOffset; - byteStream.seek(8); - return element; - } + while(byteStream.position < byteStream.byteArray.length) + { + // end reading this sequence if the next tag is the sequence delimitation item + var nextTag = dicomParser.readTag(byteStream); + byteStream.seek(-4); + if (nextTag === 'xfffee0dd') { + // set the correct length + element.length = byteStream.position - element.dataOffset; + byteStream.seek(8); + return element; + } - var item = readSequenceItemExplicit(byteStream, warnings); - element.items.push(item); - } - element.length = byteStream.position - element.dataOffset; + var item = readSequenceItemExplicit(byteStream, warnings); + element.items.push(item); + } + element.length = byteStream.position - element.dataOffset; } function readSQElementKnownLengthExplicit(byteStream, element, warnings) { - var maxPosition = element.dataOffset + element.length; - while(byteStream.position < maxPosition) - { - var item = readSequenceItemExplicit(byteStream, warnings); - element.items.push(item); - } + var maxPosition = element.dataOffset + element.length; + while(byteStream.position < maxPosition) + { + var item = readSequenceItemExplicit(byteStream, warnings); + element.items.push(item); + } } dicomParser.readSequenceItemsExplicit = function(byteStream, element, warnings) { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'element'"; - } + if(byteStream === undefined) + { + throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'byteStream'"; + } + if(element === undefined) + { + throw "dicomParser.readSequenceItemsExplicit: missing required parameter 'element'"; + } - element.items = []; + element.items = []; - if(element.length === 4294967295) - { - readSQElementUndefinedLengthExplicit(byteStream, element); - } - else - { - readSQElementKnownLengthExplicit(byteStream, element, warnings); - } + if(element.length === 4294967295) + { + readSQElementUndefinedLengthExplicit(byteStream, element); + } + else + { + readSQElementKnownLengthExplicit(byteStream, element, warnings); + } }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } function readDicomDataSetImplicitUndefinedLength(byteStream) { - var elements = {}; + var elements = {}; - while(byteStream.position < byteStream.byteArray.length) - { - var element = dicomParser.readDicomElementImplicit(byteStream); - elements[element.tag] = element; - - // we hit an item delimiter tag, return the current offset to mark - // the end of this sequence item - if(element.tag === 'xfffee00d') + while(byteStream.position < byteStream.byteArray.length) { - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + var element = dicomParser.readDicomElementImplicit(byteStream); + elements[element.tag] = element; + + // we hit an item delimiter tag, return the current offset to mark + // the end of this sequence item + if(element.tag === 'xfffee00d') + { + return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + } } - } - // eof encountered - log a warning and return what we have for the element - byteStream.warnings.push('eof encountered before finding sequence item delimiter in sequence item of undefined length'); - return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); + // eof encountered - log a warning and return what we have for the element + byteStream.warnings.push('eof encountered before finding sequence item delimiter in sequence item of undefined length'); + return new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, elements); } function readSequenceItemImplicit(byteStream) { - var item = dicomParser.readSequenceItem(byteStream); + var item = dicomParser.readSequenceItem(byteStream); - if(item.length === 4294967295) - { - item.hadUndefinedLength = true; - item.dataSet = readDicomDataSetImplicitUndefinedLength(byteStream); - item.length = byteStream.position - item.dataOffset; - } - else - { - item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); - dicomParser.parseDicomDataSetImplicit(item.dataSet, byteStream, byteStream.position + item.length); - } - return item; + if(item.length === 4294967295) + { + item.hadUndefinedLength = true; + item.dataSet = readDicomDataSetImplicitUndefinedLength(byteStream); + item.length = byteStream.position - item.dataOffset; + } + else + { + item.dataSet = new dicomParser.DataSet(byteStream.byteArrayParser, byteStream.byteArray, {}); + dicomParser.parseDicomDataSetImplicit(item.dataSet, byteStream, byteStream.position + item.length); + } + return item; } function readSQElementUndefinedLengthImplicit(byteStream, element) { - while(byteStream.position < byteStream.byteArray.length) - { - // end reading this sequence if the next tag is the sequence delimitation item - var nextTag = dicomParser.readTag(byteStream); - byteStream.seek(-4); - if (nextTag === 'xfffee0dd') { - // set the correct length - element.length = byteStream.position - element.dataOffset; - byteStream.seek(8); - return element; - } + while(byteStream.position < byteStream.byteArray.length) + { + // end reading this sequence if the next tag is the sequence delimitation item + var nextTag = dicomParser.readTag(byteStream); + byteStream.seek(-4); + if (nextTag === 'xfffee0dd') { + // set the correct length + element.length = byteStream.position - element.dataOffset; + byteStream.seek(8); + return element; + } - var item = readSequenceItemImplicit(byteStream); - element.items.push(item); - } - element.length = byteStream.byteArray.length - element.dataOffset; + var item = readSequenceItemImplicit(byteStream); + element.items.push(item); + } + element.length = byteStream.byteArray.length - element.dataOffset; } function readSQElementKnownLengthImplicit(byteStream, element) { - var maxPosition = element.dataOffset + element.length; - while(byteStream.position < maxPosition) - { - var item = readSequenceItemImplicit(byteStream); - element.items.push(item); - } + var maxPosition = element.dataOffset + element.length; + while(byteStream.position < maxPosition) + { + var item = readSequenceItemImplicit(byteStream); + element.items.push(item); + } } /** @@ -2094,40 +2143,40 @@ */ dicomParser.readSequenceItemsImplicit = function(byteStream, element) { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'byteStream'"; - } - if(element === undefined) - { - throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'element'"; - } + if(byteStream === undefined) + { + throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'byteStream'"; + } + if(element === undefined) + { + throw "dicomParser.readSequenceItemsImplicit: missing required parameter 'element'"; + } - element.items = []; + element.items = []; - if(element.length === 4294967295) - { - readSQElementUndefinedLengthImplicit(byteStream, element); - } - else - { - readSQElementKnownLengthImplicit(byteStream, element); - } + if(element.length === 4294967295) + { + readSQElementUndefinedLengthImplicit(byteStream, element); + } + else + { + readSQElementKnownLengthImplicit(byteStream, element); + } }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -2140,34 +2189,34 @@ */ dicomParser.readSequenceItem = function(byteStream) { - if(byteStream === undefined) - { - throw "dicomParser.readSequenceItem: missing required parameter 'byteStream'"; - } + if(byteStream === undefined) + { + throw "dicomParser.readSequenceItem: missing required parameter 'byteStream'"; + } - var element = { - tag : dicomParser.readTag(byteStream), - length : byteStream.readUint32(), - dataOffset : byteStream.position - }; + var element = { + tag : dicomParser.readTag(byteStream), + length : byteStream.readUint32(), + dataOffset : byteStream.position + }; - return element; + return element; }; return dicomParser; - }(dicomParser)); - /** - * Internal helper functions for parsing DICOM elements - */ +}(dicomParser)); +/** + * Internal helper functions for parsing DICOM elements + */ - var dicomParser = (function (dicomParser) - { +var dicomParser = (function (dicomParser) +{ "use strict"; if(dicomParser === undefined) { - dicomParser = {}; + dicomParser = {}; } /** @@ -2178,35 +2227,35 @@ */ dicomParser.readTag = function(byteStream) { - if(byteStream === undefined) - { - throw "dicomParser.readTag: missing required parameter 'byteStream'"; - } + if(byteStream === undefined) + { + throw "dicomParser.readTag: missing required parameter 'byteStream'"; + } - var groupNumber = byteStream.readUint16() * 256 * 256; - var elementNumber = byteStream.readUint16(); - var tag = "x" + ('00000000' + (groupNumber + elementNumber).toString(16)).substr(-8); - return tag; + var groupNumber = byteStream.readUint16() * 256 * 256; + var elementNumber = byteStream.readUint16(); + var tag = "x" + ('00000000' + (groupNumber + elementNumber).toString(16)).substr(-8); + return tag; }; return dicomParser; - }(dicomParser)); - /** - * Version - */ +}(dicomParser)); +/** + * Version + */ - var dicomParser = (function (dicomParser) +var dicomParser = (function (dicomParser) +{ + "use strict"; + + if(dicomParser === undefined) { - "use strict"; + dicomParser = {}; + } - if(dicomParser === undefined) - { - dicomParser = {}; - } + dicomParser.version = "1.2.0"; - dicomParser.version = "1.1.5"; - - return dicomParser; - }(dicomParser)); return dicomParser; -})); \ No newline at end of file +}(dicomParser)); + return dicomParser; +})); diff --git a/Packages/cornerstone/client/hangingProtocol.js b/Packages/cornerstone/client/hangingProtocol.js index 591481964..565ea5971 100644 --- a/Packages/cornerstone/client/hangingProtocol.js +++ b/Packages/cornerstone/client/hangingProtocol.js @@ -9,6 +9,14 @@ function defaultHangingProtocol(inputData) { var stacks = []; studies.forEach(function(study) { study.seriesList.forEach(function(series) { + + // Ensure that the series has image data + // (All images have rows) + var anInstance = series.instances[0]; + if (!anInstance || !anInstance.rows) { + return; + } + var stack = { series: series, study: study