U
    <gLz                     @   sZ  d 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 ddlZddl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mZ ddlmZ ddlmZ ddlmZ ddl m!Z!m"Z" ddl#m$Z$m%Z%m&Z& ddl'm(Z(m)Z) dddddddddddddgZ*G dd dZ+ej,dd d!Z-ed"d#G d$d deZ.ed"d#G d%d de.Z/ed"d#G d&d de.Z0G d'd dej1Z2d(Z3ed"d#G d)d de0Z4G d*d dej,Z5G d+d de5Z6G d,d de5Z7G d-d de7Z8e9e9e0e:e:e:d.d/d0Z;d8e9e9e0e:e:e:d.d1dZ<d9e9e9e4e:e:e:e9d2d3dZ=ej>ej?d4d4d5d6d7Z@dS ):a  
Utilities for stamping PDF files.

Here 'stamping' loosely refers to adding small overlays (QR codes, text boxes,
etc.) on top of already existing content in PDF files.

The code in this module is also used by the :mod:`.sign` module to render
signature appearances.
    N)hexlify)	dataclass)datetime)ListOptionalTuple)ConfigurableMixin)ConfigurationError)contentgenericlayout)IndirectObjectpdf_name
pdf_string)IncrementalPdfFileWriter)LayoutError)rd)PdfFancyQRImagePdfStreamQRImage)DEFAULT_BOX_LAYOUTTextBoxTextBoxStyle)BasePdfFileWriterinit_xobject_dictionaryAnnotAppearancesBaseStampStyleTextStampStyleQRStampStyleStaticStampStyle
QRPosition	BaseStamp	TextStampQRStampStaticContentStamptext_stamp_fileqr_stamp_fileSTAMP_ART_CONTENTc                   @   sB   e Zd ZdZd	ejeej eej dddZejdddZ	dS )
r   up  
    Convenience abstraction to set up an appearance dictionary for a PDF
    annotation.

    Annotations can have three appearance streams, which can be roughly
    characterised as follows:

    * *normal*: the only required one, and the default one;
    * *rollover*: used when mousing over the annotation;
    * *down*: used when clicking the annotation.

    These are given as references to form XObjects.

    .. note::
        This class only covers the simple case of an appearance dictionary
        for an annotation with only one appearance state.

    See § 12.5.5 in ISO 32000-1 for further information.
    Nnormalrolloverdownc                 C   s   || _ || _|| _d S Nr'   )selfr(   r)   r*    r-   1/tmp/pip-unpacked-wheel-w101_d3s/pyhanko/stamp.py__init__F   s    zAnnotAppearances.__init__returnc                 C   sH   t td| ji}| jdk	r,| j|td< | jdk	rD| j|td< |S )a  
        Convert the :class:`.AnnotationAppearances` instance to a PDF
        dictionary.

        :return:
            A :class:`~.pdf_utils.generic.DictionaryObject` that can be plugged
            into the ``/AP`` entry of an annotation dictionary.
        z/NNz/Rz/D)r   DictionaryObjectr   r(   r)   r*   )r,   resr-   r-   r.   as_pdf_objectP   s    


zAnnotAppearances.as_pdf_object)NN)
__name__
__module____qualname____doc__r   r   r   r/   r2   r4   r-   r-   r-   r.   r   1   s     
r0   c                 C   sd   t | tstd| dkrtS | dr2t| S ddlm} ddl	m
} || }||d dS d S )Nz)Background specification must be a stringZ	__stamp__z.pdfr   )Image)PdfImage)writer)
isinstancestrr	   r&   endswithr
   ImportedPdfPageZPILr9   Zpyhanko.pdf_utils.imagesr:   open)bg_specr9   r:   imgr-   r-   r.   _get_background_contentb   s    



rC   T)frozenc                       s   e Zd ZU dZdZeed< dZee	j
 ed< ejejjejjejddZejed< d	Zeed
< e fddZeejeddddZ  ZS )r   z&
    Base class for stamp styles.
       border_widthN
