
    h5j=                    8   U d Z ddlmZ ddlZddlmZmZmZ ddlm	Z	 ddl
mZmZ ddlmZ ddlmZmZmZmZmZmZmZmZmZmZ er
dd	lmZmZmZ  ej<                  e      Z  G d
 de!      Z"ddZ#ddZ$ddZ%dddZ&e#e$e%e&gZ'de(d<   e#gZ)de(d<    G d d      Z*y)zHXMP document handling - pure XMP XML manipulation without PDF awareness.    )annotationsN)CallableIterableIterator)BytesIO)TYPE_CHECKINGAny)	parse_xml)
DEFAULT_NAMESPACESXMP_CONTAINERS	XMP_EMPTY
XMP_NS_RDF
XMP_NS_XMLXPACKET_BEGINXPACKET_ENDAltListcleanre_xml_illegal_bytes)QName_Element_ElementTreec                      e Zd ZdZy)
NeverRaisez"An exception that is never raised.N)__name__
__module____qualname____doc__     R/var/www/html/qr/venv/lib/python3.12/site-packages/pikepdf/models/metadata/_xmp.pyr   r   "   s    ,r   r   c                *    t        t        |             S Nr
   r   xmls    r    _parser_basicr&   &   s    WS\""r   c                R    t        t        t        j                  d|                   S )Nr   )r
   r   r   subr$   s    r    _parser_strip_illegal_bytesr)   *   s     W155c3?@AAr   c                .    t        t        |       d      S )NT)recoverr#   r$   s    r    _parser_recoveryr,   .   s    WS\400r   c                J    t         j                  d       t        t              S )Nz5Error occurred parsing XMP, replacing with empty XMP.)logwarningr&   r   )_xmls    r    _parser_replace_with_empty_xmpr1   2   s    KKGH##r   z%list[Callable[[bytes], _ElementTree]]PARSERS_OVERWRITE_INVALID_XMLPARSERS_STANDARDc                     e Zd ZU dZeD  ci c]  \  }}||
 c}}} Zded<    ee      Zded<   	 d ddd	 	 	 	 	 d!dZ		 	 	 	 	 	 	 	 d"d	Z
ed#d
       Zed$d       Zd%dZd&dZd'dZ	 d(	 	 	 d)dZd*dZd(d+dZd,dZd-d.dZd/dZd0dZd1dZ	 	 	 	 	 	 d2dZd2dZd3dZd4dZd5dZd,dZd6dZd7d8dZ d9dZ!yc c}}} w ):XmpDocumenta  Pure XMP XML manipulation.

    This class handles parsing, traversing, modifying, and serializing XMP
    metadata without any PDF-specific knowledge. It can be used standalone
    for XMP manipulation.

    Example:
        >>> xmp = XmpDocument(xmp_bytes)
        >>> title = xmp.get('dc:title')
        >>> xmp.set('dc:title', 'New Title')
        >>> xml_bytes = xmp.to_bytes()
    zdict[str, str]NS
REVERSE_NSNT)parsersoverwrite_invalid_xmlc               T    ||rt         nt        }| j                  |||      | _        y)a=  Parse XMP data.

        Args:
            data: XMP XML bytes to parse. Empty creates a new XMP document.
            parsers: Custom parser chain. If None, uses default based on
                overwrite_invalid_xml setting.
            overwrite_invalid_xml: If True, use recovery parsers for invalid XML.
        N)r2   r3   _parse_xmp)selfdatar8   r9   s       r    __init__zXmpDocument.__init__S   s2     ? ) .%  #'++dG=R"S	r   c                   ddl m} ddlm} |j	                         dk(  rt
        }d}|D ]  }	  ||      } n |H	 |j                  d      }	|	D ]  }
|j                  ||
j                           | j                  |       |S t        j!                  d	       t               }|S # |r|nt        $ rN}t        |      j                  d      st        |      j                  d      rt               }Y d}~ Y d}~d}~ww xY w# |rt        nt        $ r,}t        j!                  d	|
       t               }Y d}~|S d}~ww xY w)z&Parse XMP data using fallback parsers.r   etree)XMLSyntaxErrorr   Nz!Start tag expected, '<' not foundzDocument is emptyz/processing-instruction()zError occurred parsing XMP)exc_info)lxmlrB   
