o
    i                     @   s  d Z ddlmZ ddlmZmZ ddlZddlmZm	Z	 ddl
mZ ddlmZ ddlmZ dd	lmZmZmZ dd
lmZmZ ddlmZmZmZ dddeeB eB dedee fddZ dddeeB eB dede!eee f fddZ"dddeeB eB dedee fddZ#dddddeeB eB dedB de!ee ee f e!eef B dB dedee!edf  f
ddZ$dddddeeB dedB de!ee ee f e!eef B dB dedee f
ddZ%dddddeeB eB d ede!ee ee f e!eef B dB dedB dedefd!d"Z&G d#d$ d$Z'G d%d& d&eZ(dVd(ed)edee fd*d+Z)d,edefd-d.Z*dVd(ed)edee fd/d0Z+	1dWd2ee d3ed4e,defd5d6Z-	1dXd7ee d4e,de(fd8d9Z.d2ee de!eeef fd:d;Z/d7ee de!e(eef fd<d=Z0d>ede!e,ee f fd?d@Z1d>edefdAdBZ2d>edee fdCdDZ3	dYdEedFedB dee fdGdHZ4	dYdEedFedB dee!edf  fdIdJZ5dKedee fdLdMZ6dKedefdNdOZ7d>ededB fdPdQZ8e1e2e3e4e5e6e7e8e+e*dR
Z9dSedefdTdUZ:dS )Zz@Functions for working with encapsulated (compressed) pixel data.    )Iterator)BytesIOBufferedIOBaseN)packunpack)Any)config)warn_and_log)DicomBytesIODicomIOReadableBuffer)buffer_lengthreset_buffer_position)TagItemTagSequenceDelimiterTag<
endiannessbufferr   returnc                C   s   t | ttB rt| } t| d| d\}}|d> |B dkr+tdt|| dt| d| dd }|d r@td	|dkrFg S tt| |d  d| |S )
a  Return the encapsulated pixel data's basic offset table frame offsets.

    .. versionadded:: 3.0

    Parameters
    ----------
    buffer : bytes | bytearray | readable buffer
        A buffer containing the encapsulated frame data, positioned at the
        beginning of the Basic Offset Table. May be :class:`bytes`,
        :class:`bytearray` or an object with ``read()``, ``tell()`` and
        ``seek()`` methods. If the latter then after reading it will be
        positioned at the start of the item tag of the first fragment after the
        Basic Offset Table.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Returns
    -------
    list[int]
        A list of the offset positions to the first item tag of each frame, as
        measured from the end of the basic offset table.

    References
    ----------
    :dcm:`DICOM Standard, Part 5, Annex A.4<part05/sect_A.4.html#table_A.4-1>`
    HH          ` zFound unexpected tag z@ instead of (FFFE,E000) when parsing the Basic Offset Table itemLr   @The length of the Basic Offset Table item is not a multiple of 4)	
isinstancebytes	bytearrayr   r   read
ValueErrorr   list)r   r   groupelemlength r&   D/mnt/sdb/aimis/docanh/lib/python3.10/site-packages/pydicom/encaps.pyparse_basic_offsets   s   "r(   c          
      C   sB  t | ttB rt| } |  }d}g }	 zt| d| d\}}W n	 ty,   Y nkw |d> |B }|dkrt| d }dkrRt	d|  t| d  dt| d	|d }	|	d
krlt	d|  d  d|d7 }|
|  d  | |	d n|dkrnt	dt| d|  d  dq| |d ||fS )a  Return the number of fragments and their positions in `buffer`.

    .. versionadded:: 3.0

    Parameters
    ----------
    buffer : bytes | bytearray | readable buffer
        A buffer containing the encapsulated frame data, starting at the first
        byte of item tag for a fragment, such as after the end of the Basic
        Basic Offset Table. May be :class:`bytes`, :class:`bytearray` or an
        object with ``read()``, ``tell()`` and ``seek()`` methods. If the latter
        then the offset will be reset to the starting position afterwards.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Returns
    -------
    tuple[int, list[int]]
        The number of fragments and the absolute offset position of the first
        byte of the item tag for each fragment in `buffer`.
    r   Tr   r   r   r   5Unable to determine the length of the item at offset U as the end of the data has been reached - the encapsulated pixel data may be invalidr        Undefined item length at offset 3 when parsing the encapsulated pixel data fragments         ` Unexpected tag '' at offset 8 when parsing the encapsulated pixel data fragment items)r   r   r   r   tellr   r    	Exceptionlenr!   appendseekr   )
r   r   start_offsetnr_fragmentsfragment_offsetsr#   r$   tag
raw_lengthr%   r&   r&   r'   parse_fragmentsF   sF    r>   c                c   s
   t | ttB rt| } 	 zt| d| d\}}W n
 ty&   Y dS w |d> |B }|dkrmt| d }dkrLtd| 	 t| d  dt| d|d	 }|d
krftd| 	 d  d| |V  n|dkrsdS tdt
| d| 	 d  dq)a@  Yield frame fragments from the encapsulated pixel data in `buffer`.

    .. versionadded:: 3.0

    Parameters
    ----------
    buffer : bytes | bytearray | readable buffer
        A buffer containing the encapsulated frame data, starting at the first
        byte of item tag for a fragment, usually this will be after the end
        of the Basic Offset Table. May be :class:`bytes`, :class:`bytearray` or
        an object with ``read()``, ``tell()`` and ``seek()`` methods. If the
        latter than the final offset position depends on how many fragments have
        been yielded.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Yields
    ------
    bytes
        A pixel data fragment.
    Tr   r   r   r   r)   r*   r   r   r+   r,   r-   r0   r1   r2   r3   N)r   r   r   r   r   r    r5   r6   r!   r4   r   )r   r   r#   r$   r<   r=   r%   r&   r&   r'   generate_fragments   s:   r?   number_of_framesextended_offsetsr   rA   rB   .c                c   s   t | ttB rt| } t| |d}|ryt |d tr2t|d d }tt| | d|d }n|d }t |d trTt|d d }tt| | d|d }n|d }|  }t	||D ]\}	}