background   )x_aligny_alignmarginsbackground_layoutg333333?background_opacityc                    sJ   t  | d}z|d }W n tk
r0   Y nX |dk	rFt||d< dS )a  
        This implementation of :meth:`process_entries` processes the
        :attr:`background` configuration value.
        This can either be a path to an image file, in which case it will
        be turned into an instance of :class:`~.pdf_utils.images.PdfImage`,
        or the special value ``__stamp__``, which is an alias for
        :const:`~pyhanko.stamp.STAMP_ART_CONTENT`.
        NrG   )superprocess_entriesKeyErrorrC   )clsconfig_dictrA   	__class__r-   r.   rO      s    zBaseStampStyle.process_entriesr    r;   boxtext_paramsr1   c                 C   s   t d S r+   NotImplementedErrorr,   r;   rV   rW   r-   r-   r.   create_stamp   s    zBaseStampStyle.create_stamp)r5   r6   r7   r8   rF   int__annotations__rG   r   r
   
PdfContentr   SimpleBoxLayoutRuleAxisAlignment	ALIGN_MIDMarginsuniformrL   rM   floatclassmethodrO   r   BoxConstraintsdictr[   __classcell__r-   r-   rS   r.   r   v   s    

c                   @   sH   e Zd ZU dZdZeed< edd dddZe	e
jedd	d
dZdS )r   za
    Stamp style that does not include any custom parts; it only renders
    the background.
    g      ?rM   r   r0   c                 K   s   t f dtj||di|S )a  
        Create a :class:`StaticStampStyle` from a page from an external PDF
        document. This is a convenience wrapper around
        :class:`~content.ImportedPdfContent`.

        The remaining keyword arguments are passed to
        :class:`StaticStampStyle`'s init method.

        :param file_name:
            File name of the external PDF document.
        :param page_ix:
            Page index to import. The default is ``0``, i.e. the first page.
        rG   )page_ix)r   r
   r?   )rQ   	file_nameri   kwargsr-   r-   r.   from_pdf_file   s
    zStaticStampStyle.from_pdf_filer#   rU   c                 C   s   t || |dS )Nr;   stylerV   )r#   rZ   r-   r-   r.   r[      s    zStaticStampStyle.create_stampN)r   )r5   r6   r7   r8   rM   rd   r]   re   rl   r   r   rf   rg   r[   r-   r-   r-   r.   r      s   
 c                   @   s`   e Zd ZU dZe Zeed< dZee	j
 ed< dZeed< dZeed< ee	jed	d
ddZdS )r   z
    Style for text-based stamps.

    Roughly speaking, this stamp type renders some predefined (but parametrised)
    piece of text inside a text box, and possibly applies a background to it.
    text_box_styleNinner_content_layoutz%(ts)s
stamp_textz%Y-%m-%d %H:%M:%S %Ztimestamp_formatr!   rU   c                 C   s   t || ||dS )N)r;   rn   rV   rW   )r!   rZ   r-   r-   r.   r[   
  s       zTextStampStyle.create_stamp)r5   r6   r7   r8   r   ro   r]   rp   r   r   r_   rq   r=   rr   r   rf   rg   r[   r-   r-   r-   r.   r      s   


c                   @   s   e Zd ZdZejejjejjdZ	ejejj
ejjdZejejj
ejjdZejejjejjdZedd Zed dddZd	S )
r   z[
    QR positioning constants, with the corresponding default content layout
    rule.
    )rI   rJ   )rJ   rI   c                 C   s   | t jt jfkS r+   )r   LEFT_OF_TEXTRIGHT_OF_TEXTr,   r-   r-   r.   horizontal_flow,  s    zQRPosition.horizontal_flowr0   c                 C   sJ   z t jt jt jt jd|  W S  tk
rD   td| dY nX dS )a  
        Convert from a configuration string.

        :param config_str:
            A string: 'left', 'right', 'top', 'bottom'
        :return:
            An :class:`.QRPosition` value.
        :raise ConfigurationError: on unexpected string inputs.
        leftrighttopbottom'zW' is not a valid QR position setting; valid values are 'left', 'right', 'top', 'bottom'N)r   rs   rt   
ABOVE_TEXT
BELOW_TEXTlowerrP   r	   )rQ   Z
config_strr-   r-   r.   from_config0  s    
zQRPosition.from_configN)r5   r6   r7   r8   r   r_   r`   Z	ALIGN_MINra   rs   Z	ALIGN_MAXrt   r}   r~   propertyrv   re   r   r-   r-   r-   r.   r     s*   
g?c                       s   e Zd ZU dZdZeed< dZeed< dZ	e
e ed< ejZeed< dZe
ej ed	< e fd
dZeejeddddZ  ZS )r   z
    Style for text-based stamps together with a QR code.

    This is exactly the same as a text stamp, except that the text box
    is rendered with a QR code to the left of it.
    rE   innsepz@Digital version available at
