// namespaces
var dwv = dwv || {};
dwv.dicom = dwv.dicom || {};
/**
* Get the dwv UID prefix.
* Issued by Medical Connections Ltd (www.medicalconnections.co.uk)
* on 25/10/2017.
*
* @returns {string} The dwv UID prefix.
*/
dwv.dicom.getDwvUIDPrefix = function () {
return '1.2.826.0.1.3680043.9.7278.1.';
};
/**
* Get a UID for a DICOM tag.
*
* @see http://dicom.nema.org/dicom/2013/output/chtml/part05/chapter_9.html
* @see http://dicomiseasy.blogspot.com/2011/12/chapter-4-dicom-objects-in-chapter-3.html
* @see https://stackoverflow.com/questions/46304306/how-to-generate-unique-dicom-uid
* @param {string} tagName The input tag.
* @returns {string} The corresponding UID.
*/
dwv.dicom.getUID = function (tagName) {
var uid = dwv.dicom.getDwvUIDPrefix();
if (tagName === 'ImplementationClassUID') {
uid += dwv.getVersion();
} else if (tagName === 'SOPInstanceUID') {
for (var i = 0; i < tagName.length; ++i) {
uid += tagName.charCodeAt(i);
}
// add date (only numbers)
uid += '.' + (new Date()).toISOString().replace(/\D/g, '');
} else {
throw new Error('Don\'t know how to generate a UID for the tag ' + tagName);
}
return uid;
};
/**
* Return true if the input number is even.
*
* @param {number} number The number to check.
* @returns {boolean} True is the number is even.
*/
dwv.dicom.isEven = function (number) {
return number % 2 === 0;
};
/**
* Is the input VR a non string VR.
*
* @param {string} vr The element VR.
* @returns {boolean} True if the VR is a non string one.
*/
dwv.dicom.isNonStringVr = function (vr) {
return vr === 'UN' || vr === 'OB' || vr === 'OW' ||
vr === 'OF' || vr === 'OD' || vr === 'US' || vr === 'SS' ||
vr === 'UL' || vr === 'SL' || vr === 'FL' || vr === 'FD' ||
vr === 'SQ' || vr === 'AT';
};
/**
* Is the input VR a string VR.
*
* @param {string} vr The element VR.
* @returns {boolean} True if the VR is a string one.
*/
dwv.dicom.isStringVr = function (vr) {
return !dwv.dicom.isNonStringVr(vr);
};
/**
* Is the input VR a VR that could need padding.
*
* @param {string} vr The element VR.
* @returns {boolean} True if the VR needs padding.
*/
dwv.dicom.isVrToPad = function (vr) {
return dwv.dicom.isStringVr(vr) || vr === 'OB';
};
/**
* Get the VR specific padding value.
*
* @param {string} vr The element VR.
* @returns {boolean} The value used to pad.
*/
dwv.dicom.getVrPad = function (vr) {
var pad = 0;
if (dwv.dicom.isStringVr(vr)) {
if (vr === 'UI') {
pad = '\0';
} else {
pad = ' ';
}
}
return pad;
};
/**
* Pad an input value according to its VR.
* see http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html
*
* @param {object} element The DICOM element to get the VR from.
* @param {object} value The value to pad.
* @returns {string} The padded value.
*/
dwv.dicom.padElementValue = function (element, value) {
if (typeof value !== 'undefined' && typeof value.length !== 'undefined') {
if (dwv.dicom.isVrToPad(element.vr) && !dwv.dicom.isEven(value.length)) {
if (value instanceof Array) {
value[value.length - 1] += dwv.dicom.getVrPad(element.vr);
} else {
value += dwv.dicom.getVrPad(element.vr);
}
}
}
return value;
};
/**
* Is this element an implicit length sequence?
*
* @param {object} element The element to check.
* @returns {boolean} True if it is.
*/
dwv.dicom.isImplicitLengthSequence = function (element) {
// sequence with no length
return (element.vr === 'SQ') &&
(element.vl === 'u/l');
};
/**
* Is this element an implicit length item?
*
* @param {object} element The element to check.
* @returns {boolean} True if it is.
*/
dwv.dicom.isImplicitLengthItem = function (element) {
// item with no length
return (element.tag.name === 'xFFFEE000') &&
(element.vl === 'u/l');
};
/**
* Is this element an implicit length pixel data?
*
* @param {object} element The element to check.
* @returns {boolean} True if it is.
*/
dwv.dicom.isImplicitLengthPixels = function (element) {
// pixel data with no length
return (element.tag.name === 'x7FE00010') &&
(element.vl === 'u/l');
};
/**
* Helper method to flatten an array of typed arrays to 2D typed array
*
* @param {Array} initialArray array of typed arrays
* @returns {object} a typed array containing all values
*/
dwv.dicom.flattenArrayOfTypedArrays = function (initialArray) {
var initialArrayLength = initialArray.length;
var arrayLength = initialArray[0].length;
// If this is not a array of arrays, just return the initial one:
if (typeof arrayLength === 'undefined') {
return initialArray;
}
var flattenendArrayLength = initialArrayLength * arrayLength;
var flattenedArray = new initialArray[0].constructor(flattenendArrayLength);
for (var i = 0; i < initialArrayLength; i++) {
var indexFlattenedArray = i * arrayLength;
flattenedArray.set(initialArray[i], indexFlattenedArray);
}
return flattenedArray;
};
/**
* DICOM writer.
*
* Example usage:
* var parser = new dwv.dicom.DicomParser();
* parser.parse(this.response);
*
* var writer = new dwv.dicom.DicomWriter(parser.getRawDicomElements());
* var blob = new Blob([writer.getBuffer()], {type: 'application/dicom'});
*
* var element = document.getElementById("download");
* element.href = URL.createObjectURL(blob);
* element.download = "anonym.dcm";
*
* @class
*/
dwv.dicom.DicomWriter = function () {
// flag to use VR=UN for private sequences, default to false
// (mainly used in tests)
this.useUnVrForPrivateSq = false;
// possible tag actions
var actions = {
copy: function (item) {
return item;
},
remove: function () {
return null;
},
clear: function (item) {
item.value[0] = '';
item.vl = 0;
item.endOffset = item.startOffset;
return item;
},
replace: function (item, value) {
var paddedValue = dwv.dicom.padElementValue(item, value);
item.value[0] = paddedValue;
item.vl = paddedValue.length;
item.endOffset = item.startOffset + paddedValue.length;
return item;
}
};
// default rules: just copy
var defaultRules = {
default: {action: 'copy', value: null}
};
/**
* Public (modifiable) rules.
* Set of objects as:
* name : { action: 'actionName', value: 'optionalValue }
* The names are either 'default', tagName or groupName.
* Each DICOM element will be checked to see if a rule is applicable.
* First checked by tagName and then by groupName,
* if nothing is found the default rule is applied.
*/
this.rules = defaultRules;
/**
* Example anonymisation rules.
*/
this.anonymisationRules = {
default: {action: 'remove', value: null},
PatientName: {action: 'replace', value: 'Anonymized'}, // tag
'Meta Element': {action: 'copy', value: null}, // group 'x0002'
Acquisition: {action: 'copy', value: null}, // group 'x0018'
'Image Presentation': {action: 'copy', value: null}, // group 'x0028'
Procedure: {action: 'copy', value: null}, // group 'x0040'
'Pixel Data': {action: 'copy', value: null} // group 'x7fe0'
};
/**
* Get the element to write according to the class rules.
* Priority order: tagName, groupName, default.
*
* @param {object} element The element to check
* @returns {object} The element to write, can be null.
*/
this.getElementToWrite = function (element) {
// get group and tag string name
var tag = new dwv.dicom.Tag(element.tag.group, element.tag.element);
var groupName = tag.getGroupName();
var tagName = tag.getNameFromDictionary();
// apply rules:
var rule;
if (typeof this.rules[element.tag.name] !== 'undefined') {
// 1. tag itself
rule = this.rules[element.tag.name];
} else if (tagName !== null && typeof this.rules[tagName] !== 'undefined') {
// 2. tag name
rule = this.rules[tagName];
} else if (typeof this.rules[groupName] !== 'undefined') {
// 3. group name
rule = this.rules[groupName];
} else {
// 4. default
rule = this.rules['default'];
}
// apply action on element and return
return actions[rule.action](element, rule.value);
};
};
/**
* Write a list of items.
*
* @param {dwv.dicom.DataWriter} writer The raw data writer.
* @param {number} byteOffset The offset to start writing from.
* @param {Array} items The list of items to write.
* @param {boolean} isImplicit Is the DICOM VR implicit?
* @returns {number} The new offset position.
*/
dwv.dicom.DicomWriter.prototype.writeDataElementItems = function (
writer, byteOffset, items, isImplicit) {
var item = null;
for (var i = 0; i < items.length; ++i) {
item = items[i];
var itemKeys = Object.keys(item);
if (itemKeys.length === 0) {
continue;
}
// item element (create new to not modify original)
var implicitLength = item.xFFFEE000.vl === 'u/l';
var itemElement = {
tag: item.xFFFEE000.tag,
vr: item.xFFFEE000.vr,
vl: implicitLength ? 0xffffffff : item.xFFFEE000.vl,
value: []
};
byteOffset = this.writeDataElement(
writer, itemElement, byteOffset, isImplicit);
// write rest
for (var m = 0; m < itemKeys.length; ++m) {
if (itemKeys[m] !== 'xFFFEE000' && itemKeys[m] !== 'xFFFEE00D') {
byteOffset = this.writeDataElement(
writer, item[itemKeys[m]], byteOffset, isImplicit);
}
}
// item delimitation
if (implicitLength) {
var itemDelimElement = {
tag: {
group: '0xFFFE',
element: '0xE00D',
name: 'ItemDelimitationItem'
},
vr: 'NONE',
vl: 0,
value: []
};
byteOffset = this.writeDataElement(
writer, itemDelimElement, byteOffset, isImplicit);
}
}
// return new offset
return byteOffset;
};
/**
* Write data with a specific Value Representation (VR).
*
* @param {dwv.dicom.DataWriter} writer The raw data writer.
* @param {string} vr The data Value Representation (VR).
* @param {string} vl The data Value Length (VL).
* @param {number} byteOffset The offset to start writing from.
* @param {Array} value The array to write.
* @param {boolean} isImplicit Is the DICOM VR implicit?
* @returns {number} The new offset position.
*/
dwv.dicom.DicomWriter.prototype.writeDataElementValue = function (
writer, vr, vl, byteOffset, value, isImplicit) {
// first check input type to know how to write
if (value instanceof Uint8Array) {
// binary data has been expanded 8 times at read
if (value.length === 8 * vl) {
byteOffset = writer.writeBinaryArray(byteOffset, value);
} else {
byteOffset = writer.writeUint8Array(byteOffset, value);
}
} else if (value instanceof Int8Array) {
byteOffset = writer.writeInt8Array(byteOffset, value);
} else if (value instanceof Uint16Array) {
byteOffset = writer.writeUint16Array(byteOffset, value);
} else if (value instanceof Int16Array) {
byteOffset = writer.writeInt16Array(byteOffset, value);
} else if (value instanceof Uint32Array) {
byteOffset = writer.writeUint32Array(byteOffset, value);
} else if (value instanceof Int32Array) {
byteOffset = writer.writeInt32Array(byteOffset, value);
} else {
// switch according to VR if input type is undefined
if (vr === 'UN') {
byteOffset = writer.writeUint8Array(byteOffset, value);
} else if (vr === 'OB') {
byteOffset = writer.writeInt8Array(byteOffset, value);
} else if (vr === 'OW') {
byteOffset = writer.writeInt16Array(byteOffset, value);
} else if (vr === 'OF') {
byteOffset = writer.writeInt32Array(byteOffset, value);
} else if (vr === 'OD') {
byteOffset = writer.writeInt64Array(byteOffset, value);
} else if (vr === 'US') {
byteOffset = writer.writeUint16Array(byteOffset, value);
} else if (vr === 'SS') {
byteOffset = writer.writeInt16Array(byteOffset, value);
} else if (vr === 'UL') {
byteOffset = writer.writeUint32Array(byteOffset, value);
} else if (vr === 'SL') {
byteOffset = writer.writeInt32Array(byteOffset, value);
} else if (vr === 'FL') {
byteOffset = writer.writeFloat32Array(byteOffset, value);
} else if (vr === 'FD') {
byteOffset = writer.writeFloat64Array(byteOffset, value);
} else if (vr === 'SQ') {
byteOffset = this.writeDataElementItems(
writer, byteOffset, value, isImplicit);
} else if (vr === 'AT') {
for (var i = 0; i < value.length; ++i) {
var hexString = value[i] + '';
var hexString1 = hexString.substring(1, 5);
var hexString2 = hexString.substring(6, 10);
var dec1 = parseInt(hexString1, 16);
var dec2 = parseInt(hexString2, 16);
var atValue = new Uint16Array([dec1, dec2]);
byteOffset = writer.writeUint16Array(byteOffset, atValue);
}
} else {
// join if array
if (Array.isArray(value)) {
value = value.join('\\');
}
// write
if (vr === 'SH' || vr === 'LO' || vr === 'ST' ||
vr === 'PN' || vr === 'LT' || vr === 'UT') {
byteOffset = writer.writeSpecialString(byteOffset, value);
} else {
byteOffset = writer.writeString(byteOffset, value);
}
}
}
// return new offset
return byteOffset;
};
/**
* Write a pixel data element.
*
* @param {dwv.dicom.DataWriter} writer The raw data writer.
* @param {string} vr The data Value Representation (VR).
* @param {string} vl The data Value Length (VL).
* @param {number} byteOffset The offset to start writing from.
* @param {Array} value The array to write.
* @param {boolean} isImplicit Is the DICOM VR implicit?
* @returns {number} The new offset position.
*/
dwv.dicom.DicomWriter.prototype.writePixelDataElementValue = function (
writer, vr, vl, byteOffset, value, isImplicit) {
// explicit length
if (vl !== 'u/l') {
var finalValue = value[0];
// flatten multi frame
if (value.length > 1) {
finalValue = dwv.dicom.flattenArrayOfTypedArrays(value);
}
// write
byteOffset = this.writeDataElementValue(
writer, vr, vl, byteOffset, finalValue, isImplicit);
} else {
// pixel data as sequence
var item = {};
// first item: basic offset table
item.xFFFEE000 = {
tag: {
group: '0xFFFE',
element: '0xE000',
name: 'xFFFEE000'
},
vr: 'UN',
vl: 0,
value: []
};
// data
for (var i = 0; i < value.length; ++i) {
item[i] = {
tag: {
group: '0xFFFE',
element: '0xE000',
name: 'xFFFEE000'
},
vr: vr,
vl: value[i].length,
value: value[i]
};
}
// write
byteOffset = this.writeDataElementItems(
writer, byteOffset, [item], isImplicit);
}
// return new offset
return byteOffset;
};
/**
* Write a data element.
*
* @param {dwv.dicom.DataWriter} writer The raw data writer.
* @param {object} element The DICOM data element to write.
* @param {number} byteOffset The offset to start writing from.
* @param {boolean} isImplicit Is the DICOM VR implicit?
* @returns {number} The new offset position.
*/
dwv.dicom.DicomWriter.prototype.writeDataElement = function (
writer, element, byteOffset, isImplicit) {
var isTagWithVR = new dwv.dicom.Tag(
element.tag.group, element.tag.element).isWithVR();
var is32bitVLVR = (isImplicit || !isTagWithVR)
? true : dwv.dicom.is32bitVLVR(element.vr);
// group
byteOffset = writer.writeHex(byteOffset, element.tag.group);
// element
byteOffset = writer.writeHex(byteOffset, element.tag.element);
// VR
var vr = element.vr;
// use VR=UN for private sequence
if (this.useUnVrForPrivateSq &&
new dwv.dicom.Tag(element.tag.group, element.tag.element).isPrivate() &&
vr === 'SQ') {
dwv.logger.warn('Write element using VR=UN for private sequence.');
vr = 'UN';
}
if (isTagWithVR && !isImplicit) {
byteOffset = writer.writeString(byteOffset, vr);
// reserved 2 bytes for 32bit VL
if (is32bitVLVR) {
byteOffset += 2;
}
}
// update vl for sequence or item with implicit length
var vl = element.vl;
if (dwv.dicom.isImplicitLengthSequence(element) ||
dwv.dicom.isImplicitLengthItem(element) ||
dwv.dicom.isImplicitLengthPixels(element)) {
vl = 0xffffffff;
}
// VL
if (is32bitVLVR) {
byteOffset = writer.writeUint32(byteOffset, vl);
} else {
byteOffset = writer.writeUint16(byteOffset, vl);
}
// value
var value = element.value;
// check value
if (typeof value === 'undefined') {
value = [];
}
// write
if (element.tag.name === 'x7FE00010') {
byteOffset = this.writePixelDataElementValue(
writer, element.vr, element.vl, byteOffset, value, isImplicit);
} else {
byteOffset = this.writeDataElementValue(
writer, element.vr, element.vl, byteOffset, value, isImplicit);
}
// sequence delimitation item for sequence with implicit length
if (dwv.dicom.isImplicitLengthSequence(element) ||
dwv.dicom.isImplicitLengthPixels(element)) {
var seqDelimElement = {
tag: {
group: '0xFFFE',
element: '0xE0DD',
name: 'SequenceDelimitationItem'
},
vr: 'NONE',
vl: 0,
value: []
};
byteOffset = this.writeDataElement(
writer, seqDelimElement, byteOffset, isImplicit);
}
// return new offset
return byteOffset;
};
/**
* Get the ArrayBuffer corresponding to input DICOM elements.
*
* @param {Array} dicomElements The wrapped elements to write.
* @returns {ArrayBuffer} The elements as a buffer.
*/
dwv.dicom.DicomWriter.prototype.getBuffer = function (dicomElements) {
// array keys
var keys = Object.keys(dicomElements);
// transfer syntax
var syntax = dwv.dicom.cleanString(dicomElements.x00020010.value[0]);
var isImplicit = dwv.dicom.isImplicitTransferSyntax(syntax);
var isBigEndian = dwv.dicom.isBigEndianTransferSyntax(syntax);
// calculate buffer size and split elements (meta and non meta)
var totalSize = 128 + 4; // DICM
var localSize = 0;
var metaElements = [];
var rawElements = [];
var element;
var groupName;
var metaLength = 0;
var fmiglTag = dwv.dicom.getFileMetaInformationGroupLengthTag();
// ImplementationClassUID
var icUIDTag = new dwv.dicom.Tag('0x0002', '0x0012');
// ImplementationVersionName
var ivnTag = new dwv.dicom.Tag('0x0002', '0x0013');
for (var i = 0, leni = keys.length; i < leni; ++i) {
element = this.getElementToWrite(dicomElements[keys[i]]);
if (element !== null &&
!fmiglTag.equals2(element.tag) &&
!icUIDTag.equals2(element.tag) &&
!ivnTag.equals2(element.tag)) {
localSize = 0;
// XB7 2020-04-17
// Check if UN can be converted to correct VR.
// This check must be done BEFORE calculating totalSize,
// otherwise there may be extra null bytes at the end of the file
// (dcmdump may crash because of these bytes)
dwv.dicom.checkUnknownVR(element);
// tag group name (remove first 0)
groupName = dwv.dicom.TagGroups[element.tag.group.substr(1)];
// prefix
if (groupName === 'Meta Element') {
localSize += dwv.dicom.getDataElementPrefixByteSize(element.vr, false);
} else {
localSize += dwv.dicom.getDataElementPrefixByteSize(
element.vr, isImplicit);
}
// value
var realVl = element.endOffset - element.startOffset;
localSize += parseInt(realVl, 10);
// sort elements
if (groupName === 'Meta Element') {
metaElements.push(element);
metaLength += localSize;
} else {
rawElements.push(element);
}
// add to total size
totalSize += localSize;
}
}
// ImplementationClassUID
var icUID = dwv.dicom.getDicomElement('ImplementationClassUID');
var icUIDSize = dwv.dicom.getDataElementPrefixByteSize(icUID.vr, isImplicit);
icUIDSize += dwv.dicom.setElementValue(
icUID, dwv.dicom.getUID('ImplementationClassUID'), false);
metaElements.push(icUID);
metaLength += icUIDSize;
totalSize += icUIDSize;
// ImplementationVersionName
var ivn = dwv.dicom.getDicomElement('ImplementationVersionName');
var ivnSize = dwv.dicom.getDataElementPrefixByteSize(ivn.vr, isImplicit);
var ivnValue = 'DWV_' + dwv.getVersion();
ivnSize += dwv.dicom.setElementValue(ivn, ivnValue, false);
metaElements.push(ivn);
metaLength += ivnSize;
totalSize += ivnSize;
// create the FileMetaInformationGroupLength element
var fmigl = dwv.dicom.getDicomElement('FileMetaInformationGroupLength');
var fmiglSize = dwv.dicom.getDataElementPrefixByteSize(fmigl.vr, isImplicit);
fmiglSize += dwv.dicom.setElementValue(fmigl, metaLength, false);
// add its size to the total one
totalSize += fmiglSize;
// create buffer
var buffer = new ArrayBuffer(totalSize);
var metaWriter = new dwv.dicom.DataWriter(buffer);
var dataWriter = new dwv.dicom.DataWriter(buffer, !isBigEndian);
// special character set
if (typeof dicomElements.x00080005 !== 'undefined') {
var scs = dwv.dicom.cleanString(dicomElements.x00080005.value[0]);
dataWriter.setUtfLabel(dwv.dicom.getUtfLabel(scs));
}
var offset = 128;
// DICM
offset = metaWriter.writeString(offset, 'DICM');
// FileMetaInformationGroupLength
offset = this.writeDataElement(metaWriter, fmigl, offset, false);
// write meta
for (var j = 0, lenj = metaElements.length; j < lenj; ++j) {
offset = this.writeDataElement(metaWriter, metaElements[j], offset, false);
}
// check meta position
var preambleSize = 128 + 4;
var metaOffset = preambleSize + fmiglSize + metaLength;
if (offset !== metaOffset) {
dwv.logger.warn('Bad size calculation... meta offset: ' + offset +
', calculated size:' + metaOffset +
' (diff:' + (offset - metaOffset) + ')');
}
// pass flag to writer
dataWriter.useUnVrForPrivateSq = this.useUnVrForPrivateSq;
// write non meta
for (var k = 0, lenk = rawElements.length; k < lenk; ++k) {
offset = this.writeDataElement(
dataWriter, rawElements[k], offset, isImplicit);
}
// check final position
if (offset !== totalSize) {
dwv.logger.warn('Bad size calculation... final offset: ' + offset +
', calculated size:' + totalSize +
' (diff:' + (offset - totalSize) + ')');
}
// return
return buffer;
};
/**
* Fix for broken DICOM elements: Replace "UN" with correct VR if the
* element exists in dictionary
*
* @param {object} element The DICOM element.
*/
dwv.dicom.checkUnknownVR = function (element) {
if (element.vr === 'UN') {
var tag = new dwv.dicom.Tag(element.tag.group, element.tag.element);
var dictVr = tag.getVrFromDictionary();
if (dictVr !== null && element.vr !== dictVr) {
element.vr = dictVr;
dwv.logger.info('Element ' + element.tag.group +
' ' + element.tag.element +
' VR changed from UN to ' + element.vr);
}
}
};
/**
* Get a DICOM element from its tag name (value set separatly).
*
* @param {string} tagName The string tag name.
* @returns {object} The DICOM element.
*/
dwv.dicom.getDicomElement = function (tagName) {
var tag = dwv.dicom.getTagFromDictionary(tagName);
// return element definition
return {
tag: {group: tag.getGroup(), element: tag.getElement()},
vr: tag.getVrFromDictionary()
};
};
/**
* Set a DICOM element value according to its VR (Value Representation).
*
* @param {object} element The DICOM element to set the value.
* @param {object} value The value to set.
* @param {boolean} isImplicit Does the data use implicit VR?
* @returns {number} The total element size.
*/
dwv.dicom.setElementValue = function (element, value, isImplicit) {
// byte size of the element
var size = 0;
// special sequence case
if (element.vr === 'SQ') {
// set the value
element.value = value;
element.vl = 0;
if (value !== null && value !== 0) {
var sqItems = [];
var name;
// explicit or implicit length
var explicitLength = true;
if (typeof value.explicitLength !== 'undefined') {
explicitLength = value.explicitLength;
delete value.explicitLength;
}
// items
var itemData;
var itemKeys = Object.keys(value);
for (var i = 0, leni = itemKeys.length; i < leni; ++i) {
var itemElements = {};
var subSize = 0;
itemData = value[itemKeys[i]];
// check data
if (itemData === null || itemData === 0) {
continue;
}
// elements
var subElement;
var elemKeys = Object.keys(itemData);
for (var j = 0, lenj = elemKeys.length; j < lenj; ++j) {
subElement = dwv.dicom.getDicomElement(elemKeys[j]);
subSize += dwv.dicom.setElementValue(
subElement, itemData[elemKeys[j]]);
name = new dwv.dicom.Tag(
subElement.tag.group, subElement.tag.element).getKey();
itemElements[name] = subElement;
subSize += dwv.dicom.getDataElementPrefixByteSize(
subElement.vr, isImplicit);
}
// item (after elements to get the size)
var itemElement = {
tag: {group: '0xFFFE', element: '0xE000'},
vr: 'NONE',
vl: (explicitLength ? subSize : 'u/l'),
value: []
};
name = new dwv.dicom.Tag(
itemElement.tag.group, itemElement.tag.element).getKey();
itemElements[name] = itemElement;
subSize += dwv.dicom.getDataElementPrefixByteSize('NONE', isImplicit);
// item delimitation
if (!explicitLength) {
var itemDelimElement = {
tag: {group: '0xFFFE', element: '0xE00D'},
vr: 'NONE',
vl: 0,
value: []
};
name = new dwv.dicom.Tag(
itemDelimElement.tag.group, itemDelimElement.tag.element).getKey();
itemElements[name] = itemDelimElement;
subSize += dwv.dicom.getDataElementPrefixByteSize('NONE', isImplicit);
}
size += subSize;
sqItems.push(itemElements);
}
// add sequence delimitation size
if (!explicitLength) {
size += dwv.dicom.getDataElementPrefixByteSize('NONE', isImplicit);
}
element.value = sqItems;
if (explicitLength) {
element.vl = size;
} else {
element.vl = 'u/l';
}
}
} else {
// set the value and calculate size
size = 0;
var paddedValue = dwv.dicom.padElementValue(element, value);
if (value instanceof Array) {
element.value = paddedValue;
for (var k = 0; k < paddedValue.length; ++k) {
// spearator
if (k !== 0) {
size += 1;
}
// value
size += paddedValue[k].toString().length;
}
} else {
element.value = [paddedValue];
if (typeof paddedValue !== 'undefined' &&
typeof paddedValue.length !== 'undefined') {
size = paddedValue.length;
} else {
// numbers
size = 1;
}
}
// convert size to bytes
if (element.vr === 'US' || element.vr === 'OW') {
size *= Uint16Array.BYTES_PER_ELEMENT;
} else if (element.vr === 'SS') {
size *= Int16Array.BYTES_PER_ELEMENT;
} else if (element.vr === 'UL') {
size *= Uint32Array.BYTES_PER_ELEMENT;
} else if (element.vr === 'SL') {
size *= Int32Array.BYTES_PER_ELEMENT;
} else if (element.vr === 'FL') {
size *= Float32Array.BYTES_PER_ELEMENT;
} else if (element.vr === 'FD') {
size *= Float64Array.BYTES_PER_ELEMENT;
} else {
size *= Uint8Array.BYTES_PER_ELEMENT;
}
element.vl = size;
}
// return the size of that data
return size;
};