lxml.etreerC   stripr   r   str
startswithr1   xpath
strip_tagstag_get_rdf_root_from	Exceptionr.   r/   )r=   r>   r8   r9   rB   rC   xmpparserepispis              r    r;   zXmpDocument._parsek   sH    	-::<3D#' 	FTl 	 ?7ii ;< 2B$$S"&&12'', 
 KK4502C
7 #8Z q6$$%HISN*01N2 9:C	N2& ) 7
 81E46
 
7s/   B$AD $C?1>C::C?D?!D::D?c                p    ddl m} || j                  |<   || j                  |<   |j	                  ||       y)zRegister a new XML/XMP namespace.

        Arguments:
            uri: The long form of the namespace.
            prefix: The alias to use when interpreting XMP.
        r   rA   N)rE   rB   r6   r7   register_namespace)clsuriprefixrB   s       r    register_xml_namespacez"XmpDocument.register_xml_namespace   s2     	v$s  -r   c                \   ddl m} t        ||      rt        |      S t        |t              st	        | d      |dk(  r|S |j                  d      r|S 	 |j                  dd      \  }}| j                  j                  |d	      }t         |||            S # t        $ r d|}}Y =w xY w)
zkConvert name to an XML QName.

        e.g. pdf:Producer -> {http://ns.adobe.com/pdf/1.3/}Producer
        r   r   z must be str {:   maxsplitN)
rF   r   
isinstancerH   	TypeErrorrI   split
ValueErrorr6   get)rV   namer   rX   rL   rW   s         r    qnamezXmpDocument.qname   s     	%dE"t9$$tfL1222:K??3K	#**S1*5KFC ffjj&5c?##	  	#dCF	#s   B B+*B+c                |    |j                  dd      \  }}|j                  dd      }| j                  |   dz   |z   S )z~Given a fully qualified XML name, find a prefix.

        e.g. {http://ns.adobe.com/pdf/1.3/}Producer -> pdf:Producer
        }r_   r`   r]   r\   r^   )rd   replacer7   )r=   urinameuripartrL   rW   s        r    prefix_from_urizXmpDocument.prefix_from_uri   sC    
 }}S1}5ooc2&s#c)C//r   c                    |j                  d| j                        }|*|j                         }|j                  dk(  st	        d      |S )z.Get the rdf:RDF root element from an XMP tree.z
.//rdf:RDFz0{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDFz$Metadata seems to be XML but not XMP)findr6   getrootrL   re   )r=   rO   rdfs      r    rM   zXmpDocument._get_rdf_root_from   sD    hh|TWW-;++-C77PP !GHH
r   c                8    | j                  | j                        S )zGet the rdf:RDF root element.)rM   r<   r=   s    r    _get_rdf_rootzXmpDocument._get_rdf_root   s    &&tyy11r   c              #  $  K   | j                  |      }| j                         }|j                  d| j                        D ]  }|r*||j	                         v r|||j                  |      |f n%|s#|j                         D ]  \  }}|s	||||f  |r|nd}|j                  || j                        D ]T  }|j                  r-|j                  j                         r|d|j                  |f <| j                  |      }	|d|	|f V  yw)a#  Get elements from XMP.

        Core routine to find elements matching name within the XMP and yield
        them.

        For XMP spec 7.9.2.2, rdf:Description with property attributes,
        we yield the node which will have the desired as one of its attributes.
        qname is returned so that the node.attrib can be used to locate the
        source.

        For XMP spec 7.5, simple valued XMP properties, we yield the node,
        None, and the value. For structure or array valued properties we gather
        the elements. We ignore qualifiers.

        Args:
            name: a prefixed name or QName to look for within the
                data section of the XMP; looks for all data keys if omitted

        Yields:
            tuple: (node, qname_attrib, value, parent_node)

        rdf:Description[@rdf:about=""]*N)
rh   ru   findallr6   keysrf   itemstextrG   _get_subelements)
r=   rg   rh   rr   rdfdesckvrJ   nodevaluess
             r    _get_elementszXmpDocument._get_elements   s    2 

4   "{{#CTWWM 	4G',,.0w{{5'93??#MMO 3DAq&1c223 "EsEtww7 499!2tyy'::..t4T67334	4s   BD
BDc                (   |j                  d| j                        }|	 |d   j                  S t        D ]L  \  }}}|j                  d| | j                        }|) |       }|D ]  } |||j                          |c S  y# t        $ r Y yw xY w)zGather the sub-elements attached to a node.

        Gather rdf:Bag and and rdf:Seq into set and list respectively. For
        alternate languages values, take the first language only for
        simplicity.
        zrdf:Altr   r\   zrdf:)rp   r6   r|   
IndexErrorr   )r=   r   r{   xmlcontainer	containerinsertfnresultitems           r    r}   zXmpDocument._get_subelements   s     		)TWW-Qx}}$ 2@ 	-L)XII\N3TWW=E}[F ,+,M	   s   B 	BBc              #  P   K   d | j                  |      D        E d {    y 7 w)Nc              3  &   K   | ]	  }|d      yw)   Nr   ).0r   s     r    	<genexpr>z2XmpDocument._get_element_values.<locals>.<genexpr>  s     ;QAaD;s   )r   )r=   rg   s     r    _get_element_valueszXmpDocument._get_element_values  s     ;$"4"4T":;;;s   &$&c                6    t        | j                  |            S )zTest if XMP key exists.)anyr   r=   keys     r    __contains__zXmpDocument.__contains__  s    4++C011r   c                Z    	 t        | j                  |            S # t        $ r |cY S w xY w)z/Get XMP value for key, or default if not found.)nextr   StopIteration)r=   r   defaults      r    rf   zXmpDocument.get  s1    	00566 	N	s    **c                j    	 t        | j                  |            S # t        $ r t        |      dw xY w)zRetrieve XMP metadata for key.N)r   r   r   KeyErrorr   s     r    __getitem__zXmpDocument.__getitem__&  s8    	*00566 	*3-T)	*s    2c              #  |   K   | j                         D ]%  \  }}}}|rt        |       |j                   ' yw)z2Iterate through XMP metadata attributes and nodes.N)r   rH   rL   )r=   r   attrib_val_parentss        r    __iter__zXmpDocument.__iter__-  s=     ,0,>,>,@ 	(D&$&k!hh		s   :<c                <    t        t        t        |                   S )z#Return number of items in metadata.)lenlistiterrt   s    r    __len__zXmpDocument.__len__5  s    4T
#$$r   c                    | j                  |      }	 | j                  |||       y# t        $ r | j                  ||       Y yw xY wzSet XMP metadata key to value.N)rh   _setitem_updater   _setitem_insert)r=   r   valqkeys       r    	set_valuezXmpDocument.set_value9  sG     zz#	+  c40 	+  c*	+s   ' AAc                (    | j                  ||       yr   )r   )r=   r   r   s      r    __setitem__zXmpDocument.__setitem__H  s    sC r   c           
     n   t        fdt        D              }ddlm} ddlm} |j                  |t         |t        |                  }d }|dk(  rt         |t        d            di}D ]F  }|j                  |t         |t        d            |	      }	|.t        |      }
|
d
k(  rd }
|
|	_        H y )Nc              3  d   K   | ]'  }t        |j                        s|j                   ) y wr"   )rb   py_typerdf_type)r   cr{   s     r    r   z1XmpDocument._setitem_add_array.<locals>.<genexpr>M  s'      
*UAII2NAJJ
s   00r   rA   r[   Altlangz	x-defaultlir   r\   )r   r   rE   rB   rF   r   
SubElementrH   r   r   r   r|   )r=   r   r{   r   rB   r   seq
tag_attribr   el
inner_texts     `        r    _setitem_add_arrayzXmpDocument._setitem_add_arrayL  s     
 .
 
 	$tSz8)D%EF,0