this url: %(url)s
Timestamp: %(ts)srq   Nqr_inner_sizeqr_positionqr_inner_contentc                    s@   t  | z|d }t||d< W n tk
r:   Y nX d S )Nr   )rN   rO   r   r   rP   )rQ   rR   Zqr_posrS   r-   r.   rO     s    zQRStampStyle.process_entriesr"   rU   c                 C   s@   z| d}W n tk
r,   tdY nX t|| |||dS )Nurlz7Using a QR stamp style requires a 'url' text parameter.)rn   r   rW   rV   )poprP   r   r   r"   )r,   r;   rV   rW   r   r-   r-   r.   r[     s    
    zQRStampStyle.create_stamp)r5   r6   r7   r8   r   r\   r]   rq   r=   r   r   r   rs   r   r   r
   r^   re   rO   r   r   rf   rg   r[   rh   r-   r-   rS   r.   r   R  s   

	c                       st   e Zd Zdeeej d fddZdd Zdd Z	d	d
 Z
ejdddZeeedddZedddZ  ZS )r    Nr;   rV   c                    s&   t  j||d || _d| _d | _d S )N)rV   r;   F)rN   r/   rn   Z_resources_ready
_stamp_refr,   r;   rn   rV   rS   r-   r.   r/     s    zBaseStamp.__init__c              	   C   s   | j j}|| j | }|j}|jrH|jrH| j j	| j|j
|j}n | j jj}tjdd|j|jd}t| j j}| jtjjtdttd|td|id d| |f }| |j |S )N   )x_scaley_scalex_posy_posz/BackgroundGSz/CAz/cacategorynamevalues   q /BackgroundGS gs %s %s Q)rn   rG   Z
set_writerr;   renderrV   width_definedheight_definedrL   fitwidthheightrK   r   ZPositioningrx   r{   r   FloatObjectrM   set_resourcer
   ResourceTypeZEXT_G_STATEr   r2   as_cmZimport_resources	resources)r,   bgZ
bg_contentZbg_boxZpositioningrK   Zopacitycommandr-   r-   r.   _render_background  s>      
   	zBaseStamp._render_backgroundc                 C   s   t d S r+   rX   ru   r-   r-   r.   _render_inner_content  s    zBaseStamp._render_inner_contentc                 C   sp   dg}|   }| jjr$||   |r2|| | j}| jj}|r\|d||j|j	f  |d d
|S )N   qs   %g w 0 0 %g %g re S   Q    )r   rn   rG   appendr   extendrV   rF   r   r   join)r,   command_streamZinner_contentbboxrF   r-   r-   r.   r     s    

zBaseStamp.renderr0   c                 C   s0   | j }|dkr,| j}|  }|| | _ }|S )a  
        Register the stamp with the writer coupled to this instance, and
        cache the returned reference.

        This works by calling :meth:`.PdfContent.as_form_xobject`.

        :return:
            An indirect reference to the form XObject containing the stamp.
        N)r   Z_ensure_writerZas_form_xobject
