U
    g                     @   s  d Z ddlZddlZddlZddl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mZmZmZmZ ddlmZmZ ddlmZmZ dd	lmZmZmZ dd
lmZ dddddddddddddddddddgZe e!Z"ej#G dd dej$Z%edd G d!d dZ&edd G d"d dZ'ee' d#d$dZ(dEej)e*ee' d%d&dZ+ej#G d'd dej$Z,edd G d(d dZ-G d)d dZ.edd G d*d dZ/e0e/ee/ d+d,d-Z1ee/ d.d/d0Z2ee/ d1d2d3Z3G d4d dZ4G d5d dejZ5d6d7 Z6d8d Z7G d9d dej8Z9d:d; Z:d<d= Z;G d>d dZ<eee0e0f ee0ee0e0f f f Z=ej>ej)fZ?G d?d dZ@d@dA ZAeee0e0f e0f dBdCdZBG dDd dej)ZCdS )Fz
Internal utilities to handle the processing of cross-reference data and
document trailer data.

This entire module is considered internal API.
    N)	dataclass)BytesIO)chain)DictIteratorListOptionalSetTupleUnion)genericmisc)EncryptedObjAccesspdf_name)PdfReadErrorPdfStrictReadErrorpeek)
PdfHandler	XRefCacheXRefBuilderXRefType	XRefEntryObjStreamRefObjectHeaderReadErrorXRefSectionXRefSectionDataXRefSectionTypeXRefSectionMetaInfoTrailerDictionaryread_object_headerparse_xref_streamparse_xref_tablewrite_xref_tableObjectStream
XRefStreamOBJSTREAM_FORBIDDENPositionDictc                   @   s(   e Zd ZdZe Ze Ze ZdS )r   z5
    Different types of cross-reference entries.
    N)	__name__
__module____qualname____doc__enumautoFREESTANDARDIN_OBJ_STREAM r0   r0   :/tmp/pip-unpacked-wheel-owvgwkas/pyhanko/pdf_utils/xref.pyr   /   s
   T)frozenc                   @   s"   e Zd ZU dZeed< eed< dS )r   z?
    Identifies an object that's part of an object stream.
    obj_stream_idZix_in_streamN)r'   r(   r)   r*   int__annotations__r0   r0   r0   r1   r   E   s   
c                   @   sB   e Zd ZU dZeed< eeee	f  ed< eed< dZ
eed< dS )r   zA
    Value type representing a single cross-reference entry.
    	xref_typelocationidnumr   
generationN)r'   r(   r)   r*   r   r5   r   r   r4   r   r9   r0   r0   r0   r1   r   V   s   
returnc           	      c   s~  t |  | dtj tj| }t |  | dtj tj| }t |  | dtj td|D ]}| 	d}|d dkr| dtj | 	d}qx|d dkr| dtj |dd 
d	\}}}|d
krttjt||t|dV  n"|dkrttjd|t|dV  |d7 }qjt |  | dtj | 	d}|dkrjt |  | dtj dS | dtj qdS )a  
    Parse a single cross-reference table and yield its entries one by one.

    This is internal API.

    :param stream:
        A file-like object pointed to the start of the cross-reference table.
    :return:
        A generator object yielding :class:`.XRefEntry` objects.
    r      s   
