def write_sequence_item(fp: DicomIO, dataset: Dataset, encodings: List[str]) -> None: """Write a `dataset` in a sequence to the file-like `fp`. This is similar to writing a data_element, but with a specific tag for Sequence Item. See DICOM Standard, Part 5, :dcm:`Section 7.5<sect_7.5.html>`. Parameters ---------- fp : file-like The file-like to write the encoded data to. dataset : Dataset The :class:`Dataset<pydicom.dataset.Dataset>` to write to `fp`. encodings : list of str The character encodings to use on text values. """ fp.write_tag(ItemTag) # marker for start of Sequence Item length_location = fp.tell() # save location for later. # will fill in real value later if not undefined length fp.write_UL(0xffffffff) write_dataset(fp, dataset, parent_encoding=encodings) if getattr(dataset, "is_undefined_length_sequence_item", False): fp.write_tag(ItemDelimiterTag) fp.write_UL(0) # 4-bytes 'length' field for delimiter item else: # we will be nice and set the lengths for the reader of this file location = fp.tell() fp.seek(length_location) fp.write_UL(location - length_location - 4) # 4 is length of UL fp.seek(location) # ready for next data_element
def write_data_element( fp: DicomIO, elem: Union[DataElement, RawDataElement], encodings: Optional[Union[str, List[str]]] = None) -> None: """Write the data_element to file fp according to dicom media storage rules. """ # Write element's tag fp.write_tag(elem.tag) # write into a buffer to avoid seeking back which can be expansive buffer = DicomBytesIO() buffer.is_little_endian = fp.is_little_endian buffer.is_implicit_VR = fp.is_implicit_VR VR: Optional[str] = elem.VR if not fp.is_implicit_VR and VR and len(VR) != 2: msg = (f"Cannot write ambiguous VR of '{VR}' for data element with " f"tag {repr(elem.tag)}.\nSet the correct VR before " f"writing, or use an implicit VR transfer syntax") raise ValueError(msg) if elem.is_raw: elem = cast(RawDataElement, elem) # raw data element values can be written as they are buffer.write(cast(bytes, elem.value)) is_undefined_length = elem.length == 0xFFFFFFFF else: elem = cast(DataElement, elem) if VR not in writers: raise NotImplementedError( f"write_data_element: unknown Value Representation '{VR}'") encodings = encodings or [default_encoding] encodings = convert_encodings(encodings) fn, param = writers[VR] is_undefined_length = elem.is_undefined_length if not elem.is_empty: if VR in text_VRs or VR in ('PN', 'SQ'): fn(buffer, elem, encodings=encodings) # type: ignore[operator] else: # Many numeric types use the same writer but with # numeric format parameter if param is not None: fn(buffer, elem, param) # type: ignore[operator] else: fn(buffer, elem) # type: ignore[operator] # valid pixel data with undefined length shall contain encapsulated # data, e.g. sequence items - raise ValueError otherwise (see #238) if is_undefined_length and elem.tag == 0x7fe00010: encap_item = b'\xfe\xff\x00\xe0' if not fp.is_little_endian: # Non-conformant endianness encap_item = b'\xff\xfe\xe0\x00' if not cast(bytes, elem.value).startswith(encap_item): raise ValueError( "(7FE0,0010) Pixel Data has an undefined length indicating " "that it's compressed, but the data isn't encapsulated as " "required. See pydicom.encaps.encapsulate() for more " "information") value_length = buffer.tell() if (not fp.is_implicit_VR and VR not in extra_length_VRs and not is_undefined_length and value_length > 0xffff): # see PS 3.5, section 6.2.2 for handling of this case msg = ( f"The value for the data element {elem.tag} exceeds the " f"size of 64 kByte and cannot be written in an explicit transfer " f"syntax. The data element VR is changed from '{VR}' to 'UN' " f"to allow saving the data.") warnings.warn(msg) VR = 'UN' # write the VR for explicit transfer syntax if not fp.is_implicit_VR: VR = cast(str, VR) fp.write(bytes(VR, default_encoding)) if VR in extra_length_VRs: fp.write_US(0) # reserved 2 bytes if (not fp.is_implicit_VR and VR not in extra_length_VRs and not is_undefined_length): fp.write_US(value_length) # Explicit VR length field is 2 bytes else: # write the proper length of the data_element in the length slot, # unless is SQ with undefined length. fp.write_UL(0xFFFFFFFF if is_undefined_length else value_length) fp.write(buffer.getvalue()) if is_undefined_length: fp.write_tag(SequenceDelimiterTag) fp.write_UL(0) # 4-byte 'length' of delimiter data item