| 
||	 d d | |
fV  qadS |rg }d}d}t|d }t| |dD ].}||kr|| q|||d  k r|| nt|V  |d7 }|g}|t|d 7 }qt|V  dS t| |d\}}t| |d}|dkrt|fV  dS |std||krdd	 |D E dH  dS |dkrtd
d	 |D V  dS ||krQd}g }d}|D ]}|| ||dd v r,t|V  |d7 }g }q|rF||kr9d}nd}t| t|V  dS ||k rOtd dS td)a  Yield fragmented pixel data frames from `buffer`.

    .. versionadded:: 3.0

    .. note::

        When the Basic Offset Table is empty and the Extended Offset Table
        isn't supplied then more fragmented frames may be yielded than given
        by `number_of_frames` provided there are sufficient excess fragments
        available.

    Parameters
    ----------
    buffer : bytes | bytearray | readable buffer
        A buffer containing the encapsulated frame data, positioned at the first
        byte of the basic offset table. May be :class:`bytes`,
        :class:`bytearray` or an object with ``read()``, ``tell()`` and
        ``seek()`` methods. If the latter then the final position depends on
        how many fragmented frames have been yielded.
    number_of_frames : int, optional
        Required when the Basic Offset Table is empty and the Extended Offset Table
        has not been supplied. This should be the value of (0028,0008) *Number of
        Frames* or the expected number of frames in the encapsulated data.
    extended_offsets : tuple[list[int], list[int]] or tuple[bytes, bytes], optional
        The (offsets, lengths) of the Extended Offset Table as taken from
        (7FE0,0001) *Extended Offset Table* and (7FE0,0002) *Extended Offset
        Table Lengths* as either the raw encoded values or a list of their
        decoded equivalents.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Yields
    -------
    tuple[bytes, ...]
        An encapsulated pixel data frame, with the contents of the tuple the
        frame's fragmented encoded data.
    r   r   r/   Qr.   NzUnable to determine the frame boundaries for the encapsulated pixel data as there is no Basic or Extended Offset Table and the number of frames has not been suppliedc                 s   s    | ]}|fV  qd S Nr&   .0fragmentr&   r&   r'   	<genexpr>I  s    z-generate_fragmented_frames.<locals>.<genexpr>c                 s       | ]}|V  qd S rD   r&   rE   r&   r&   r'   rH   N         zThe end of the encapsulated pixel data has been reached but no JPEG EOI/EOC marker was found, the final frame may be be invalidzThe end of the encapsulated pixel data has been reached but fewer frames than expected have been found. Please confirm that the generated frame data is correctzfThe end of the encapsulated pixel data has been reached but fewer frames than expected have been foundzUnable to generate frames from the encapsulated pixel data as there are fewer fragments than frames; the dataset may be corrupt or the number of frames may be incorrect)r   r   r   r   r(   r6   r"   r   r4   zipr8   r    r?   r7   tupler>   nextr!   r	   )r   rA   rB   r   basic_offsets
nr_offsetsoffsetslengthsfragments_startoffsetr%   framecurrent_indexcurrent_offsetfinal_indexrG   r:   _	fragments
eoi_markerframe_nrmsgr&   r&   r'   generate_fragmented_frames   s   -









r_   c                c   s,    t | |||d}|D ]}d|V  qdS )a  Yield complete pixel data frames from `buffer`.

    .. versionadded:: 3.0

    .. note::

        When the Basic Offset Table is empty and the Extended Offset Table
        isn't supplied then more frames may be yielded than given by
        `number_of_frames` provided there are sufficient excess fragments
        available.

    Parameters
    ----------
    buffer : bytes | readable buffer
        A buffer containing the encapsulated frame data, starting at the first
        byte of the basic offset table. May be :class:`bytes`,
        :class:`bytearray` or an object with ``read()``, ``tell()`` and
        ``seek()`` methods. If the latter then the final offset position depends
        on the number of yielded frames.
    number_of_frames : int, optional
        Required when the Basic Offset Table is empty and the Extended Offset
        Table has not been supplied. This should be the value of (0028,0008) *Number
        of Frames* or the expected number of frames in the encapsulated data.
    extended_offsets : tuple[list[int], list[int]] or tuple[bytes, bytes], optional
        The (offsets, lengths) of the Extended Offset Table as taken from
        (7FE0,0001) *Extended Offset Table* and (7FE0,0002) *Extended Offset
        Table Lengths* as either the raw encoded values or a list of their
        decoded equivalents.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Yields
    ------
    bytes
        The encoded pixel data, one frame at a time.

    References
    ----------
    DICOM Standard Part 5, :dcm:`Annex A <part05/chapter_A.html>`
    r@       Nr_   join)r   rA   rB   r   fragmented_framesr[   r&   r&   r'   generate_frames  s   0rd   )rB   rA   r   indexc                C   s^  t | ttB rt| } |  }t| |d}|rt |d tr5t|d d }tt| | d|d }n|d }t |d trWt|d d }tt| | d|d }	n|d }	|t|krkt	d|d  d| 
|| d d | |	| }
| 
| |
S |r|t|krt	d|d  d|t|d k r||d  ||  }| 
|| d t| ||d}n| 
|d	 d t| |d}d
dd |D }
| 
| |
S t| |d\}}|dkr|dkrtt| |d}
| 
|d |
S t	d|st	d||kr2||d krt	d| d| d| 
|| d tt| |d}
| 
|d |
S t| |d}|dkrX|dkrTd
dd |D }
| 
|d |
S t	dd}g }d}|D ]+}|| ||dd v r||krd
|}
| 
|d |
  S |d7 }g }q`|r||krtd d
|}
| 
|d |
S t	d|d  d)a  Return the specified frame at `index`.

    .. versionadded:: 3.0

    .. note::

        When the Basic Offset Table is empty and the Extended Offset Table
        isn't supplied then it's possible to return a frame at a higher `index`
        than expected from the supplied `number_of_frames` value provided there
        are sufficient excess fragments available.

    Parameters
    ----------
    buffer : bytes | bytearray | readable buffer
        A buffer containing the encapsulated frame data, positioned at the first
        byte of the basic offset table. May be :class:`bytes`,
        :class:`bytearray` or an object with ``read()``, ``tell()`` and
        ``seek()`` methods. If the latter then the buffer will be reset to the
        starting position if the frame was
        returned successfully.
    index : int
        The index of the frame to be returned, starting at ``0`` for the first
        frame.
    number_of_frames : int, optional
        Required for multi-frame data when the Basic Offset Table is empty,
        the Extended Offset Table has not been supplied and there are
        multiple frames. This should be the value of (0028,0008) *Number of
        Frames* or the expected number of frames in the encapsulated data.
    extended_offsets : tuple[list[int], list[int]] or tuple[bytes, bytes], optional
        The (offsets, lengths) of the Extended Offset Table as taken from
        (7FE0,0001) *Extended Offset Table* and (7FE0,0002) *Extended Offset
        Table Lengths* as either the raw encoded values or a list of their
        decoded equivalents.
    endianness : str, optional
        If ``"<"`` (default) then the encapsulated data uses little endian
        encoding, otherwise if ``">"`` it uses big endian encoding.

    Returns
    -------
    bytes
        A single frame of encoded pixel data.


    References
    ----------
    DICOM Standard Part 5, :dcm:`Annex A <part05/chapter_A.html>`
    r   r   r/   rC   r.   z=There aren't enough offsets in the Extended Offset Table for z framesz:There aren't enough offsets in the Basic Offset Table for r`   c                 s   rI   rD   r&   rE   r&   r&   r'   rH   -  rJ   zget_frame.<locals>.<genexpr>zHFound 1 frame fragment in the encapsulated pixel data, 'index' must be 0zUnable to determine the frame boundaries for the encapsulated pixel data as there is no basic or extended offset table data and the number of frames has not been suppliedzFound z? frame fragments in the encapsulated pixel data, an 'index' of z is invalidc                 s   rI   rD   r&   rE   r&   r&   r'   rH   ^  rJ   z2The 'index' must be 0 if the number of frames is 1rK   rL   NzThe end of the encapsulated pixel data has been reached but no JPEG EOI/EOC marker was found, the returned frame data may be invalidz,There is insufficient pixel data to contain )r   r   r   r   r4   r(   r6   r"   r   r!   r8   r    r?   rb   r>   rO   r7   r	   )r   re   rB   rA   r   starting_positionrP   rQ   rR   rS   rV   r%   r[   r:   r;   r\   frame_fragmentsr]   rG   r&   r&   r'   	get_frame  s   7








ri   c                   @   s8   e Zd ZdZdeddfddZdededefd	d
ZdS )_BufferedItemar  Convenience class for a buffered encapsulation item.

    Attributes
    ----------
    buffer : io.BufferedIOBase
        The buffer containing data to be encapsulated.
    length : int
        The total length of the encapsulated item, including the item tag and
        value and any trailing padding required to bring the item data up to an
        even length.
    r   r   Nc                 C   sl   || _ t|| _| jdkrtdd| j | jd  | _t| jd | _dd| jd jddd	f| _	d