is   0123456789tN          n)r6   r7   r8   r9      f      s   traileri)r   read_non_whitespaceseekosSEEK_CURr   NumberObjectread_from_streamrangereadsplitr   r   r.   r4   r-   )	streamnumsizeZcntlineoffsetr9   markerZ
trailertagr0   r0   r1   r!   q   sN    










)xref_streamstrictr;   c                 #   sN  t | j| dd| dg}| d  fdd}d}t|D ]\}}||ks\t|| }t||| D ]}|d}|dkr|d}	|d}
ttj	||	|
d	V  qr|dkr|d}|d}t
||}	ttj||	d
V  qr|dkr|d |d}ttj||ddV  qr|d |d qrqHtddkrJrJtddS )aQ  
    Parse a single cross-reference stream and yield its entries one by one.

    This is internal API.

    :param xref_stream:
        A :class:`~generic.StreamObject`.
    :param strict:
        Boolean indicating whether we're running in strict mode.
    :return:
        A generator object yielding :class:`.XRefEntry` objects.
    /Indexr   /Size/Wc                    sd    |  }|dkrP |}t||kr0t||S rPtd| dt| d| dkr\dS dS d S )Nr   zBXRef stream ended prematurely; incomplete entry: expected to read z bytes, but only got .rB   )rK   lenconvert_to_intr   r   )ixZentry_widthdZentry_sizesstream_datarT   r0   r1   	get_entry   s    

z$parse_xref_stream.<locals>.get_entryrB      )r6   r8   r7   r9   )r6   r8   r7   N)r6   r8   r9   r7   z'Trailing data in cross-reference stream)r   datagetr   Z	pair_iterAssertionErrorrJ   r   r   r.   r   r/   r-   rY   rK   r   )rS   rT   Z	idx_pairsr_   Zlast_endstartrO   rN   r6   r7   r9   Z
objstr_numZ
objstr_idxnext_generationr0   r]   r1   r       sR    






c                   @   s,   e Zd Ze Ze Ze Ze ZdS )r   N)	r'   r(   r)   r+   r,   r.   STREAMHYBRID_MAINHYBRID_STREAMr0   r0   r0   r1   r     s   c                   @   sD   e Zd ZU eed< eed< eed< eed< eed< eej ed< dS )r   xref_section_typerO   declared_startxrefstart_locationend_location
stream_refN)	r'   r(   r)   r   r5   r4   r   r   	Referencer0   r0   r0   r1   r     s   
c                   @   sx   e Zd ZdZd dddZeejejf e	ee
ef  dddZee edd	d
Zee eedddZdd ZdS )r   zn
    Internal class for bookkeeping on a single cross-reference section,
    independently of the others.
    selfc                 C   s,   i | _ i | _i | _t | _t | _d | _d S N)freedstandard_xrefsxrefs_in_objstmsetexplicit_refs_in_revisionobj_streams_usedhybridro   r0   r0   r1   __init__G  s    zXRefSectionData.__init__)refr;   c                 C   s   |j dkr.z| j|j W S  tk
r,   Y nX | j|jd }|d k	rd|d |j kr\|d S t|| j|jd }|d k	r|j |d krd S | jd k	r| jj	|S t|d S )Nr   rB   )
r9   rt   r8   KeyErrorrs   rb   rr   rx   	xref_datatry_resolve)rp   rz   Zstd_refZfreed_next_generationr0   r0   r1   r}   Q  s&    

zXRefSectionData.try_resolve)entriesrT   c           	      C   s  d}|D ]
}|j }|j}t||}|dkrH|rtd| d| dq|j dkrTq|jtjkr|j}t|t	stt
||f| j|< | j||f q|jtjkr|dkst
|j}t|tst
|| j|< | j|j | j|df q|jtjkr|| j|< | j||d f q|S )Nr   i  zIllegal generation z for object ID rX   rB   )r8   r9   maxr   r6   r   r.   r7   
isinstancer4   rc   rs   rv   addr/   r   rt   rw   r3   r-   rr   )	rp   r~   rT   Z
highest_idZ
xref_entryr8   r9   rQ   locr0   r0   r1   process_entries~  s:    




