Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
def write_ATvalue(fp: DicomIO, elem: DataElement) -> None:
    """Write a data_element tag to a file."""
    try:
        iter(cast(Sequence[Any], elem.value))  # see if is multi-valued AT;
        # Note will fail if Tag ever derived from true tuple rather than being
        # a long
    except TypeError:
        # make sure is expressed as a Tag instance
        tag = Tag(cast(int, elem.value))
        fp.write_tag(tag)
    else:
        tags = [Tag(tag) for tag in cast(Sequence[int], elem.value)]
        for tag in tags:
            fp.write_tag(tag)
Ejemplo n.º 3
0
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