S )zCreate a new ``_BufferedItem`` instance.

        Parameters
        ----------
        buffer : io.BufferedIOBase
            The buffer containing data to be encapsulated, may be empty.
        l    z?Buffers containing more than 4294967294 bytes are not supportedr/      r`       r   little)r%   	byteorderN)
r   r   _blenr!   r%   bool_paddingrb   to_bytes_item)selfr   r&   r&   r'   __init__  s   


z_BufferedItem.__init__startsizec                 C   s*  d|  kr| j k sn td| d| j d  dd}t }||  }r|| }|dk r7| j|||  }nBd|d   krD| jk rjn n$t| j | j|d  | j|}W d   n1 sdw   Y  n| j	rw|| j d krwd}nd	}|s	 t|S |t
|7 }|| ||  }s%t|S )
a  Return data from the encapsulated frame.

        Parameters
        ----------
        start : int
            The initial position in the encapsulated item where data should be read
            from, must be greater than or equal to 0, where ``0`` is the first byte
            of the item tag.
        size : int
            The number of bytes to read from the buffer.

        Returns
        -------
        bytes
            The data read from the buffer.
        r   zInvalid 'start' value 'z&', must be in the closed interval [0, r.   ]r/   N    r`   )r%   r!   r   rs   ro   r   r   r8   r    rq   r6   extendr   )rt   rv   rw   nr_readoutr%   rU   _readr&   r&   r'   r      s8   
z_BufferedItem.read)	__name__
__module____qualname____doc__r   ru   intr   r    r&   r&   r&   r'   rj     s    rj   c                   @   s  e Zd ZdZd%dee deddfddZede	fd	d
Z
edefddZede	fddZede	fddZedefddZedee fddZedee fddZd&dedB de	fddZdefddZejfdededefdd Zdefd!d"Zdefd#d$ZdS )'EncapsulatedBufferzConvenience class for managing the encapsulation of one or more buffers
    containing compressed *Pixel Data*.

    .. versionadded:: 3.0
    Fbuffersuse_botr   Nc                    s   t |ts	tddd |D | _d| _|| _| j dg| _| j fdd| j	D  | j
| j dd t| jD | _tt dd	 | jd< d	S )
a@  Create a new class instance.

        Parameters
        ----------
        buffers : list[io.BufferedIOBase]
            The buffered pixel data frames to be encapsulated on writing the dataset.
            Only a single frame of pixel data can be in each buffer and the buffers
            must inherit from :class:`io.BufferedIODBase` and be readable and seekable.
        use_bot : bool, optional
            If ``True`` then the Basic Offset Table will include the offsets for
            each encapsulated items, otherwise no offsets will be included (default).
        zI'buffers' must be a list of objects that inherit from 'io.BufferedIOBase'c                 S      g | ]}t |qS r&   )rj   )rF   br&   r&   r'   