zXRefSectionData.process_entries)r~   xref_meta_inforT   c                 C   s$   t  }|j||d t||| _d S )NrT   )r   r   r   rx   )rp   r~   r   rT   rx   r0   r0   r1   process_hybrid_entries  s    z&XRefSectionData.process_hybrid_entriesc                 c   s.   | j  D ]\}\}}|dkr
||fV  q
d S )Nr   )rs   items)rp   r8   r9   _r0   r0   r1   higher_generation_refs  s    z&XRefSectionData.higher_generation_refsN)r'   r(   r)   r*   ry   r   r   rn   IndirectObjectr   r4   r   r}   r   r   boolr   r   r   r   r0   r0   r0   r1   r   A  s   -$
c                   @   s"   e Zd ZU dZeed< eed< dS )r   zg
    Describes a cross-reference section and describes how it is serialised into
    the PDF file.
    	meta_infor|   N)r'   r(   r)   r*   r   r5   r   r0   r0   r0   r1   r     s   
)r[   sectionall_sectionsc                 C   s  |j j}|rH|j|jf}|| d d  D ]}|j}||jkr(tdq(|jj	 D ]6\}}|dkrx| dkrxtd| t
|d k r|| d  }	|	jjd k	r|	jjjnd }
|
d k	r||
jksT||
jkrqTd }|| d d  D ]}|j}||jkrd}nBz*|j| \}}|dks||k r"|}W n tk
r:   Y nX |d k	r|dkrhtd| d| dqtd| d| d	|d  d
qqTd S )NrB   z9XRef stream objects must not be clobbered in strict mode.r   ztIn strict mode, a free xref with next generation 0 is onlypermitted in an initial revision due to unclear semantics.Object with id zA was listed as dead, but is reused later, with generation number rX   z and generation z was found after z was freed.)r   rm   r8   r9   r|   rv   r   r   rr   r   rY   rx   rs   rt   r{   )r[   r   r   Zxstream_refas_tupleZsection_ra   r8   Zexpected_next_generationZnext_sectionrx   Zimproper_generationsuccre   r   r0   r0   r1   _check_freed_refs  s`    



r   )sectionsc                 c   s*   | D ] }|j jd k	r|j jV  |V  qd S rq   )r|   rx   )r   r   r0   r0   r1   _with_hybrids/  s    
r   )r   c                 C   sH  t | D ]\}}t|||  qtt| }d}t |D ]
\}}t|j }|jj}|jj	}||k r|t
jkrtd| d| dt||}|jj	t
jk}|D ]\}	}
t|d | }|rt|}|	|jjkr q6|D ]@}z |jj|	 }||
k rW  qW q tk
r   Y qY qX qtd|	 d|
 d|
d  dqq6d S )	Nr   zEXRef section sizes must be nondecreasing; found XRef section of size z after section of size rX   r   z( has an orphaned generation: generation z7 was not preceded by a free instruction for generation rB   )	enumerater   listr   ru   r|   r   r   rO   ri   r   rh   r   r   r   reversednextrr   r{   )r   r[   r   Zexpanded_sectionsZ	prev_sizeZ
higher_genszr6   Zis_hybrid_streamr8   r9   Z	precedingprecre   r0   r0   r1   _check_xref_consistency6  s>    
	

r   c                   @   sN   e Zd ZdZeeedddZdd Zeddd	Z	edd
dZ
dd ZdS )r   
   )handlerrT   last_startxrefc                 C   s>   || _ || _|| _|| _g | _t | _t|| j_	d| _
d S )NF)r   rM   rT   r   r   r   trailerr   TrailerReferencecontainer_refhas_xref_stream)rp   r   rM   rT   r   r0   r0   r1   ry   z  s    zXRefBuilder.__init__c                 C   sX   | j }t|| jd\}}tj||| jd}tj||}||_|	ddksPt
||fS )Nr   Zpdf/Type/XRef)rM   r   rT   r   rn   r   StreamObjectrI   r   raw_getrc   )rp   rM   r8   r9   xrefstream_ref
xrefstreamr0   r0   r1   _read_xref_stream_object  s     z$XRefBuilder._read_xref_stream_object)rj   c                 C   s   | j }| }|  \}}t }|jt|| jd| jd ttj	t
|d||| |d}| jt|| | j| |dS )Nr   rV   ri   rO   rj   rk   rl   rm   /Prev)rM   tellr   r   r   r    rT   r   r   rf   r4   r   r   appendr   r   add_trailer_revisionrb   )rp   rj   rM   rk   r   r   xref_section_datar   r0   r0   r1   _read_xref_stream  s&    
zXRefBuilder._read_xref_streamc              	   C   sx  | j }| }t }|jt|| jd}| }tj|t	| j
}t|tjsVtt|d}| jr||krtd| d|d  dzt|d}	W n ttfk
r   d }	Y nX |	d k	r2| }
||	 |  \}}ttjt|d|	|	| |d}|jt||| jd ||
 tj}n
d }tj}t||||||d}| jt|| | j | |!d	S )
Nr   rV   z9Xref table size mismatch: table allocated object with id z, but according to the trailer rB   z" is the maximal allowed object id./XRefStmr   r   )"rM   r   r   r   r!   rT   r   DictionaryObjectrI   r   r   r   rc   r4   r   r   r   r{   
ValueErrorrE   r   r   r   rh   r   r    rg   r.   r   r   r   r   r   rb   )rp   rj   rM   Z
xref_startr   ZhighestZxref_endZnew_trailerZdeclared_sizeZhybrid_xref_stm_locZ
stream_posrm   r   Zhybrid_stream_metar6   r   r0   r0   r1   _read_xref_table  sl      




zXRefBuilder._read_xref_tablec           	   
   C   s`  | j }| j }}d}|d k	r>| jr(|s2|| jkr:td|sB|}|| t|rht	d |
 }|d}|dkr|d}|d d dkrtd	| |}n| r$|d
