import { dictionary, tagGroups } from './dictionary'; /** * Immutable tag. */ export class Tag { /** * The tag group. * * @type {string} */ #group; /** * The tag element. * * @type {string} */ #element; /** * @param {string} group The tag group as '####'. * @param {string} element The tag element as '####'. */ constructor(group, element) { if (!group || typeof group === 'undefined') { throw new Error('Cannot create tag with no group.'); } if (group.length !== 4) { throw new Error('Cannot create tag with badly sized group: ' + group); } if (!element || typeof element === 'undefined') { throw new Error('Cannot create tag with no element.'); } if (element.length !== 4) { throw new Error('Cannot create tag with badly sized element: ' + element); } this.#group = group; this.#element = element; } /** * Get the tag group. * * @returns {string} The tag group. */ getGroup() { return this.#group; } /** * Get the tag element. * * @returns {string} The tag element. */ getElement() { return this.#element; } /** * Get as string representation of the tag: 'key: name'. * * @returns {string} A string representing the tag. */ toString() { return this.getKey() + ': ' + this.getNameFromDictionary(); } /** * Check for Tag equality. * * @param {Tag} rhs The other tag to compare to. * @returns {boolean} True if both tags are equal. */ equals(rhs) { return rhs !== null && typeof rhs !== 'undefined' && this.#group === rhs.getGroup() && this.#element === rhs.getElement(); } /** * Get the group-element key used to store DICOM elements. * * @returns {string} The key as '########'. */ getKey() { return this.#group + this.#element; } /** * Get the group name as defined in TagGroups. * * @returns {string} The name. */ getGroupName() { return tagGroups[this.#group]; } /** * Does this tag have a VR. * Basically not the Item, ItemDelimitationItem nor * SequenceDelimitationItem tags. * * @returns {boolean} True if this tag has a VR. */ isWithVR() { return !(this.#group === 'FFFE' && (this.#element === 'E000' || this.#element === 'E00D' || this.#element === 'E0DD') ); } /** * Is the tag group a private tag group ? * * See: {@link http://dicom.nema.org/medical/dicom/2022a/output/html/part05.html#sect_7.8}. * * @returns {boolean} True if the tag group is private, * ie if its group is an odd number. */ isPrivate() { return parseInt(this.#group, 16) % 2 === 1; } /** * Get the tag info from the dicom dictionary. * * @returns {string[]|undefined} The info as [vr, multiplicity, name]. */ #getInfoFromDictionary() { let info; if (typeof dictionary[this.#group] !== 'undefined' && typeof dictionary[this.#group][this.#element] !== 'undefined') { info = dictionary[this.#group][this.#element]; } return info; } /** * Get the tag Value Representation (VR) from the dicom dictionary. * * @returns {string|undefined} The VR. */ getVrFromDictionary() { let vr; const info = this.#getInfoFromDictionary(); if (typeof info !== 'undefined') { vr = info[0]; } return vr; } /** * Get the tag name from the dicom dictionary. * * @returns {string|undefined} The VR. */ getNameFromDictionary() { let name; const info = this.#getInfoFromDictionary(); if (typeof info !== 'undefined') { name = info[2]; } return name; } } // Tag class /** * Tag compare function. * * @param {Tag} a The first tag. * @param {Tag} b The second tag. * @returns {number} The result of the tag comparison, * positive for b before a, negative for a before b and * zero to keep same order. */ export function tagCompareFunction(a, b) { // first by group let res = parseInt(a.getGroup(), 16) - parseInt(b.getGroup(), 16); if (res === 0) { // by element if same group res = parseInt(a.getElement(), 16) - parseInt(b.getElement(), 16); } return res; } /** * Split a group-element key used to store DICOM elements. * * @param {string} key The key in form "00280102" as generated by tag::getKey. * @returns {Tag} The DICOM tag. */ export function getTagFromKey(key) { if (!key || typeof key === 'undefined') { throw new Error('Cannot create tag with no key.'); } if (key.length !== 8) { throw new Error('Cannot create tag with badly sized key: ' + key); } return new Tag(key.substring(0, 4), key.substring(4, 8)); } /** * Get the TransferSyntaxUID Tag. * * @returns {Tag} The tag. */ export function getTransferSyntaxUIDTag() { return new Tag('0002', '0010'); } /** * Get the FileMetaInformationGroupLength Tag. * * @returns {Tag} The tag. */ export function getFileMetaInformationGroupLengthTag() { return new Tag('0002', '0000'); } /** * Is the input tag the FileMetaInformationGroupLength Tag. * * @param {Tag} tag The tag to test. * @returns {boolean} True if the asked tag. */ export function isFileMetaInformationGroupLengthTag(tag) { return tag.equals(getFileMetaInformationGroupLengthTag()); } /** * Get the Item Tag. * * @returns {Tag} The tag. */ export function getItemTag() { return new Tag('FFFE', 'E000'); } /** * Is the input tag the Item Tag. * * @param {Tag} tag The tag to test. * @returns {boolean} True if the asked tag. */ export function isItemTag(tag) { // faster than tag.equals(getItemTag()); return tag.getKey() === 'FFFEE000'; } /** * Get the ItemDelimitationItem Tag. * * @returns {Tag} The tag. */ export function getItemDelimitationItemTag() { return new Tag('FFFE', 'E00D'); } /** * Is the input tag the ItemDelimitationItem Tag. * * @param {Tag} tag The tag to test. * @returns {boolean} True if the asked tag. */ export function isItemDelimitationItemTag(tag) { // faster than tag.equals(getItemDelimitationItemTag()); return tag.getKey() === 'FFFEE00D'; } /** * Get the SequenceDelimitationItem Tag. * * @returns {Tag} The tag. */ export function getSequenceDelimitationItemTag() { return new Tag('FFFE', 'E0DD'); } /** * Is the input tag the SequenceDelimitationItem Tag. * * @param {Tag} tag The tag to test. * @returns {boolean} True if the asked tag. */ export function isSequenceDelimitationItemTag(tag) { // faster than tag.equals(getSequenceDelimitationItemTag()); return tag.getKey() === 'FFFEE0DD'; } /** * Get the PixelData Tag. * * @returns {Tag} The tag. */ export function getPixelDataTag() { return new Tag('7FE0', '0010'); } /** * Is the input tag the PixelData Tag. * * @param {Tag} tag The tag to test. * @returns {boolean} True if the asked tag. */ export function isPixelDataTag(tag) { // faster than tag.equals(getPixelDataTag()); return tag.getKey() === '7FE00010'; } /** * Get a tag from the dictionary using a tag string name. * * @param {string} tagName The tag string name. * @returns {Tag|undefined} The tag object or null if not found. */ export function getTagFromDictionary(tagName) { if (typeof tagName === 'undefined' || tagName === null) { return null; } let group = null; let element = null; const dict = dictionary; const keys0 = Object.keys(dict); let keys1 = null; let foundTag = false; // search through dictionary for (let k0 = 0, lenK0 = keys0.length; k0 < lenK0; ++k0) { group = keys0[k0]; keys1 = Object.keys(dict[group]); for (let k1 = 0, lenK1 = keys1.length; k1 < lenK1; ++k1) { element = keys1[k1]; if (dict[group][element][2] === tagName) { foundTag = true; break; } } if (foundTag) { break; } } let tag; if (foundTag) { tag = new Tag(group, element); } return tag; }