<listcomp>      z/EncapsulatedBuffer.__init__.<locals>.<listcomp>r   c                    s   g | ]}|t   qS r&   r6   )rF   rU   botr&   r'   r   
      c                 S   s   i | ]	\}}|d  |qS r.   r&   )rF   idxitemr&   r&   r'   
<dictcomp>  s    z/EncapsulatedBuffer.__init__.<locals>.<dictcomp>r/   N)r   r"   	TypeError_items_offset_use_botbasic_offset_table_item_offsetsrz   rR   r7   encapsulated_length	enumerate_buffersrj   r   )rt   r   r   r&   r   r'   ru     s   
zEncapsulatedBuffer.__init__c                 C   sZ   | j sdS dg}|tddt| j  |tdt| j dg| jR   d|S )z%Return an encoded Basic Offset Table.s        rl   <Ir   r   Ir`   )r   r7   r   r6   rR   rb   )rt   r   r&   r&   r'   r     s   &
z%EncapsulatedBuffer.basic_offset_tablec                 C      t dd | jD S )z>Return ``True`` if any of the encapsulated buffers are closed.c                 s   s    | ]}|j jV  qd S rD   )r   closedrF   r   r&   r&   r'   rH   &      z,EncapsulatedBuffer.closed.<locals>.<genexpr>)anyr   rt   r&   r&   r'   r   #     zEncapsulatedBuffer.closedc                 C   s*   t dt| j dgdd | jD R  S )zReturn an encoded *Extended Offset Table Lengths* value from `lengths`

        Returns
        -------
        bytes
            The encoded lengths of the frame.
        r   rC   c                 s   s    | ]}|d  V  qdS )r/   Nr&   )rF   r%   r&   r&   r'   rH   2  r   z6EncapsulatedBuffer.extended_lengths.<locals>.<genexpr>)r   r6   rS   r   r&   r&   r'   extended_lengths(  s   *
z#EncapsulatedBuffer.extended_lengthsc                 C   s    t dt| j dg| jR  S )aZ  Return an encoded *Extended Offset Table* value from `offsets`

        Returns
        -------
        bytes
            The encoded offsets to the first byte of the item tag of the first fragment
            for every frame, as measured from the first byte of the first item tag
            following the empty Basic Offset Table Item.
        r   rC   )r   r6   rR   r   r&   r&   r'   rB   4  s    z#EncapsulatedBuffer.extended_offsetsc                 C   s   t | jt| j S )z>Return the total length of the encapulated *Pixel Data* value.)r6   r   sumrS   r   r&   r&   r'   r   A  r   z&EncapsulatedBuffer.encapsulated_lengthc                 C   s   dd | j D S )z%Return the encapsulated item lengths.c                 S   s   g | ]}|j qS r&   )r%   r   r&   r&   r'   r   I  s    z.EncapsulatedBuffer.lengths.<locals>.<listcomp>)r   r   r&   r&   r'   rS   F  s   zEncapsulatedBuffer.lengthsc                    s    fddt  jD S )zGReturn the encapsulated item offsets, starting at 0 for the first item.c                    s"   g | ]\}}t  jd | qS )r   )r   rS   )rF   r   rZ   r   r&   r'   r   N  s   " z.EncapsulatedBuffer.offsets.<locals>.<listcomp>)r   rS   r   r&   r   r'   rR   K  s   zEncapsulatedBuffer.offsets    rw   c          
      C   s   | j | jkrdS |du r| jn|}d}t }||  }rytt| j| jdd }|D ]!\}\}}|| j   kr<|k rLn q+| j| | j | |}	 nq+|	sT	 t
|S |  j t|	7  _ |t|	7 }|	|	 | j | jkrs	 t
|S ||  }st
|S )aJ  Read up to `size` bytes of data from the encapsulated buffers.

        Parameters
        ----------
        size : int, optional
            The amount of data to be read, if ``None`` then all data will be returned.

        Returns
        -------
        bytes
            The data read from the encapsulated buffers.
        r`   Nr   r.   )r   r   r   r   rM   r   r   r    r6   rz   r   )
rt   rw   r{   r|   r%   iteratorr   rv   endr}   r&   r&   r'   r    P  s.   	
zEncapsulatedBuffer.readc                 C   r   )z=Return ``True`` if all the encapsulated buffers are readable.c                 s       | ]}|j  V  qd S rD   )r   readabler   r&   r&   r'   rH   y      z.EncapsulatedBuffer.readable.<locals>.<genexpr>allr   r   r&   r&   r'   r   w     zEncapsulatedBuffer.readablerU   whencec                C   s   |t jt jt jfvrtd|t jkr!|dk rtd| |}n%|t jkr4| j| }|dk r1dn|}n|t jkrF| j| }|dk rDdn|}|| _| jS )zChange the encapsulated buffers position to the given byte `offset`,
        relative to the position indicated by `whence` and return the new absolute
        position.
        z+Invalid 'whence' value, should be 0, 1 or 2r   zNegative seek 'offset' value )osSEEK_SETSEEK_CURSEEK_ENDr!   r   r   )rt   rU   r   
new_offsetr&   r&   r'   r8   {  s   