ueJ78+FJ 	%D!!#s5T+B'CJ!WB).t
#!%J$	%r   c           
     L   ddl m} t        | j                  |            \  }}}}|rt	        |t
              sZ|| j                  d      k(  r)|j                  |= | j                  |t        |             y t        d| d| dt        |             |j                  |t        |             y t	        |t        t        z        r:|j                  d      D ]  }	|j                  |	        | j                  ||       y t	        |t
              rz|j                  d      D ]  }	|j                  |	        t        | j                  |            |v r&| j                  |t!        t        |      g             y t        |      |_        y t        d| d| dt        |             )Nr   	LANG_ALTSz
dc:creatorSetting  to  with type rx   )"pikepdf.models.metadata._constantsr   r   r   rb   rH   rh   r   r   r   rc   typesetr   ry   remover   r|   )
r=   r   r   r   r   r   r   _oldval_parentchilds
             r    r   zXmpDocument._setitem_update^  sf   @ *.d.@.@.E)F&fgwc3'4::l33 D)++D%*=#hse4uKS	{$STTs,TCZ(c* #E"###D#.S!c* #E"#4::c?#y0''guSzl.CD!#J	hse4uKS	{KLLr   c           
        ddl m} ddlm} ddlm} | j                         }t        | j                  |            |v rt        t        |      g      }|j                  d| j                        }|>|j                  |t         |t        d            t         |t        d            di	      }t        |t         t"        z        r4|j                  || j                  |            }| j%                  ||       y t        |t              r2|j                  || j                  |            }t        |      |_        y t)        d