tj z| |}W nL tk
r } z,tj	d|d t||}|d7 }W Y qW 5 d }~X Y nX d| _nt||}|d7 }qd}qtt| j}| jr\t| |S )Nr   zFailed to locate xref sectionz>Encountered unexpected whitespace when looking for xref streamrB      x      s   refzxref table read errorr<   z;Failed to read xref stream header, attempting to correct...)exc_infoT)rM   r   rT   	err_limitr   rE   r   skip_over_whitespaceloggerdebugr   rK   r   r   isdigitrF   rG   r   r   _attempt_startxref_correctionr   r   r   r   r   )	rp   rM   rj   	startxrefZ	err_countxrz   eZchrono_sectionsr0   r0   r1   
read_xrefs  sR    








zXRefBuilder.read_xrefsN)r'   r(   r)   r   r   r   r4   ry   r   r   r   r   r0   r0   r0   r1   r   w  s     Ec                   @   s   e Zd ZdS )r   N)r'   r(   r)   r0   r0   r0   r1   r   *  s   c                 C   s   d}t |  |t | O }t | }t|}|t | O }t | }t|}| d t j| dd |r|rtd| d|  ||fS )NFr   T)Z	seek_backz.Superfluous whitespace found in object header  )	r   Zskip_over_commentr   Zread_until_whitespacer4   rK   rD   r   warning)rM   rT   extraZidnum_bytesr8   Zgeneration_bytesr9   r0   r0   r1   _read_object_header.  s    



r   c              
   C   sL   |   }zt| |W S  tk
rF } ztd| |W 5 d }~X Y nX d S )Nz Failed to read object header at )r   r   r   r   )rM   rT   posr   r0   r0   r1   r   F  s    c                   @   s   e Zd ZdZdddddddhZd d	d
dZejdddZdd Z	e
jdfe
dddZdd Zdd Zd&ejdddZdd Zdd Zd d! Zd"d# Zd'd$d%ZdS )(r   z
    The standard mandates that each trailer shall contain
    at least all keys used in the preceding trailer, even if unmodified.
    Of course, we cannot trust documents to actually follow this rule, so
    this class implements fallbacks.
    z/Lengthz/Filterz/DecodeParmsrW   r   rU   r   ro   c                 C   s   g | _ t | _d S rq   )_trailer_revisionsr   r   _new_changesro   r0   r0   r1   ry   d  s    zTrailerDictionary.__init__)trailer_dictc                 C   s   | j | d S rq   )r   r   )rp   r   r0   r0   r1   r   j  s    z&TrailerDictionary.add_trailer_revisionc                 C   s   |  | S rq   )r   Z