zEncapsulatedBuffer.seekc                 C   r   )z=Return ``True`` if all the encapsulated buffers are seekable.c                 s   r   rD   )r   seekabler   r&   r&   r'   rH     r   z.EncapsulatedBuffer.seekable.<locals>.<genexpr>r   r   r&   r&   r'   r     r   zEncapsulatedBuffer.seekablec                 C   s   | j S )z>Return the current stream position of the encapsulated buffers)r   r   r&   r&   r'   r4     s   zEncapsulatedBuffer.tell)F)r   )r~   r   r   r   r"   r   rp   ru   propertyr   r   r   r   rB   r   r   rS   rR   r    r   r   r   r8   r   r4   r&   r&   r&   r'   r     s,    -'r   r.   rV   r:   c                 c   s    t | }||d d krtdt|| }|d r|d7 }td||d  |D ]}| |||  V  q)||d  }| |d }|| d rK|d7 }|V  dS )a4  Yield one or more fragments from `frame`.

    .. versionadded:: 1.2

    Parameters
    ----------
    frame : bytes
        The data to fragment.
    nr_fragments : int, optional
        The number of fragments (default ``1``).

    Yields
    ------
    bytes
        The fragmented data, with all fragments as an even number of bytes
        greater than or equal to two.

    Notes
    -----

    * All items containing an encoded fragment shall be made of an even number
      of bytes greater than or equal to two.
    * The last fragment of a frame may be padded, if necessary to meet the
      sequence item format requirements of the DICOM Standard.
    * Any necessary padding may be appended after the end of image marker.
    * Encapsulated Pixel Data has the Value Representation OB.
    * Values with a VR of OB shall be padded with a single trailing NULL byte
      value (``0x00``) to achieve even length.

    References
    ----------
    DICOM Standard, Part 5, :dcm:`Section 6.2 <part05/sect_6.2.html>` and
    :dcm:`Annex A.4 <part05/sect_A.4.html>`
    r.   g       @zCToo many fragments requested (the minimum fragment size is 2 bytes)rk   r   Nry   )r6   r!   r   range)rV   r:   frame_lengthr%   rU   rG   r&   r&   r'   fragment_frame  s    #
r   rG   c                 C   s"   d}|t dt| 7 }|| 7 }|S )a  Return an itemized `fragment`.

    .. versionadded:: 1.2

    Parameters
    ----------
    fragment : bytes
        The fragment to itemize.

    Returns
    -------
    bytes
        The itemized fragment.

    Notes
    -----

    * The encoding of the item shall be in Little Endian.
    * Each fragment is encapsulated as a DICOM Item with tag (FFFE,E000), then
      a 4 byte length.
    rl   r   )r   r6   )rG   r   r&   r&   r'   itemize_fragment  s   r   c                 c   s     t | |D ]}t|V  qdS )a  Yield items generated from `frame`.

    .. versionadded:: 1.2

    Parameters
    ----------
    frame : bytes
        The data to fragment and itemise.
    nr_fragments : int, optional
        The number of fragments/items (default 1).

    Yields
    ------
    bytes
        An itemized fragment of the frame, encoded as little endian.

    Notes
    -----

    * The encoding of the items shall be in Little Endian.
    * Each fragment is encapsulated as a DICOM Item with tag (FFFE,E000), then
      a 4 byte length.

    References
    ----------
    DICOM Standard, Part 5, :dcm:`Section 7.5 <part05/sect_7.5.html>` and
    :dcm:`Annex A.4 <part05/sect_A.4.html>`
    N)r   r   )rV   r:   rG   r&   r&   r'   itemize_frame  s   r   Tframesfragments_per_framehas_botc                 C   s$  t | }t }|d |rB|d d tdd | dd D  }|dkr0td	| d
d d|tdd|  |d|  n|tdd dg}t| D ]"\}}d}	t||D ]}
|	t |
7 }	||
 q\||| |	  qQ|rtd| dg|dd R  |ddd|  < t	|S )ai  Return encapsulated `frames`.

    .. versionadded:: 1.2

    When using a compressed transfer syntax (such as RLE Lossless or one of
    JPEG formats) then any *Pixel Data* must be :dcm:`encapsulated
    <part05/sect_A.4.html>`::

      # Where `frame1`, `frame2` are single frames that have been encoded
      # using the corresponding compression method to Transfer Syntax UID
      ds.PixelData = encapsulate([frame1, frame2, ...])

    For multi-frame data each frame must be encoded separately and then all
    encoded frames encapsulated together.

    When many large frames are to be encapsulated, the total length of
    encapsulated data may exceed the maximum length available with the
    :dcm:`Basic Offset Table<part05/sect_A.4.html>` (2**31 - 1 bytes). Under
    these circumstances you can:

    * Pass ``has_bot=False`` to :func:`~pydicom.encaps.encapsulate`
    * Use :func:`~pydicom.encaps.encapsulate_extended` and add the
      :dcm:`Extended Offset Table<part03/sect_C.7.6.3.html>` elements to your
      dataset (recommended)

    Data will be encapsulated with a Basic Offset Table Item at the beginning,
    then one or more fragment items. Each item will be of even length and the
    final fragment of each frame may be padded with ``0x00`` if required.

    Parameters
    ----------
    frames : list of bytes
        The frame data to encapsulate, one frame per item.
    fragments_per_frame : int, optional
        The number of fragments to use for each frame (default ``1``).
    has_bot : bool, optional
        ``True`` to include values in the Basic Offset Table, ``False``
        otherwise (default ``True``). If `fragments_per_frame` is not ``1``
        then it's strongly recommended that this be ``True``.

    Returns
    -------
    bytes
        The encapsulated pixel data.

    References
    ----------
    DICOM Standard, Part 5, :dcm:`Section 7.5 <part05/sect_7.5.html>` and
    :dcm:`Annex A.4 <part05/sect_A.4.html>`

    See Also
    --------
    :func:`~pydicom.encaps.encapsulate_buffer`
    :func:`~pydicom.encaps.encapsulate_extended`
    :func:`~pydicom.encaps.encapsulate_extended_buffer`
    rl   r.   r/   c                 S   r   r&   r   )rF   fr&   r&   r'   r   c  r   zencapsulate.<locals>.<listcomp>Nrf   r+   z1The total length of the encapsulated frame data (zL bytes) will be greater than the maximum allowed by the Basic Offset Table (z bytes), it's recommended that you use the Extended Offset Table instead (see the 'encapsulate_extended' function for more information)r   r   s   r   r   r   )
