Example #1
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.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 = data_element.VR
    if not fp.is_implicit_VR and len(VR) != 2:
        msg = ("Cannot write ambiguous VR of '{}' for data element with "
               "tag {}.\nSet the correct VR before writing, or use an "
               "implicit VR transfer syntax".format(VR,
                                                    repr(data_element.tag)))
        raise ValueError(msg)

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if not data_element.is_empty:
            if VR in text_VRs or VR in ('PN', 'SQ'):
                writer_function(buffer, data_element, encodings=encodings)
            else:
                # Many numeric types use the same writer but with
                # numeric format parameter
                if writer_param is not None:
                    writer_function(buffer, data_element, writer_param)
                else:
                    writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.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 data_element.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 = ('The value for the data element {} exceeds the size '
               'of 64 kByte and cannot be written in an explicit transfer '
               'syntax. The data element VR is changed from "{}" to "UN" '
               'to allow saving the data.'.format(data_element.tag, VR))
        warnings.warn(msg)
        VR = 'UN'

    # write the VR for explicit transfer syntax
    if not fp.is_implicit_VR:
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        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
Example #2
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
Example #3
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.tag)

    # If explicit VR, write the VR
    VR = data_element.VR
    if not fp.is_implicit_VR:
        if len(VR) != 2:
            msg = ("Cannot write ambiguous VR of '{}' for data element with "
                   "tag {}.\nSet the correct VR before writing, or use an "
                   "implicit VR transfer syntax".format(
                       VR, repr(data_element.tag)))
            raise ValueError(msg)
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    # 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

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if VR in text_VRs or VR in ('PN', 'SQ'):
            writer_function(buffer, data_element, encodings=encodings)
        else:
            # Many numeric types use the same writer but with numeric format
            # parameter
            if writer_param is not None:
                writer_function(buffer, data_element, writer_param)
            else:
                writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.tag == 0x7fe00010:
        val = data_element.value
        if (fp.is_little_endian and not val.startswith(b'\xfe\xff\x00\xe0')
                or not fp.is_little_endian
                and not val.startswith(b'\xff\xfe\xe0\x00')):
            raise ValueError('Pixel Data with undefined length must '
                             'start with an item tag')

    value_length = buffer.tell()
    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 only 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
Example #4
0
def write_data_element(fp, data_element, encodings=None):
    """Write the data_element to file fp according to
    dicom media storage rules.
    """
    # Write element's tag
    fp.write_tag(data_element.tag)

    # If explicit VR, write the VR
    VR = data_element.VR
    if not fp.is_implicit_VR:
        if len(VR) != 2:
            msg = ("Cannot write ambiguous VR of '{}' for data element with "
                   "tag {}.\nSet the correct VR before writing, or use an "
                   "implicit VR transfer syntax".format(
                       VR, repr(data_element.tag)))
            raise ValueError(msg)
        if not in_py2:
            fp.write(bytes(VR, default_encoding))
        else:
            fp.write(VR)
        if VR in extra_length_VRs:
            fp.write_US(0)  # reserved 2 bytes

    # 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

    if data_element.is_raw:
        # raw data element values can be written as they are
        buffer.write(data_element.value)
        is_undefined_length = data_element.length == 0xFFFFFFFF
    else:
        if VR not in writers:
            raise NotImplementedError(
                "write_data_element: unknown Value Representation "
                "'{0}'".format(VR))

        encodings = encodings or [default_encoding]
        encodings = convert_encodings(encodings)
        writer_function, writer_param = writers[VR]
        is_undefined_length = data_element.is_undefined_length
        if VR in text_VRs or VR in ('PN', 'SQ'):
            writer_function(buffer, data_element, encodings=encodings)
        else:
            # Many numeric types use the same writer but with numeric format
            # parameter
            if writer_param is not None:
                writer_function(buffer, data_element, writer_param)
            else:
                writer_function(buffer, data_element)

    # valid pixel data with undefined length shall contain encapsulated
    # data, e.g. sequence items - raise ValueError otherwise (see #238)
    if is_undefined_length and data_element.tag == 0x7fe00010:
        val = data_element.value
        if (fp.is_little_endian and not
                val.startswith(b'\xfe\xff\x00\xe0') or
                not fp.is_little_endian and
                not val.startswith(b'\xff\xfe\xe0\x00')):
            raise ValueError('Pixel Data with undefined length must '
                             'start with an item tag')

    value_length = buffer.tell()
    if (not fp.is_implicit_VR and VR not in extra_length_VRs and
            not is_undefined_length):
        try:
            fp.write_US(value_length)  # Explicit VR length field is 2 bytes
        except struct.error:
            msg = ('The value for the data element {} exceeds the size '
                   'of 64 kByte and cannot be written in an explicit transfer '
                   'syntax. You can save it using Implicit Little Endian '
                   'transfer syntax, or you have to truncate the value to not '
                   'exceed the maximum size of 64 kByte.'
                   .format(data_element.tag))
            raise ValueError(msg)
    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