get_objectrp   itemr0   r0   r1   __getitem__m  s    zTrailerDictionary.__getitem__Ndecryptc              	   C   s   | j }|d kr6z| j||W S  tk
r2   Y qRX nt|d | }||d  }|| jkrl|d ||S |D ]0}z|||W   S  tk
r   Y qpY qpX qpt|d S )NrB   r   )r   r   r   r{   rY   non_trailer_keys)rp   keyr   revisionZ	revisionsr   r0   r0   r1   r   r  s     
zTrailerDictionary.raw_getc                 C   s   || j |< d S rq   )r   )rp   r   valuer0   r0   r1   __setitem__  s    zTrailerDictionary.__setitem__c                 C   s0   z| j |= W n tk
r*   tdY nX d S )NzICannot remove existing entries from trailer dictionary, only update them.)r   r{   r   ZPdfErrorr   r0   r0   r1   __delitem__  s    zTrailerDictionary.__delitem__r:   c                 C   sh   | j }|d k	r || d d  }tdd t|D }|d krL|| j | jD ]}||d  qR|S )NrB   c                 S   s$   i | ]}|  D ]\}}||qqS r0   )r   ).0r   kvr0   r0   r1   
<dictcomp>  s
   
  z-TrailerDictionary.flatten.<locals>.<dictcomp>)r   r   r   r   updater   r   pop)rp   r   Zrelevant_revisionsr   r   r0   r0   r1   flatten  s    
zTrailerDictionary.flattenc                 C   s2   z| j |tjd W dS  tk
r,   Y dS X d S )Nr   TF)r   r   ZPROXYr{   r   r0   r0   r1   __contains__  s
    zTrailerDictionary.__contains__c                 C   s   t t| jf| j S rq   )	frozensetr   r   r   ro   r0   r0   r1   keys  s    zTrailerDictionary.keysc                 C   s   t |  S rq   )iterr   ro   r0   r0   r1   __iter__  s    zTrailerDictionary.__iter__c                 C   s   |    S rq   )r   r   ro   r0   r0   r1   r     s    zTrailerDictionary.itemsc                 C   s   t dd S )Nz3TrailerDictionary object cannot be written directly)NotImplementedError)rp   rM   r   r   r0   r0   r1   write_to_stream  s    z!TrailerDictionary.write_to_stream)N)NN)r'   r(   r)   r*   r   ry   r   r   r   r   r   ZTRANSPARENTr   r   r   r   r   r   r   r   r   r0   r0   r0   r1   r   P  s2   


c                 C   s   |  dtj | d}|d}|dkr6|d|  S d}|  ||  | d| }|d}|dkr|  || | d	  t|  |  }t	dD ]}| d	
 r||   S qtd