r6   r   rz   r   r!   r   r   r   r7   r   )r   r   r   	nr_framesoutputtotalbot_offsetsiirV   itemised_lengthr   r&   r&   r'   encapsulate   s0   ;
&	0r   r   c                 C   s   t | |dS )a  Return an :class:`~pydicom.encaps.EncapsulatedBuffer` instance from `buffers`.

    .. versionadded:: 3.0

    Examples
    --------

    .. code-block:: python

        from pydicom import Dataset, FileMetaDataset
        from pydicom.encaps import encapsulate_buffer
        from pydicom.uid import JPEG2000Lossless

        # Open the compressed image frames as io.BufferedReader instances
        frame1 = open("frame1.j2k", "rb")
        frame2 = open("frame2.j2k", "rb")
        frame3 = open("frame3.j2k", "rb")

        ds = Dataset()
        ds.file_meta = FileMetaDataset()
        ds.file_meta.TransferSyntaxUID = JPEG2000Lossless

        ds.PixelData = encapsulate_buffer([frame1, frame2, frame3])

        # Write the encapsulated buffer data to file
        ds.save_as("buffered_dataset.dcm")

        # Close the buffers
        frame1.close()
        frame2.close()
        frame3.close()

    Parameters
    ----------
    buffers : list[io.BufferedIOBase]
        A list of objects inheriting :class:`io.BufferedIOBase` containing the
        compressed *Pixel Data* frames to be encapsulated.

    Returns
    -------
    EncapsulatedBuffer
        A :class:`~pydicom.encaps.EncapsulatedBuffer` instance that can be used as
        the value for a *Pixel Data* element.

    See Also
    --------
    :func:`~pydicom.encaps.encapsulate`
    :func:`~pydicom.encaps.encapsulate_extended`
    :func:`~pydicom.encaps.encapsulate_extended_buffer`
    )r   )r   )r   r   r&   r&   r'   encapsulate_buffer  s   5r   c                 C   s   t | }dd | D }dd |D }dg}t|dd D ]\}}||| | d  qtd| d	g|R  }td| d	g|R  }t| d
d||fS )a  Return encapsulated image data and values for the Extended Offset Table
    elements.

    When using a compressed transfer syntax (such as RLE Lossless or one of
    JPEG formats) then any *Pixel Data* must be :dcm:`encapsulated
    <part05/sect_A.4.html>`. When many large frames are to be encapsulated, the
    total length of encapsulated data may exceed the maximum offset available
    with the :dcm:`Basic Offset Table<part05/sect_A.4.html>` (2**32 - 1 bytes).
    Under these circumstances you can:

    * Use :func:`~pydicom.encaps.encapsulate_extended` and add the
      :dcm:`Extended Offset Table<part03/sect_C.7.6.3.html>` elements to your
      dataset (recommended)
    * Pass ``has_bot=False`` to :func:`~pydicom.encaps.encapsulate`

    Examples
    --------

    .. code-block:: python

        from pydicom import Dataset, FileMetaDataset
        from pydicom.encaps import encapsulate_extended
        from pydicom.uid import JPEG2000Lossless

        # 'frames' is a list of image frames that have been each been encoded
        # separately using the compression method corresponding to the Transfer
        # Syntax UID
        frames: list[bytes] = [...]
        out: tuple[bytes, bytes, bytes] = encapsulate_extended(frames)

        ds = Dataset()
        ds.file_meta = FileMetaDataset()
        ds.file_meta.TransferSyntaxUID = JPEG2000Lossless

        ds.PixelData = out[0]
        ds.ExtendedOffsetTable = out[1]
        ds.ExtendedOffsetTableLengths = out[2]

    Parameters
    ----------
    frames : list of bytes
        The compressed frame data to encapsulate, one frame per item.

    Returns
    -------
    bytes, bytes, bytes
        The (encapsulated frames, extended offset table, extended offset
        table lengths).

    See Also
    --------
    :func:`~pydicom.encaps.encapsulate`
    :func:`~pydicom.encaps.encapsulate_buffer`
    :func:`~pydicom.encaps.encapsulate_extended_buffer`
    c                 S   r   r&   r   )rF   rV   r&   r&   r'   r     r   z(encapsulate_extended.<locals>.<listcomp>c                 S   s   g | ]}||d   qS )rk   r&   rF   r   r&   r&   r'   r     r   r   Nrf   r/   r   rC   F)r   )r6   r   r7   r   r   )r   r   frame_lengthsframe_offsetsr   r%   rR   rS   r&   r&   r'   encapsulate_extended  s   8r   c                 C   s   t | }||j|jfS )a  Return :class:`~pydicom.encaps.EncapsulatedBuffer` as well as encoded offsets
    and lengths for the Extended Offset Table elements.

    .. versionadded:: 3.0

    Examples
    --------

    .. code-block:: python

        from pydicom import Dataset, FileMetaDataset
        from pydicom.encaps import encapsulate_extended_buffer
        from pydicom.uid import JPEG2000Lossless

        # Open the compressed image frames as io.BufferedReader instances
        frame1 = open("frame1.j2k", "rb")
        frame2 = open("frame2.j2k", "rb")
        frame3 = open("frame3.j2k", "rb")

        out: tuple[EncapsulatedBuffer, bytes, bytes] = (
            encapsulate_extended_buffer([frame1, frame2, frame3])
        )

        ds = Dataset()
        ds.file_meta = FileMetaDataset()
        ds.file_meta.TransferSyntaxUID = JPEG2000Lossless

        ds.PixelData = out[0]
        ds.ExtendedOffsetTable = out[1]
        ds.ExtendedOffsetTableLengths = out[2]

        # Write the encapsulated buffer data to file
        ds.save_as("buffered_dataset.dcm")

        # Close the buffers
        frame1.close()
        frame2.close()
        frame3.close()

    Parameters
    ----------
    buffers : list[io.BufferedIOBase]
        A list of objects inheriting :class:`io.BufferedIOBase` containing the
        compressed *Pixel Data* frames to be encapsulated.

    Returns
    -------
    tuple[EncapsulatedBuffer, bytes, bytes]
        The (:class:`~pydicom.encaps.EncapsulatedBuffer`, extended offset table,
        extended offset table lengths).

    See Also
    --------
    :func:`~pydicom.encaps.encapsulate`
    :func:`~pydicom.encaps.encapsulate_buffer`
    :func:`~pydicom.encaps.encapsulate_extended`
    )r   rB   r   )r   ebr&   r&   r'   encapsulate_extended_buffer  s   <r   fpc                    s    j stdt  }|dkrtd| d  }|d r%tdg }|dkr0|d | fdd	t|d D  t||fS )