add_object)r,   	stamp_refwrZ	form_xobjr-   r-   r.   register  s    
zBaseStamp.register)	dest_pagexyc              	   C   s   |   }dtt j }dt|t||f }tj|d}tt	dtt	|
d|ii}| j}	|	dk	stt|	||	||}
| jj| jjf}|
|fS )a[  
        Apply a stamp to a particular page in the PDF writer attached to this
        :class:`.BaseStamp` instance.

        :param dest_page:
            Index of the page to which the stamp is to be applied
            (starting at `0`).
        :param x:
            Horizontal position of the stamp's lower left corner on the page.
        :param y:
            Vertical position of the stamp's lower left corner on the page.
        :return:
            A reference to the affected page object, together with
            a ``(width, height)`` tuple describing the dimensions of the stamp.
        s   /Stamps   q 1 0 0 1 %g %g cm %s Do Q)Zstream_dataz/XObjectasciiN)r   r   uuidZuuid4bytesr   r   ZStreamObjectr2   r   decoder;   AssertionErrorZadd_stream_to_pager   rV   r   r   )r,   r   r   r   r   resource_nameZstamp_paintZstamp_wrapper_streamr   r   page_refZdimsr-   r-   r.   apply  s0       zBaseStamp.applyc                 C   s   |   }t|dS )a  
        Turn this stamp into an appearance dictionary for an annotation
        (or a form field widget), after rendering it.
        Only the normal appearance will be defined.

        :return:
            An instance of :class:`.AnnotAppearances`.
        )r(   )r   r   )r,   r   r-   r-   r.   as_appearances"  s    zBaseStamp.as_appearances)N)r5   r6   r7   r   r   r   rf   r/   r   r   r   r   r   r   r\   r   r   r   rh   r-   r-   rS   r.   r      s    ''c                       s4   e Zd ZdZeeejd fddZdd Z	  Z
S )r#   z.Class representing stamps with static content.rm   c                    s0   |r|j r|jstdt j|||d d S )Nz9StaticContentStamp requires a predetermined bounding box.rV   rn   r;   )r   r   r   r   rN   r/   r   rS   r-   r.   r/   4  s
    zStaticContentStamp.__init__c                 C   s   g S r+   r-   ru   r-   r-   r.   r   @  s    z(StaticContentStamp._render_inner_content)r5   r6   r7   r8   r   r   r   rf   r/   r   rh   r-   r-   rS   r.   r#   1  s   c                       sX   e Zd ZdZdeeej d fddZdd Z	dd	 Z
d
d Zdd Zdd Z  ZS )r!   zf
    Class that renders a text stamp as specified by an instance
    of :class:`.TextStampStyle`.
    Nr   c                    s"   t  j|||d || _d | _d S )Nr   )rN   r/   rW   text_box)r,   r;   rn   rW   rV   rS   r-   r.   r/   J  s    zTextStamp.__init__c                 C   s"   t jt d}d|| jjiS )aQ  
        Compute values for the default string interpolation parameters
        to be applied to the template string specified in the stamp
        style. This method does not take into account the ``text_params``
        init parameter yet.

        :return:
            A dictionary containing the parameters and their values.
        )tzts)r   nowtzlocalZget_localzonestrftimern   rr   )r,   r   r-   r-   r.   get_default_text_paramsV  s    
 z!TextStamp.get_default_text_paramsc              
   C   s   t | jj| j| jd d | _}|  }| jd k	r<|| j z| jj	| }W n8 t
k
r } ztd|jd  dW 5 d }~X Y nX ||_| S )N)r;   r   rV   zStamp text parameter 'r   z' is missing)r   rn   ro   r;   r   r   r   rW   updaterq   rP   r   argsr
   r   )r,   tbZ_text_paramstexter-   r-   r.   _text_layoute  s    
(zTextStamp._text_layoutc                 C   s"   |   }| jj}|g|j|jffS r+   )r   r   rV   r   r   )r,   text_commandsZinn_boxr-   r-   r.   _inner_layout_natural_sizez  s    z$TextStamp._inner_layout_natural_sizec                 C   s   | j jp
tS r+   )rn   rp   r   ru   r-   r-   r.   _inner_content_layout_rule  s    z$TextStamp._inner_content_layout_rulec                 C   sX   dg}|   \}\}}|  }| j}||||}||  || |d |S )Nr   r   )r   r   rV   r   r   r   r   )r,   r   Zinn_commands	inn_width
inn_heightZinner_layoutr   Zinn_positionr-   r-   r.   r     s    