d S )Nir=   s   xrefr<   r      r`      
rB   z/Could not find xref table at specified location)rE   rF   rG   rK   findrfindr   r   r   rJ   r   r   )rM   r   tmpZxref_locZxrefstm_readbackZnewline_locZ
line_startZlookr0   r0   r1   r     s"    



r   c                    sF    dkr(t d  }td||  d S t fddt| D S d S )N   z>qr   c                 3   s&   | ]\}}|d  | d   V  qdS )   rB   Nr0   )r   r[   digitrO   r0   r1   	<genexpr>  s     z!convert_to_int.<locals>.<genexpr>)bytesstructunpacksumr   )r\   rO   paddingr0   r   r1   rZ     s    rZ   c                   @   s   e Zd ZdZee dddZedd Ze	j
ddd	Zd
d Ze	j
dddZedddZedddZee	j
 dddZee	j
 dddZedddZeeeef  dddZdd ZeedddZdS ) r   a/  
    Internal class to parse & store information from the xref section(s) of a
    PDF document.

    Stores both the most recent status of all xrefs in addition to their
    historical values.

    All members of this class are considered internal API and are subject
    to change without notice.
    )xref_sectionsc                 C   s    || _ || _dd |D | _d S )Nc                 S   s    h | ]}|j jd k	r|j jqS rq   )r   rm   r   r   r0   r0   r1   	<setcomp>   s   z%XRefCache.__init__.<locals>.<setcomp>)reader_xref_sectionsZxref_stream_refs)rp   r   r   r0   r0   r1   ry     s
    zXRefCache.__init__c                 C   s
   t | jS rq   )rY   r   ro   r0   r0   r1   total_revisions  s    zXRefCache.total_revisions)rz   c              	   C   s^   t t| jD ]B\}}z$|j| t| jd | W   S  tk
rN   Y qX qt|d S NrB   )r   r   r   r|   r}   rY   r{   )rp   rz   r[   r   r0   r0   r1   get_last_change
  s    zXRefCache.get_last_changec                    s     j | } fdd|jjD S )Nc                    s   h | ]}t j| jd qS r   r   rn   r   )r   Z	objstm_idro   r0   r1   r     s   z3XRefCache.object_streams_used_in.<locals>.<setcomp>)r   r|   rw   rp   r   r   r0   ro   r1   object_streams_used_in  s    

z XRefCache.object_streams_used_inc              	   C   sX   t | jD ]@\}}z"|j|}|d k	r2|W   S W q
 tk
rH   Y q
X q
t|d S rq   )r   r   r|   r}   r{   )rp   rz   r[   r   resultr0   r0   r1   get_introducing_revision  s    z"XRefCache.get_introducing_revisionr:   c                 C   s   | j | }|jS rq   )r   r   r   r0   r0   r1   get_xref_container_info&  s    
z!XRefCache.get_xref_container_infoc                 C   s   | j | }|jS rq   )r   r|   r   r0   r0   r1   get_xref_data*  s    
zXRefCache.get_xref_datac                    sN    j | } fdd|jjD }|jj}|dk	rJ| fdd|jjD O }|S )a  
        Look up the object refs for all objects explicitly added or overwritten
        in a given revision.

        :param revision:
            A revision number. The oldest revision is zero.
        :return:
            A set of Reference objects.
        c                    s"   h | ]\}}t j|| jd qS r   r   r   r8   r9   ro   r0   r1   r   9  s   z6XRefCache.explicit_refs_in_revision.<locals>.<setcomp>Nc                    s"   h | ]\}}t j|| jd qS r   r   r  ro   r0   r1   r   @  s   )r   r|   rv   rx   )rp   r   r   r   rx   r0   ro   r1   rv   .  s    


z#XRefCache.explicit_refs_in_revisionc                    s$    j | } fdd|jj D S )z
        Look up the object refs for all objects explicitly freed
        in a given revision.

        :param revision:
            A revision number. The oldest revision is zero.
        :return:
            A set of Reference objects.
        c                    s.   h | ]&\}}|d krt j||d  jdqS )r   rB   r   r   )r   r8   genro   r0   r1   r   Q  s   z3XRefCache.refs_freed_in_revision.<locals>.<setcomp>)r   r|   rr   r   r   r0   ro   r1   refs_freed_in_revisionF  s    



z XRefCache.refs_freed_in_revisionc                 C   s   | j | }|jjS )a  
        Look up the location of the XRef table/stream associated with a specific
        revision, as indicated by startxref or /Prev.

        :param revision:
            A revision number. The oldest revision is zero.
        :return:
            An integer pointer
        )r   r   rj   r   r0   r0   r1   get_startxref_for_revisionW  s    

z$XRefCache.get_startxref_for_revisionc              	   C   sP   t | jd|d  D ]4}z|j|}|W   S  tk
rH   Y qY qX qdS )ab  
        Look up the location of the historical value of an object.

        .. note::
            This method is not suitable for determining whether or not
            a particular object ID is available in a given revision, since
            it treats unused objects and freed objects the same way.

        :param ref:
            An object reference.
        :param revision:
            A revision number. The oldest revision is zero.
        :return:
            An integer offset, an object stream reference, or ``None`` if
            the reference does not resolve in the specified revision.
        NrB   )r   r   r|   r}   r{   )rp   rz   r   r   r   r0   r0   r1   get_historical_refd  s    
zXRefCache.get_historical_refc                 C   s   |  |t| jd S r   )r  rY   r   )rp   rz   r0   r0   r1   r     s    zXRefCache.__getitem__c                 C   s   t dd | jD S )z
        Determine if a file uses hybrid references anywhere.

        :return:
            ``True`` if hybrid references were detected, ``False`` otherwise.
        c                 s   s   | ]}|j jtjkV  qd S rq   )r   ri   r   rg   r   r0   r0   r1   r     s   z1XRefCache.hybrid_xrefs_present.<locals>.<genexpr>)anyr   ro   r0   r0   r1   hybrid_xrefs_present  s    zXRefCache.hybrid_xrefs_presentN)r'   r(   r)   r*   r   r   ry   propertyr   r   rn   r   r   r  r   r  r   r  r	   rv   r  r4   r  r   r   r   r  r   r   r
  r0   r0   r0   r1   r     s"   

c                   @   sL   e Zd ZdZdddZdd Zdd Zeej	d	d
dZ
ejdddZdS )r#   u  
    Utility class to collect objects into a PDF object stream.

    Object streams are mainly useful for space efficiency reasons.
    They allow related objects to be grouped & compressed together in a
    more flexible manner.


    .. warning::
        Object streams can only be used in files with a cross-reference
        stream, as opposed to a classical XRef table.
        In particular, this means that incremental updates to files with a
        legacy XRef table cannot contain object streams either.
        See § 7.5.7 in ISO 32000-1 for further details.

    .. danger::
        Use :meth:`.BasePdfFileWriter.prepare_object_stream` to create instances
        of object streams. The `__init__` function is internal API.

    Tc                 C   s   i | _ || _d | _d S rq   )	_obj_refscompressrz   )rp   r  r0   r0   r1   ry     s    zObjectStream.__init__c                 C   s
   t | jS rq   )r   r  ro   r0   r0   r1   __bool__  s    zObjectStream.__bool__c                 C   s   t | j S rq   )r   r  r   ro   r0   r0   r1   r     s    zObjectStream.__iter__)r8   objc                 C   s    t |trtd|| j|< dS )a  
        Add an object to an object stream.
        Note that objects in object streams always have their generation number
        set to `0` by definition.

        :param idnum:
            The object's ID number.
        :param obj:
            The object to embed into the object stream.
        :raise TypeError:
            Raised if ``obj`` is an instance of :class:`~.generic.StreamObject`
            or :class:`~.generic.IndirectObject`.
        zJStream objects and bare references cannot be embedded into object streams.N)r   r%   	TypeErrorr  )rp   r8   r  r0   r0   r1   
add_object  s
    
zObjectStream.add_objectr:   c           
   	   C   s   t  }t  }| j D ].\}}| }||d |d||f  q| }|d ||}||  }t	j
tdtdtdt	t| jtdt	|i|d}	| jr|	  |	S )	z
        Render the object stream to a PDF stream object

        :return: An instance of :class:`~.generic.StreamObject`.
        Ns   %d %d r   r   z/ObjStmz/Nz/First)r^   )r   r  r   r   r   writerE   rK   getvaluer   r   r   rH   rY   r  )
rp   Zstream_headerZ	main_bodyr8   r  rQ   Zfirst_obj_offsetZsh_bytesr^   Zstream_objectr0   r0   r1   as_pdf_object  s.    

   zObjectStream.as_pdf_objectN)T)r'   r(   r)   r*   ry   r  r   r4   r   	PdfObjectr  r   r  r0   r0   r0   r1   r#     s   
c           	      c   s   d}g }|   sdS t|   dd d}t|\\}}}|D ]D}|\}}|rj||d krj||fV  g }|}|| | |f |}q<||fV  dS )zT
    Helper method to divide the XRef table (or stream) into contiguous chunks.
    Nc                 S   s   | d S r   r0   )tr0   r0   r1   <lambda>      z)_contiguous_xref_chunks.<locals>.<lambda>)r   rB   )r   sortedr   r   )	position_dictZprevious_idnumZcurrent_chunkZkey_iterr   first_idnumr[   r9   r8   r0   r0   r1   _contiguous_xref_chunks  s    
r  r  c           	         s      } d t|} fdd} fdd}zt|\}}W n" tk
rd    d | Y S X d}|dkr|d	t|d   | || n* d
  | ||t| || |D ]\}}||t| || q|S )Ns   xref
c                    s    d| |f }  |d d S )Nz%d %d
asciir  encode)r8   lengthheaderrM   r0   r1   write_header  s    z&write_xref_table.<locals>.write_headerc                    s.   | D ]$\}}d||f }  |d qd S )Nz%010d %05d n 
r  r  )chunkpositionr9   entryr#  r0   r1   write_subsection   s    z*write_xref_table.<locals>.write_subsections   0 0
s   0000000000 65535 f 
rB   r   s   0 1
)r   r  r  r   StopIterationrY   )	rM   r  Zxref_locationsubsectionsr$  r(  r  
subsectionZnull_obj_refr0   r#  r1   r"     s.    







c                       s0   e Zd Zed fddZd fdd	Z  ZS )r$   r  c                    sD   t    || _ttjd}| tdt|tdtdi d S )N)rB   r   r`   rW   r   r   )	superry   r  mapr   rH   r   r   ArrayObject)rp   r  Zwidths	__class__r0   r1   ry   E  s    
  zXRefStream.__init__Nc                    s  ddg}t | j}t }|d |D ]\}}||t|g7 }|D ]\}	}