| d| dt+        |             d )Nr   rA   r[   r   rw   Descriptionaboutr\   r   r   r   r   )rE   rB   rF   r   r   r   ru   rH   rh   r   r   rp   r6   r   r   rb   r   r   r   r|   rc   r   )	r=   r   r   rB   r   r   rr   r~   r   s	            r    r   zXmpDocument._setitem_insert|  s#   $@  "tzz#9,5:,'C ((;TWWE?&&E*m45E*g67< ' G
 c4#:&##GTZZ_=D##D#.S!##GTZZ_=Dc
DIhse4uKS	{KLRVVr   c                l   ddl m} 	 t        | j                  |            \  }}}}|rl|j                  |= t        |j                        dk(  rFt        |      dk(  r8 |t        d      |j                  j                         v r|j                  |       y|j                  |       y# t        $ r Y yw xY w)z}Delete item from XMP metadata.

        Returns:
            True if item was found and deleted, False if not found.
        r   r[   r_   r   TF)
rF   r   r   r   r   r   r   rz   r   r   )r=   r   r   r   r   r   parents          r    deletezXmpDocument.delete  s     	%	,01C1CC1H,I)D&'6KK'$)D	Qj'2dkk6F6F6HH MM$'  d# 		s   BB' B' '	B32B3c                <    | j                  |      st        |      y)zDelete item from XMP metadata.N)r   r   r   s     r    __delitem__zXmpDocument.__delitem__  s    {{33-  r   c                    t               }|r|j                  t               | j                  j                  |dd       |r|j                  t               |j                  d       |j                         S )zSerialize XMP to XML bytes.

        Args:
            xpacket: If True, wrap in xpacket markers.

        Returns:
            XML bytes representation of the XMP.
        utf-8T)encodingpretty_printr   )r   writer   r<   r   seekread)r=   xpacketr>   s      r    to_byteszXmpDocument.to_bytes  sV     yJJ}%		wTBJJ{#		!yy{r   c                D    | j                  d      j                  d      S )z#Convert XMP metadata to XML string.F)r   r   )r   decodert   s    r    __str__zXmpDocument.__str__  s    }}U}+227;;r   r   )r>   bytesr8   z0Iterable[Callable[[bytes], _ElementTree]] | Noner9   bool)r>   r   r8   z)Iterable[Callable[[bytes], _ElementTree]]r9   r   returnr   )rW   rH   rX   rH   r   None)rg   zQName | strr   rH   )rl   rH   r   rH   )rO   r   r   r   )r   r   )r\   )rg   str | QNamer   z<Iterator[tuple[_Element, str | bytes | None, Any, _Element]])r   r   r   r	   )rg   r   r   zIterator[Any])r   r   r   r   r"   )r   r   r   r	   r   r	   )r   r   r   r	   )r   zIterator[str])r   int)r   r   r   zset[str] | list[str] | strr   r   )r   r   r{   r   r   r   )r   r   r   r	   r   rH   r   r   )r   r   r   r	   r   r   )r   r   r   r   )T)r   r   r   r   )r   rH   )"r   r   r   r   r   r6   __annotations__dictr7   r?   r;   classmethodrY   rh   rn   rM   ru   r   r}   r   r   rf   r   r   r   r   r   r   r   r   r   r   r   r   )r   rW   rX   s   000r    r5   r5   A   sh    :LLL+#v&#+LBL!%&8!9J9 T EI&*TT B	T
  $T0-- ;-  $	-
 
-^ . . $ $.02
 #%(4(4	E(4T0<2*%++ (+ 
	+!%$M<W62 
$<o Ms   B>r5   )r%   r   r   r   r   )r0   r   r   r   )+r   
__future__r   loggingcollections.abcr   r   r   ior   typingr   r	   pikepdf._xmlr
   r   r   r   r   r   r   r   r   r   r   r   rF   r   r   r   	getLoggerr   r.   rN   r   r&   r)   r,   r1   r2   r   r3   r5   r   r   r    <module>r      s    O "  8 8  % "   88 g!- -#B1$ "	H D  <I/ 7 IH< H<r   