All files / extensions/default/src/utils dicomWriter.ts

13.33% Statements 4/30
0% Branches 0/10
14.28% Functions 1/7
13.33% Lines 4/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111    182x   182x                         182x                                         139674x                                                                                                                                                
import dcmjs from 'dcmjs';
 
export const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1';
 
export const DICOM_WRITE_OPTIONS = {
  allowInvalidVRLength: false,
  // `fragmentMultiframe` only governs whether a SINGLE frame is split across
  // multiple fragments (dcmjs splits frames larger than its 20KB fragment size).
  // It does NOT merge frames: in an encapsulated (compressed) transfer syntax
  // every frame is always written as its own fragment, preceded by the Basic
  // Offset Table; in an uncompressed syntax pixel data is never fragmented at
  // all. Keeping this `false` therefore yields exactly one fragment per frame
  // for compressed SEG — the conformant layout — without splitting large frames.
  fragmentMultiframe: false,
};
 
/** OHIF runtime fields — not DICOM tags; must not be enumerable for dcmjs datasetToDict. */
export const RUNTIME_INSTANCE_PROPERTY_KEYS = [
  'url',
  'wadorsuri',
  'wadouri',
  'wadoRoot',
  'wadoUri',
  'wadoUriRoot',
  'imageRendering',
  'imageId',
  '_parentInstance',
  'frameNumber',
] as const;
 
/**
 * Attaches OHIF runtime data on an instance without enumerable keys (safe for dcmjs datasetToDict).
 */
export function setNonEnumerableInstanceProperty(
  instance: Record<string, unknown>,
  key: string,
  value: unknown
) {
  Object.defineProperty(instance, key, {
    value,
    enumerable: false,
    writable: true,
    configurable: true,
  });
}
 
/**
 * Re-defines any existing enumerable runtime properties as non-enumerable (keeps values).
 */
export function makeExistingPropertiesNonEnumerable(instance: Record<string, unknown>) {
  for (const key of RUNTIME_INSTANCE_PROPERTY_KEYS) {
    Iif (!Object.prototype.hasOwnProperty.call(instance, key)) {
      continue;
    }
 
    const descriptor = Object.getOwnPropertyDescriptor(instance, key);
 
    Iif (!descriptor || descriptor.enumerable === false) {
      continue;
    }
 
    setNonEnumerableInstanceProperty(instance, key, descriptor.value);
  }
}
 
export function getDatasetTransferSyntaxUID(dataset) {
  const fromMeta = dataset?._meta?.TransferSyntaxUID;
 
  Iif (typeof fromMeta === 'string') {
    return fromMeta;
  }
 
  Iif (Array.isArray(fromMeta?.Value)) {
    return fromMeta.Value[0];
  }
 
  Iif (typeof dataset?.TransferSyntaxUID === 'string') {
    return dataset.TransferSyntaxUID;
  }
 
  return EXPLICIT_VR_LITTLE_ENDIAN;
}
 
function applyTransferSyntaxToFileMeta(dicomDict, transferSyntaxUID) {
  Iif (!transferSyntaxUID || !dicomDict?.meta) {
    return;
  }
 
  // Hex key only: DicomMessage.write treats every meta key as a tag string, so a
  // naturalized key like 'TransferSyntaxUID' would be parsed via parseInt(..., 16)
  // into tag (0000,0000) and written as a garbage element in group 2.
  dicomDict.meta['00020010'] = { vr: 'UI', Value: [transferSyntaxUID] };
}
 
export function datasetToDicomPart10Buffer(dataset) {
  makeExistingPropertiesNonEnumerable(dataset);
  const transferSyntaxUID = getDatasetTransferSyntaxUID(dataset);
  const dicomDict = dcmjs.data.datasetToDict(dataset);
  applyTransferSyntaxToFileMeta(dicomDict, transferSyntaxUID);
  return dicomDict.write(DICOM_WRITE_OPTIONS);
}
 
export function datasetToDicomBlob(dataset) {
  const part10Buffer = datasetToDicomPart10Buffer(dataset);
  return new Blob([part10Buffer], { type: 'application/dicom' });
}
 
export function writeDicomDictToPart10Buffer(dicomDict) {
  return dicomDict.write(DICOM_WRITE_OPTIONS);
}