zTextStamp._render_inner_content)NN)r5   r6   r7   r8   r   r   r   rf   r/   r   r   r   r   r   rh   r-   r-   rS   r.   r!   D  s   	  c                       s~   e Zd Zdeeeeej d fddZ	dd Z
eee eeef f d fdd	Zd
d Z fddZ fddZ  ZS )r"   N)r;   r   rn   rV   c                    s$   t  j||||d || _d | _d S )N)rW   rV   )rN   r/   r   Z_qr_size)r,   r;   r   rn   rW   rV   rS   r-   r.   r/     s    zQRStamp.__init__c                 C   s   | j }|jd k	r|jS |jjS r+   )rn   rp   r   r   )r,   rn   r-   r-   r.   r     s    
z"QRStamp._inner_content_layout_ruler0   c                    s  t   \}\}}|  \}}| jtjjtd|d | j}| j	}|j
}|jd k	rZ|j}	nD|jr|jrtt|j|t|j|}
|
d|  }	nttt| }	|	| }|	d|  }|jjr|| }t||}nt||}|| }|jj}tj|j|jtj|tjjd}t ||}|!||	|	}d|j"| |j#| |j$|j%f }|jt&j'krftj|dddd}nV|jt&j(krtj|dddd}n4|jt&j)krtj|dddd	}ntj|dddd
}tj|jj*|jj*|tjjd}|!|||}|d|+ g}|,| |-d |||ffS )Nz/QRr      )rI   rJ   rK   Zinner_content_scalings   q %g 0 0 %g %g %g cm /QR Do Qr   rw   )ry   rx   rz   r{   )r{   ry   rx   rz   )rz   ry   rx   r{   r   r   ).rN   r   _qr_xobjectr   r
   r   ZXOBJECTr   rn   rV   r   r   r   r   minmaxr   r   r\   roundDEFAULT_QR_SCALEr   rv   r   r   r_   rI   rJ   rb   rc   ZInnerScalingZ
NO_SCALINGrf   r   r   r   r   r   r   rs   rt   r~   Zflippedr   r   r   )r,   r   
text_widthZtext_heightZqr_refZnatural_qr_sizern   Z	stamp_boxr   Zqr_sizeZmin_dimZqr_innunits_scaleZ	qr_paddedr   r   Zdefault_layoutZqr_layout_ruleZ	inner_boxZ
qr_inn_posZdraw_qr_commandZ
tb_marginsZtb_layout_ruleZtext_inn_poscommandsrS   r-   r.   r     s    




	            


z"QRStamp._inner_layout_natural_sizec                 C   s   | j jd k	}|rtjjntjj}tj|d}|| j |	  |r\|j
t|j| j jd}n|j
td}| }|jd|j  |j }t|||}|  | j||fS )N)Zerror_correction)image_factoryversionZcenter_image)r   r   )rn   r   qrcode	constantsZERROR_CORRECT_HZERROR_CORRECT_MZQRCodeZadd_datar   makeZ
make_imager   r   r   Zrender_command_streamZmodules_countZborderZbox_sizer   compressr;   r   )r,   Zis_fancyZerr_corrZqrrB   r   Z	bbox_sizeZqr_xobjr-   r-   r.   r     s(    
zQRStamp._qr_xobjectc                    s   t   }| j|d< |S )Nr   )rN   r   r   )r,   tprS   r-   r.   r   8  s    

zQRStamp.get_default_text_paramsc           
         s   t  |||\}\}}|||| || f}ttdtdtdtdtdttttj|tdttdtdtdt	| j