t|	tr|
dks`t|	\}}|d |t	d| |t	d| qB|d |t	d|	 |t	d|
 qBq&t
tt
j|}|| td< | | _t |d  d S )	Nr   rB   s               z>Qz>H   rU   )r  r  r   r  rY   r   tuplerc   r   packr   r.  r-  rH   r   	getbuffer_datar,  r   )rp   rM   r   r   indexr*  Zstream_contentr  r+  r&  r9   Zobj_stream_numr[   Zindex_entryr/  r0   r1   r   T  s(    





zXRefStream.write_to_stream)NN)r'   r(   r)   r&   ry   r   __classcell__r0   r0   r/  r1   r$   D  s   )T)Dr*   r+   loggingrF   r   Zdataclassesr   ior   	itertoolsr   typingr   r   r   r   r	   r
   r   Zpyhanko.pdf_utilsr   r   Zpyhanko.pdf_utils.genericr   r   Zpyhanko.pdf_utils.miscr   r   r   Zpyhanko.pdf_utils.rw_commonr   __all__	getLoggerr'   r   uniqueEnumr   r   r   r!   r   r   r    r   r   r   r   r4   r   r   r   r   r   r   r   r  r   r   rZ   r   r&   r   r%   r#   r  r"   r$   r0   r0   r0   r1   <module>   s   $
I  ^!o  mA 4
v )$U!1