at  Return a list of the fragment offsets from the Basic Offset Table.

    .. deprecated:: 3.0

        This function will be removed in v4.0, please use
        :func:`~pydicom.encaps.parse_basic_offsets` instead.

    **Basic Offset Table**

    The Basic Offset Table Item must be present and have a tag (FFFE,E000) and
    a length, however it may or may not have a value.

    Basic Offset Table with no value
    ::

        Item Tag   | Length    |
        FE FF 00 E0 00 00 00 00

    Basic Offset Table with value (2 frames)
    ::

        Item Tag   | Length    | Offset 1  | Offset 2  |
        FE FF 00 E0 08 00 00 00 00 00 00 00 10 00 00 00

    For single or multi-frame images with only one frame, the Basic Offset
    Table may or may not have a value. When it has no value then its length
    shall be ``0x00000000``.

    For multi-frame images with more than one frame, the Basic Offset Table
    should have a value containing concatenated 32-bit unsigned integer values
    that are the byte offsets to the first byte of the Item tag of the first
    fragment of each frame as measured from the first byte of the first item
    tag following the Basic Offset Table Item.

    All decoders, both for single and multi-frame images should accept both
    an empty Basic Offset Table and one containing offset values.

    .. versionchanged:: 1.4

        Changed to return (is BOT empty, list of offsets).

    Parameters
    ----------
    fp : filebase.DicomIO
        The encapsulated pixel data positioned at the start of the Basic Offset
        Table. ``fp.is_little_endian`` should be set to ``True``.

    Returns
    -------
    bool, list of int
        Whether or not the BOT is empty, and a list of the byte offsets
        to the first fragment of each frame, as measured from the start of the
        first item following the Basic Offset Table item.

    Raises
    ------
    ValueError
        If the Basic Offset Table item's tag is not (FFEE,E000) or if the
        length in bytes of the item's value is not a multiple of 4.

    References
    ----------
    DICOM Standard, Part 5, :dcm:`Annex A.4 <part05/sect_A.4.html>`
    "'fp.is_little_endian' must be Truer   r1   z*' when parsing the Basic Offset Table itemr   r   r   c                 3   s    | ]}   V  qd S rD   )read_ULr   r   r&   r'   rH     r   z%_get_frame_offsets.<locals>.<genexpr>)	is_little_endianr!   r   read_tagr   r7   rz   r   rp   )r   r<   r%   rR   r&   r   r'   _get_frame_offsetsG  s"   A

 r   c                 C   s   | j stdt| d S )zReturn the number of fragments in `fp`.

    .. deprecated:: 3.0

        This function will be removed in v4.0, please use
        :func:`~pydicom.encaps.parse_fragments` instead.
    r   r   )r   r!   r>   r   r&   r&   r'   _get_nr_fragments  s   r   c                 c   s"    | j stdt| E dH  dS )ap  Yield the encapsulated pixel data fragments.

    .. deprecated:: 3.0

        This function will be remove in v4.0, please use
        :func:`~pydicom.encaps.generate_fragments` instead.

    For compressed (encapsulated) Transfer Syntaxes, the (7FE0,0010) *Pixel
    Data* element is encoded in an encapsulated format.

    **Encapsulation**

    The encoded pixel data stream is fragmented into one or more Items. The
    stream may represent a single or multi-frame image.

    Each *Data Stream Fragment* shall have tag of (FFFE,E000), followed by a 4
    byte *Item Length* field encoding the explicit number of bytes in the Item.
    All Items containing an encoded fragment shall have an even number of bytes
    greater than or equal to 2, with the last fragment being padded if
    necessary.

    The first Item in the Sequence of Items shall be a 'Basic Offset Table',
    however the Basic Offset Table item value is not required to be present.
    It is assumed that the Basic Offset Table item has already been read prior
    to calling this function (and that `fp` is positioned past this item).

    The remaining items in the Sequence of Items are the pixel data fragments
    and it is these items that will be read and returned by this function.

    The Sequence of Items is terminated by a (FFFE,E0DD) *Sequence Delimiter
    Item* with an Item Length field of value ``0x00000000``. The presence
    or absence of the *Sequence Delimiter Item* in `fp` has no effect on the
    returned fragments.

    *Encoding*

    The encoding of the data shall be little endian.

    Parameters
    ----------
    fp : filebase.DicomIO
        The encoded (7FE0,0010) *Pixel Data* element value, positioned at the
        start of the item tag for the first item after the Basic Offset Table
        item. ``fp.is_little_endian`` should be set to ``True``.

    Yields
    ------
    bytes
        A pixel data fragment.

    Raises
    ------
    ValueError
        If the data contains an item with an undefined length or an unknown
        tag.

    References
    ----------
    DICOM Standard Part 5, :dcm:`Annex A.4 <part05/sect_A.4.html>`
    r   N)r   r!   r?   r   r&   r&   r'   _generate_pixel_data_fragment  s   =r   