ii}| j}	|	||	| |||ffS )	Nz/Typez/Annotz/Subtypez/Linkz/Rectz/Az/Sz/URI)rN   r   r   r2   r   ZArrayObjectlistmapr   r   r   r;   Zregister_annotationr   )
r,   r   r   r   r   whZ	link_rectZ
link_annotr   rS   r-   r.   r   =  s0          zQRStamp.apply)NN)r5   r6   r7   r   r=   r   r   r   rf   r/   r   r   r   r   r\   r   r   r   r   rh   r-   r-   rS   r.   r"     s     &n)
input_nameoutput_namern   r   r   r   c              
   K   sh   t | dT}t|dd}	|f |	|d|}
|
||| t |d}|	| W 5 Q R X W 5 Q R X d S )NrbF)strict)r;   rn   wb)r@   r   r   write)r   r   rn   Zstamp_classr   r   r   Zstamp_kwargsZfinZpdf_outZstampoutr-   r-   r.   _stamp_fileT  s    
r   c              
   C   s   t | ||t||||d dS )a5  
    Add a text stamp to a file.

    :param input_name:
        Path to the input file.
    :param output_name:
        Path to the output file.
    :param style:
        Text stamp style to use.
    :param dest_page:
        Index of the page to which the stamp is to be applied (starting at `0`).
    :param x:
        Horizontal position of the stamp's lower left corner on the page.
    :param y:
        Vertical position of the stamp's lower left corner on the page.
    :param text_params:
        Additional parameters for text template interpolation.
    )rW   N)r   r!   )r   r   rn   r   r   r   rW   r-   r-   r.   r$   g  s    )r   r   rn   r   r   r   r   c                 C   s   t | ||t|||||d	 dS )aj  
    Add a QR stamp to a file.

    :param input_name:
        Path to the input file.
    :param output_name:
        Path to the output file.
    :param style:
        QR stamp style to use.
    :param dest_page:
        Index of the page to which the stamp is to be applied (starting at `0`).
    :param x:
        Horizontal position of the stamp's lower left corner on the page.
    :param y:
        Vertical position of the stamp's lower left corner on the page.
    :param url:
        URL for the QR code to point to.
    :param text_params:
        Additional parameters for text template interpolation.
    )r   rW   N)r   r"   )r   r   rn   r   r   r   r   rW   r-   r-   r.   r%     s    d   )r   r   s  
q 1 0 0 -1 0 100 cm 
0.603922 0.345098 0.54902 rg
3.699 65.215 m 3.699 65.215 2.375 57.277 7.668 51.984 c 12.957 46.695 27.512
 49.34 39.418 41.402 c 39.418 41.402 31.48 40.078 32.801 33.465 c 34.125
 26.852 39.418 28.172 39.418 24.203 c 39.418 20.234 30.156 17.59 30.156
14.945 c 30.156 12.297 28.465 1.715 50 1.715 c 71.535 1.715 69.844 12.297
 69.844 14.945 c 69.844 17.59 60.582 20.234 60.582 24.203 c 60.582 28.172
 65.875 26.852 67.199 33.465 c 68.52 40.078 60.582 41.402 60.582 41.402
c 72.488 49.34 87.043 46.695 92.332 51.984 c 97.625 57.277 96.301 65.215
 96.301 65.215 c h f
3.801 68.734 92.398 7.391 re f
3.801 79.512 92.398 7.391 re f
3.801 90.289 92.398 7.391 re f
Q
)rV   data)N)N)Ar8   enumr   binasciir   dataclassesr   r   typingr   r   r   r   r   Zpyhanko.config.apir   Zpyhanko.config.errorsr	   Zpyhanko.pdf_utilsr
   r   r   Zpyhanko.pdf_utils.genericr   r   r   Z$pyhanko.pdf_utils.incremental_writerr   Zpyhanko.pdf_utils.layoutr   Zpyhanko.pdf_utils.miscr   Zpyhanko.pdf_utils.qrr   r   Zpyhanko.pdf_utils.textr   r   r   Zpyhanko.pdf_utils.writerr   r   __all__r   r^   rC   r   r   r   Enumr   r   r   r    r#   r!   r"   r=   r\   r   r$   r%   Z
RawContentrf   r&   r-   r-   r-   r.   <module>   s   
1@+14	H W ; / ,