bytestreamr   c                 c   s$    t | |dD ]}d|V  qdS )a]  Yield complete frames from `buffer` as :class:`bytes`.

    .. deprecated:: 3.0

        This function will be remove in v4.0, please use
        :func:`~pydicom.encaps.generate_frames` instead

    Parameters
    ----------
    bytestream : bytes
        The value of the (7FE0,0010) *Pixel Data* element from an encapsulated
        dataset. The Basic Offset Table item should be present and the
        Sequence Delimiter item may or may not be present.
    nr_frames : int, optional
        Required for multi-frame data when the Basic Offset Table is empty
        and there are multiple frames. This should be the value of (0028,0008)
        *Number of Frames*.

    Yields
    ------
    bytes
        A frame contained in the encapsulated pixel data.

    References
    ----------
    DICOM Standard Part 5, :dcm:`Annex A <part05/chapter_A.html>`
    rA   r`   Nra   )r   r   rV   r&   r&   r'   _generate_pixel_data_frame  s   r   c                 c   s    t | |dE dH  dS )a]	  Yield an encapsulated pixel data frame.

    .. deprecated:: 3.0

        Please use :func:`~pydicom.encaps.generate_fragmented_frames` instead.

    For the following transfer syntaxes, a fragment may not contain encoded
    data from more than one frame. However data from one frame may span
    multiple fragments.

    * 1.2.840.10008.1.2.4.50 - JPEG Baseline (Process 1)
    * 1.2.840.10008.1.2.4.51 - JPEG Baseline (Process 2 and 4)
    * 1.2.840.10008.1.2.4.57 - JPEG Lossless, Non-Hierarchical (Process 14)
    * 1.2.840.10008.1.2.4.70 - JPEG Lossless, Non-Hierarchical, First-Order
      Prediction (Process 14 [Selection Value 1])
    * 1.2.840.10008.1.2.4.80 - JPEG-LS Lossless Image Compression
    * 1.2.840.10008.1.2.4.81 - JPEG-LS Lossy (Near-Lossless) Image Compression
    * 1.2.840.10008.1.2.4.90 - JPEG 2000 Image Compression (Lossless Only)
    * 1.2.840.10008.1.2.4.91 - JPEG 2000 Image Compression
    * 1.2.840.10008.1.2.4.92 - JPEG 2000 Part 2 Multi-component Image
      Compression (Lossless Only)
    * 1.2.840.10008.1.2.4.93 - JPEG 2000 Part 2 Multi-component Image
      Compression

    For the following transfer syntaxes, each frame shall be encoded in one and
    only one fragment.

    * 1.2.840.10008.1.2.5 - RLE Lossless

    Parameters
    ----------
    bytestream : bytes
        The value of the (7FE0,0010) *Pixel Data* element from an encapsulated
        dataset. The Basic Offset Table item should be present and the
        Sequence Delimiter item may or may not be present.
    nr_frames : int, optional
        Required for multi-frame data when the Basic Offset Table is empty
        and there are multiple frames. This should be the value of (0028,0008)
        *Number of Frames*.

    Yields
    -------
    tuple of bytes
        An encapsulated pixel data frame, with the contents of the
        :class:`tuple` the frame's fragmented data.

    Notes
    -----
    If the Basic Offset Table is empty and there are multiple fragments per
    frame then an attempt will be made to locate the frame boundaries by
    searching for the JPEG/JPEG-LS/JPEG2000 EOI/EOC marker (``0xFFD9``). If the
    marker is not present or the pixel data hasn't been compressed using one of
    the JPEG standards then the generated pixel data may be incorrect.

    References
    ----------
    DICOM Standard Part 5, :dcm:`Annex A <part05/chapter_A.html>`
    r   N)r_   )r   r   r&   r&   r'   _generate_pixel_data  s   =r   datac                 C   s^   t | !}d|_t|}g }	 t|}|sn|| q|W  d   S 1 s(w   Y  dS )a	  Read encapsulated data and return a list of bytes.

    .. deprecated:: 3.0

        This function will be removed in v4.0, Please use
        :func:`~pydicom.encaps.generate_frames` for generating frame
        data or :func:`~pydicom.encaps.generate_fragments` for generating
        fragment data.

    Parameters
    ----------
    data : bytes
        The encapsulated data, typically the value from ``Dataset.PixelData``.

    Returns
    -------
    list of bytes
        All fragments as a list of ``bytes``.
    TN)r
   r   
_read_itemr7   )r   r   BasicOffsetTableseqr   r&   r&   r'   _decode_data_sequenceU  s   

	$r   c                 C   s   d t| S )a  Read encapsulated data and return the fragments as one continuous bytes.

    .. deprecated:: 3.0

        This function will be removed in v4.0, Please use
        :func:`~pydicom.encaps.generate_frames` for generating frame
        data or :func:`~pydicom.encaps.generate_fragments` for generating
        fragment data.

    Parameters
    ----------
    data : bytes
        The encapsulated pixel data fragments.

    Returns
    -------
    bytes
        All fragments concatenated together.
    r`   )rb   r   )r   r&   r&   r'   _defragment_data|  s   r   c                 C   s   t j}z|  }W n
 ty   Y dS w |tkr8|  }|d|  d | |dkr6|d||  d  dS |t	krL|dt	|  d  |  }n|  }|d|  d | |d	krkt
d
|  d d| |}|S )ax  Read and return a single Item in the fragmented data stream.

    .. deprecated:: 3.0

        This function will be removed in v4.0, please use
        :func:`~pydicom.encaps.generate_fragments` instead.

    Parameters
    ----------
    fp : filebase.DicomIO
        The file-like to read the item from.

    Returns
    -------
    bytes
        The Item's raw bytes.
    Nz%%04x: Sequence Delimiter, length 0x%xr/   r   zFExpected 0x00000000 after delimiter, found 0x%x, at data position 0x%xr   z/Expected Item with tag %s at data position 0x%xz%04x: Item, length 0x%xr+   zCEncapsulated data fragment had Undefined Length at data position 0xx)r   loggerr   EOFErrorr   r   debugr4   warningr   r!   r    )r   r   r<   r%   	item_datar&   r&   r'   r     s>   


r   )
get_frame_offsetsget_nr_fragmentsgenerate_pixel_data_fragmentgenerate_pixel_data_framegenerate_pixel_datadecode_data_sequencedefragment_data	read_itemitemise_frameitemise_fragmentnamec                 C   s:   | t v rtjst|  dt t |  S tdtd| )Nz* is deprecated and will be removed in v4.0zmodule z has no attribute )_DEPRECATEDr   _use_futurer	   DeprecationWarningAttributeErrorr~   )r   r&   r&   r'   __getattr__  s   r   r   )r.   T)TrD   );r   collections.abcr   ior   r   r   structr   r   typingr   pydicomr   pydicom.miscr	   pydicom.filebaser
   r   r   pydicom.fileutilr   r   pydicom.tagr   r   r   r   r   strr"   r   r(   rN   r>   r?   r_   rd   ri   rj   r   r   r   r   rp   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r&   r&   r&   r'   <module>   s"  

5

F

?
"
 C"
>
"
 C^ A? "
h
 8G
A[D
#
@'>