Ë
    ðì�irg  ã                   ób   — d Z ddlmZ ddlmZ  G d„ de«      Z G d„ de«      Z G d„ d	e«      Zy
)z�
PassportEye::MRZ: Machine-readable zone extraction and parsing.
MRZ textual representation parsing.

Author: Konstantin Tretyakov
License: MIT
é    )ÚOrderedDict)Údatetimec                   ór   — e Zd ZdZd„ Zed„ «       Zd„ Zed„ «       Zd„ Z	d„ Z
d„ Zd	„ Zd
„ Zed„ «       Zdd„Zy)ÚMRZau  
    A simple parser for a Type1 or Type3 Machine-readable zone strings from identification documents.
    See:
        - https://en.wikipedia.org/wiki/Machine-readable_passport
        - http://www.icao.int/publications/pages/publication.aspx?docnum=9303

    Usage:
        Represent the MRZ as a list of 2 or 3 lines, create an instance of this class,
        and read off the various fields filled by the parser.
        The first field you should check is .mrz_type.
        It is either None (no parsing done at all) or 'TD1', 'TD2', 'TD3', 'MRVA' or 'MRVB' depending on the type of the MRZ.
        The next one is 'valid'. If this is true, you may be pretty sure the parsing was successful and
        all the checksum digits passed the test as well. Sometimes the validity check may fail for some trivial reason
        (e.g. nonstandard document type character or one of the checksums wrong while others corect) -
        for this reason there is a field `valid_score`, which is an integer between 0 (nothing is valid) to 100
        (all checksums, line lengths and miscellaneous checks passed).

        Otherwise at least some of the checks failed, the meaning of which is up to you to interpret.
        When given invalid data, the algorithm attempts to do some trivial data clean-up: drop whitespaces from lines,
        and extend short lines with filler characters <, after which the fields are extracted from the lines as if
        they were valid.

        The parsing computes three validation indicators:
            valid_check_digits - a list of booleans indicating which of the "check digits" in the MRZ were valid.
                                TD1/TD2 has four check digits, TD3 - five, MRVA/B - three.
                                The separate booleans are also available as valid_number, valid_date_of_birth, valid_expiration_date, valid_composite
                                and valid_personal_number (TD3 only).
            valid_line_lengths - a list of booleans, indicating which of the lines (3 in TD1, 2 in TD2/TD3) had the expected length.
            valid_misc         - a list of booleans, indicating various additional validity checks (unspecified, see code).
        The valid_score field counts the "validity score" according to the flags above and is an int between 0 and 100.
        When all validation passes, the valid field is set to True as well.
        However, you may attempt reading fields from a "not completely valid" MRZ as well sometimes.

        The reported fields are: type, country, number, date_of_birth, sex, expiration_date, nationality, names, surname
        TD1 MRZ also has fields optional1 and optional2. TD2 MRZ has optional1, TD3 MRZ has personal_number.
        MRVA and MRVB are the same as TD3 except personal_number and check_composite (which are not present)

        The field aux is a dictionary of additional data that may be associated with MRZ by OCR code,
        e.g. aux['roi'], aux['box'] or aux['raw_text'] may be used to carry around the part of the image that was used
        to extract the information, aux['method'] to mark the method used, etc.

    # Valid ID card (TD1)
    >>> m = MRZ(['IDAUT10000999<6<<<<<<<<<<<<<<<', '7109094F1112315AUT<<<<<<<<<<<4', 'MUSTERFRAU<<ISOLDE<<<<<<<<<<<<'])
    >>> assert m.mrz_type == 'TD1' and m.valid and m.valid_score == 100
    >>> assert m.type == 'ID' and m.country == 'AUT' and m.number == '10000999<'
    >>> assert m.date_of_birth == '710909' and m.sex == 'F' and m.expiration_date == '111231' and m.nationality == 'AUT'
    >>> assert m.names == 'ISOLDE' and m.surname == 'MUSTERFRAU'
    >>> assert m.check_number == '6' and m.check_date_of_birth == '4' and m.check_expiration_date == '5' and m.check_composite == '4'
    >>> assert m.optional1 == '<<<<<<<<<<<<<<<' and m.optional2 == '<<<<<<<<<<<'

    # Valid TD2
    >>> m = MRZ(['I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<', 'D231458907UTO7408122F1204159<<<<<<<6'])
    >>> assert m.mrz_type == 'TD2' and m.valid and m.valid_score == 100
    >>> assert m.type == 'I<' and m.country == 'UTO' and m.number == 'D23145890'
    >>> assert m.date_of_birth == '740812' and m.sex == 'F' and m.expiration_date == '120415' and m.nationality == 'UTO'
    >>> assert m.names == 'ANNA MARIA' and m.surname == 'ERIKSSON'
    >>> assert m.check_number == '7' and m.check_date_of_birth == '2' and m.check_expiration_date == '9' and m.check_composite == '6'

    # Valid Visa
    >>> m = MRZ(['VIUSATRAVELER<<HAPPYPERSON<<<<<<<<<<<<<<<<<<', '555123ABC6GBR6502056F04122361FLNDDDAM5803085'])
    >>> assert m.mrz_type == 'MRVA' and m.valid and m.valid_score == 100
    >>> assert m.type == 'VI' and m.country == 'USA' and m.number == '555123ABC'
    >>> assert m.date_of_birth == '650205' and m.sex == 'F' and m.expiration_date == '041223' and m.nationality == 'GBR'
    >>> assert m.names == 'HAPPYPERSON' and m.surname == 'TRAVELER'
    >>> assert m.check_number == '6' and m.check_date_of_birth == '6' and m.check_expiration_date == '6'

    # Valid passport (TD3)
    >>> m = MRZ(['P<POLKOWALSKA<KWIATKOWSKA<<JOANNA<<<<<<<<<<<', 'AA00000000POL6002084F1412314<<<<<<<<<<<<<<<4'])
    >>> assert m.mrz_type == 'TD3' and m.valid and m.valid_score == 100
    >>> assert m.type == 'P<' and m.country == 'POL' and m.number == 'AA0000000' and m.personal_number == '<<<<<<<<<<<<<<'
    >>> assert m.date_of_birth == '600208' and m.sex == 'F' and m.expiration_date == '141231' and m.nationality == 'POL'
    >>> assert m.names == 'JOANNA' and m.surname == 'KOWALSKA KWIATKOWSKA'
    >>> assert m.check_number == '0' and m.check_date_of_birth == '4' and m.check_expiration_date == '4' and m.check_personal_number == '<' and m.check_composite == '4'

    # Invalid examples
    >>> assert MRZ([]).mrz_type is None
    >>> assert MRZ([1,2,3,4]).mrz_type is None
    >>> assert MRZ([1,2,3]).mrz_type is None

    >>> m = MRZ(['IDAUT10000999<6<<<<<<<<<<<<<<<', '7109094F1112315AUT<<<<<<<<<<<6', 'MUSTERFRAU<<ISOLDE<<<<<<<<<<<<'])
    >>> assert m.mrz_type == 'TD1' and not m.valid and m.valid_score < 100
    >>> assert m.valid_check_digits == [True, True, True, False]
    >>> assert m.type == 'ID' and m.country == 'AUT' and m.number == '10000999<'

    # The utility from_ocr function will convert a single newline-separated string obtained as OCR output
    # into MRZ lines, doing some basic cleanup inbetween (removing empty lines and lines that are too short,
    # removing spaces, converting mismatched characters, etc), and then attempt the parsing.
    >>> m = MRZ.from_ocr('\n\n this line useless \n IDAUT10000999<6  <<<<<<<<< <<<<<< \n 7IO9O94FIi  iz3iSAUT<<<<<<<<<<<4 \n MUSTERFRA  U<<ISOLDE<<<  <<<<<<<<<')
    >>> assert m.valid and m.names == 'ISOLDE' and m.surname == 'MUSTERFRAU'

    c                 ó4   — | j                  |«       i | _        y)a  
        Parse a TD1/TD2/TD3/MRVA/MRVB MRZ from a single newline-separated string or a list of strings.

        :param mrz_lines: either a single string with newlines, or a list of 2 or 3 strings, representing the lines of an MRZ.
        :return: self
        N)Ú_parseÚaux©ÚselfÚ	mrz_liness     úy/var/www/ITT-ServiceAPI-Services/ITT-Document-Scan/mrzscanner/mrzenv/lib/python3.12/site-packages/passporteye/mrz/text.pyÚ__init__zMRZ.__init__j   s   € ð 	�‰�IÔØˆ�ó    c                 ó~   — t        t        j                  | «      «      }| |j                  d<   | |j                  d<   |S )zvGiven a single string which is output from an OCR routine, cleans it up using MRZ.ocr_cleanup and creates a MRZ objectÚtextÚraw_text)r   ÚMRZOCRCleanerÚapplyr	   )Úmrz_ocr_stringÚresults     r   Úfrom_ocrzMRZ.from_ocrt   s:   € ô ”]×(Ñ(¨Ó8Ó9ˆØ+ˆ�
‰
�6ÑØ!/ˆ�
‰
�:ÑØˆr   c           	      ó˜  — | j                   rRdj                  | j                  | j                  | j                  | j
                  | j                  | j                  «      S | j                  dkD  r]dj                  | j                  | j                  | j                  | j                  | j
                  | j                  | j                  «      S y)Nz(MRZ({0}[valid], {1}, {2}, {3}, {4}, {5})r   z&MRZ({0}[{1}], {2}, {3}, {4}, {5}, {6})zMRZ(invalid))	ÚvalidÚformatÚmrz_typeÚnumberÚnamesÚsurnameÚsexÚdate_of_birthÚvalid_score)r   s    r   Ú__repr__zMRZ.__repr__|   së   € Ø�:Š:Ø=×DÑDÀTÇ]Á]ÐTX×T_ÑT_Ðae×akÑakÐmq×myÑmyÐ{÷  |Dñ  |Dð  FJ÷  FXñ  FXó  Yð  YØ×Ñ Ò!Ø;×BÑBÀ4Ç=Á=ÐRV×RbÑRbÐdh×doÑdoÐqu×q{Ñq{ð  ~B÷  ~Jñ  ~Jð  LP÷  LTñ  LTð  VZ÷  Vhñ  Vhó  ið  ià!r   c                 ó2  — 	 t        | «      dk(  ryt        | «      dk(  r?t        | d   «      dk  r.t        | d   «      dk  r| d   d   j                  «       dk(  rdS d	S t        | «      dk(  r| d   d   j                  «       dk(  rd
S dS y# t        $ r Y yw xY w)aÚ  Guesses the type of the MRZ from given lines. Returns 'TD1', 'TD2', 'TD3', 'MRVA', 'MRVB' or None.
        The algorithm is basically just counting lines, looking at their length and checking whether the first character is a 'V'

        >>> MRZ._guess_type([]) is None
        True
        >>> MRZ._guess_type([1]) is None
        True
        >>> MRZ._guess_type([1,2]) is None  # No len() for numbers
        True
        >>> MRZ._guess_type(['a','b'])  # This way passes
        'TD2'
        >>> MRZ._guess_type(['*'*40, '*'*40])
        'TD3'
        >>> MRZ._guess_type([1,2,3])
        'TD1'
        >>> MRZ._guess_type(['V'*40, '*'*40])
        'MRVA'
        >>> MRZ._guess_type(['V'*36, '*'*36])
        'MRVB'
        é   ÚTD1é   r   é(   é   ÚVÚMRVBÚTD2ÚMRVAÚTD3N)ÚlenÚupperÚ	Exception)r   s    r   Ú_guess_typezMRZ._guess_type„   s«   € ð,
	Ü�9‹~ Ò"ØÜ�Y“ 1Ò$¬¨Y°q©\Ó):¸RÒ)?ÄCÈ	ÐRSÉÓDUÐXZÒDZØ!*¨1¡¨a¡×!6Ñ!6Ó!8¸CÒ!?�vÐJÀUÐJÜ�Y“ 1Ò$Ø!*¨1¡¨a¡×!6Ñ!6Ó!8¸CÒ!?�vÐJÀUÐJàøÜò 	Ùð	ús)   ‚B
 ‘A
B
 ÁB
 Á(B
 ÂB
 Â
	BÂBc                 ó  — t         j                  |«      | _        	 | j                  dk(  r | j                  |Ž | _        y | j                  dk(  r | j
                  |Ž | _        y | j                  dk(  r | j                  |Ž | _        y | j                  dk(  r | j                  |ddiŽ| _        y | j                  dk(  r | j                  |ddiŽ| _        y d	| _        d
| _        y # t        $ r d | _        d	| _        d
| _        Y y w xY w)Nr%   r+   r-   r,   Úlengthé,   r*   é$   Fr   )
r   r1   r   Ú
_parse_td1r   Ú
_parse_td2Ú
_parse_td3Ú
_parse_mrvr!   r0   r
   s     r   r   z
MRZ._parse¦   së   € ÜŸ™¨	Ó2ˆŒð	!Ø�}‰} Ò%Ø,˜TŸ_™_¨iÐ8�•
Ø—‘ %Ò'Ø,˜TŸ_™_¨iÐ8�•
Ø—‘ %Ò'Ø,˜TŸ_™_¨iÐ8�•
Ø—‘ &Ò(Ø,˜TŸ_™_¨iÐCÀÑC�•
Ø—‘ &Ò(Ø,˜TŸ_™_¨iÐCÀÑC�•
à"�”
Ø#$�Õ øÜò 	!Ø ˆDŒMØˆDŒJØ ˆDÖð	!ús/   œ#C% Á #C% Á$#C% Â&C% Â/&C% ÃC% Ã%DÄDc                 ór  — t        «       }| j                  |d<   | j                  |d<   d| j                  v r| j                  d   |d<   | j                  ��á| j                  |d<   | j
                  |d<   | j                  |d<   | j                  |d<   | j                  |d<   | j                  |d	<   | j                  |d
<   | j                  |d<   | j                  |d<   | j                  dk(  r| j                  |d<   | j                  |d<   n-| j                  dv r| j                  |d<   n| j                  |d<   | j                   |d<   | j"                  |d<   | j$                  |d<   | j                  dvr| j&                  |d<   | j                  dk(  r| j(                  |d<   | j*                  d   |d<   | j*                  d   |d<   | j*                  d   |d<   | j                  dvr| j*                  d   |d <   | j                  dk(  r| j*                  d!   |d"<   d#| j                  v r| j                  d#   |d#<   |S )$a±  Converts this object to an (ordered) dictionary of field-value pairs.

        >>> m = MRZ(['IDAUT10000999<6<<<<<<<<<<<<<<<', '7109094F1112315AUT<<<<<<<<<<<6', 'MUSTERFRAU<<ISOLDE<<<<<<<<<<<<']).to_dict()
        >>> assert m['type'] == 'ID' and m['country'] == 'AUT' and m['number'] == '10000999<'
        >>> assert m['valid_number'] and m['valid_date_of_birth'] and m['valid_expiration_date'] and not m['valid_composite']
        r   r!   r   ÚtypeÚcountryr   r    Úexpiration_dateÚnationalityr   r   r   r%   Ú	optional1Ú	optional2)r+   r,   r*   Úpersonal_numberÚcheck_numberÚcheck_date_of_birthÚcheck_expiration_date)r,   r*   Úcheck_compositer-   Úcheck_personal_numberr   Úvalid_numberr(   Úvalid_date_of_birthr&   Úvalid_expiration_dater$   Úvalid_compositeé   Úvalid_personal_numberÚmethod)r   r   r!   r	   r;   r<   r   r    r=   r>   r   r   r   r?   r@   rA   rB   rC   rD   rE   rF   Úvalid_check_digits)r   r   s     r   Úto_dictzMRZ.to_dict»   s?  € ô “ˆØ!Ÿ]™]ˆˆzÑØ $× 0Ñ 0ˆˆ}ÑØ˜Ÿ™Ñ!Ø!%§¡¨*Ñ!5ˆF�:ÑØ�=‰=Ñ$Ø!ŸY™YˆF�6‰NØ $§¡ˆF�9ÑØ#Ÿ{™{ˆF�8ÑØ&*×&8Ñ&8ˆF�?Ñ#Ø(,×(<Ñ(<ˆFÐ$Ñ%Ø$(×$4Ñ$4ˆF�=Ñ!Ø ŸH™HˆF�5‰MØ"Ÿj™jˆF�7‰OØ $§¡ˆF�9ÑØ�}‰} Ò%Ø&*§n¡n��{Ñ#Ø&*§n¡n��{Ò#Ø—‘Ð"9Ñ9Ø&*§n¡n��{Ò#à,0×,@Ñ,@�Ð(Ñ)Ø%)×%6Ñ%6ˆF�>Ñ"Ø,0×,DÑ,DˆFÐ(Ñ)Ø.2×.HÑ.HˆFÐ*Ñ+Ø�}‰}Ð$4Ñ4Ø,0×,@Ñ,@�Ð(Ñ)Ø�}‰} Ò%Ø26×2LÑ2L�Ð.Ñ/Ø%)×%<Ñ%<¸QÑ%?ˆF�>Ñ"Ø,0×,CÑ,CÀAÑ,FˆFÐ(Ñ)Ø.2×.EÑ.EÀaÑ.HˆFÐ*Ñ+Ø�}‰}Ð$4Ñ4Ø,0×,CÑ,CÀAÑ,F�Ð(Ñ)Ø�}‰} Ò%Ø26×2IÑ2IÈ!Ñ2L�Ð.Ñ/Ø˜4Ÿ8™8Ñ#Ø#'§8¡8¨HÑ#5��xÑ Øˆr   c                 óæ  — t        |«      t        |«      t        |«      }}}t        |«      dk  r|ddt        |«      z
  z  z   }t        |«      dk  r|ddt        |«      z
  z  z   }t        |«      dk  r|ddt        |«      z
  z  z   }|dd | _        |dd | _        |dd | _        |d   | _        |dd | _        |dd | _        |d   | _        |d	   | _        |d
d | _	        |d   | _
        |dd | _        |dd | _        |d   | _        |j                  dd«      }t        |«      dk  r|dgz  }|\  | _        | _        | j                   j#                  dd«      j%                  «       | _        | j                  j#                  dd«      j%                  «       | _        t&        j)                  | j                  «      | j                  k(  | _        t&        j)                  | j                  «      | j                  k(  xr t,        j/                  | j                  «      | _        t&        j)                  | j                  «      | j                  k(  xr t,        j/                  | j                  «      | _        t&        j)                  |dd |dd	 z   |d
d z   |dd z   «      | j                  k(  | _        | j*                  | j0                  | j2                  | j4                  g| _        |dk(  |dk(  |dk(  g| _        |d   dv g| _        dt=        | j6                  «      z  t=        | j8                  «      z   t=        | j:                  «      z   dz   | _        d| j>                  z  dz  | _        | j>                  dk(  S )Né   ú<r   r&   é   é   é   é   é   é   é   é   ú<<r(   Ú ú ÚIACé
   éd   é-   ) r.   r;   r<   r   rB   r?   r    rC   r   r=   rD   r>   r@   rE   Úsplitr   r   ÚreplaceÚstripÚMRZCheckDigitÚcomputerG   r   Ú_check_daterH   rI   rJ   rN   Úvalid_line_lengthsÚ
valid_miscÚsumr!   )r   ÚaÚbÚcÚlen_aÚlen_bÚlen_cÚsurname_namess           r   r6   zMRZ._parse_td1ë   sy  € Ü! !›f¤c¨!£f¬c°!«f�eˆuˆÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAØ�a˜�FˆŒ	Ø˜˜1�vˆŒØ˜˜"�gˆŒØ˜b™EˆÔØ˜2˜b˜ˆŒØ˜q ˜VˆÔØ#$ Q¡4ˆÔ Ø�Q‘4ˆŒØ   2˜wˆÔØ%& r¡UˆÔ"Ø˜R ˜8ˆÔØ˜2˜b˜ˆŒØ  ™uˆÔØŸ™  aÓ(ˆÜˆ}Ó Ò!Ø˜b˜TÑ!ˆMØ#0Ñ ˆŒ�d”jØ—Z‘Z×'Ñ'¨¨SÓ1×7Ñ7Ó9ˆŒ
Ø—|‘|×+Ñ+¨C°Ó5×;Ñ;Ó=ˆŒä)×1Ñ1°$·+±+Ó>À$×BSÑBSÑSˆÔÜ#0×#8Ñ#8¸×9KÑ9KÓ#LÐPT×PhÑPhÑ#hò  $QÔmp×m|Ñm|ð  ~B÷  ~Pñ  ~Pó  nQˆÔ Ü%2×%:Ñ%:¸4×;OÑ;OÓ%PÐTX×TnÑTnÑ%nò  &YÔsv÷  tCñ  tCð  DH÷  DXñ  DXó  tYˆÔ"Ü,×4Ñ4°Q°q¸°W¸qÀÀ1¸vÑ5EÈÈ!ÈBÈÑ5OÐRSÐTVÐWYÐRZÑ5ZÓ[Ð_c×_sÑ_sÑsˆÔØ#'×#4Ñ#4Ø#'×#;Ñ#;Ø#'×#=Ñ#=Ø#'×#7Ñ#7ð#9ˆÔð $)¨B¡;°¸±¸UÀb¹[Ð"IˆÔØ˜Q™4 5˜=˜/ˆŒØœc $×"9Ñ"9Ó:Ñ:¼SÀ×AXÑAXÓ=YÑYÔ\_Ð`d×`oÑ`oÓ\pÑpÐstÑtˆÔØ˜t×/Ñ/Ñ/°(Ñ;ˆÔØ×Ñ 3Ñ&Ð&r   c                 ój  — t        |«      t        |«      }}t        |«      dk  r|ddt        |«      z
  z  z   }t        |«      dk  r|ddt        |«      z
  z  z   }|dd | _        |dd | _        |dd j                  dd«      }t        |«      dk  r|dgz  }|\  | _        | _        | j
                  j                  dd	«      j                  «       | _        | j                  j                  dd	«      j                  «       | _        |dd
 | _        |d
   | _	        |dd | _
        |dd | _        |d   | _        |d   | _        |dd | _        |d   | _        |dd | _        |d   | _        t$        j'                  | j                  «      | j                  k(  | _        t$        j'                  | j                  «      | j                  k(  xr t*        j-                  | j                  «      | _        t$        j'                  | j                  «      | j                  k(  xr t*        j-                  | j                  «      | _        t$        j'                  |dd |dd z   |dd z   «      | j"                  k(  | _        | j(                  | j.                  | j0                  | j2                  g| _        |dk(  |dk(  g| _        |d   dv g| _        dt;        | j4                  «      z  t;        | j6                  «      z   t;        | j8                  «      z   dz   | _        d| j<                  z  dz  | _        | j<                  dk(  S )Nr5   rR   r   r&   rS   r[   r(   r\   r]   é	   r_   é   é   é   é   é   é   é#   ÚACIr`   r4   )r.   r;   r<   rb   r   r   rc   rd   r   rB   r>   r    rC   r   r=   rD   r?   rE   re   rf   rG   r   rg   rH   rI   rJ   rN   rh   ri   rj   r!   ©r   rk   rl   rn   ro   rq   s         r   r7   zMRZ._parse_td2  s3  € Ü˜1“vœs 1›vˆuˆÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAØ�a˜�FˆŒ	Ø˜˜1�vˆŒØ˜!˜B˜Ÿ™ d¨AÓ.ˆÜˆ}Ó Ò!Ø˜b˜TÑ!ˆMØ#0Ñ ˆŒ�d”jØ—Z‘Z×'Ñ'¨¨SÓ1×7Ñ7Ó9ˆŒ
Ø—|‘|×+Ñ+¨C°Ó5×;Ñ;Ó=ˆŒØ˜˜!�fˆŒØ˜a™DˆÔØ˜R ˜8ˆÔØ˜r "˜XˆÔØ#$ R¡5ˆÔ Ø�R‘5ˆŒØ   B˜xˆÔØ%& r¡UˆÔ"Ø˜2˜b˜ˆŒØ  ™uˆÔÜ)×1Ñ1°$·+±+Ó>À$×BSÑBSÑSˆÔÜ#0×#8Ñ#8¸×9KÑ9KÓ#LÐPT×PhÑPhÑ#hò  $QÔmp×m|Ñm|ð  ~B÷  ~Pñ  ~Pó  nQˆÔ Ü%2×%:Ñ%:¸4×;OÑ;OÓ%PÐTX×TnÑTnÑ%nò  &YÔsv÷  tCñ  tCð  DH÷  DXñ  DXó  tYˆÔ"Ü,×4Ñ4°Q°q¸°W¸qÀÀB¸xÑ5GÈ!ÈBÈrÈ(Ñ5RÓSÐW[×WkÑWkÑkˆÔØ#'×#4Ñ#4Ø#'×#;Ñ#;Ø#'×#=Ñ#=Ø#'×#7Ñ#7ð#9ˆÔð $)¨B¡;°¸±Ð"<ˆÔØ˜Q™4 5˜=˜/ˆŒØœc $×"9Ñ"9Ó:Ñ:¼SÀ×AXÑAXÓ=YÑYÔ\_Ð`d×`oÑ`oÓ\pÑpÐrsÑsˆÔØ˜t×/Ñ/Ñ/°(Ñ;ˆÔØ×Ñ 3Ñ&Ð&r   c                 ó\  — t        |«      t        |«      }}t        |«      dk  r|ddt        |«      z
  z  z   }t        |«      dk  r|ddt        |«      z
  z  z   }|dd | _        |dd | _        |dd j                  dd«      }t        |«      dk  r|dgz  }|\  | _        | _        | j
                  j                  dd	«      j                  «       | _        | j                  j                  dd	«      j                  «       | _        |dd
 | _        |d
   | _	        |dd | _
        |dd | _        |d   | _        |d   | _        |dd | _        |d   | _        |dd | _        |d   | _        |d   | _        t&        j)                  | j                  «      | j                  k(  | _        t&        j)                  | j                  «      | j                  k(  xr t,        j/                  | j                  «      | _        t&        j)                  | j                  «      | j                  k(  xr t,        j/                  | j                  «      | _        t&        j)                  |dd |dd z   |dd z   «      | j$                  k(  | _        | j"                  dk(  xs | j"                  dk(  xr | j                   dk(  xs, t&        j)                  | j                   «      | j"                  k(  | _        | j*                  | j0                  | j2                  | j4                  | j6                  g| _        |dk(  |dk(  g| _        |d   dv g| _        dt?        | j8                  «      z  t?        | j:                  «      z   t?        | j<                  «      z   dz   | _         d| j@                  z  dz  | _         | j@                  dk(  S )Nr4   rR   r   r&   rS   r[   r(   r\   r]   rs   r_   rt   ru   rv   rw   rx   ry   é*   é+   Ú0z<<<<<<<<<<<<<<ÚPr`   é6   )!r.   r;   r<   rb   r   r   rc   rd   r   rB   r>   r    rC   r   r=   rD   rA   rF   rE   re   rf   rG   r   rg   rH   rI   rJ   rL   rN   rh   ri   rj   r!   r|   s         r   r8   zMRZ._parse_td3;  s»  € Ü˜1“vœs 1›vˆuˆÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAÜˆq‹6�BŠ;Ø�C˜œc !›f™Ñ%Ñ%ˆAØ�a˜�FˆŒ	Ø˜˜1�vˆŒØ˜!˜B˜Ÿ™ d¨AÓ.ˆÜˆ}Ó Ò!Ø˜b˜TÑ!ˆMØ#0Ñ ˆŒ�d”jØ—Z‘Z×'Ñ'¨¨SÓ1×7Ñ7Ó9ˆŒ
Ø—|‘|×+Ñ+¨C°Ó5×;Ñ;Ó=ˆŒØ˜˜!�fˆŒØ˜a™DˆÔØ˜R ˜8ˆÔØ˜r "˜XˆÔØ#$ R¡5ˆÔ Ø�R‘5ˆŒØ   B˜xˆÔØ%& r¡UˆÔ"Ø   B˜xˆÔØ%& r¡UˆÔ"Ø  ™uˆÔÜ)×1Ñ1°$·+±+Ó>À$×BSÑBSÑSˆÔÜ#0×#8Ñ#8¸×9KÑ9KÓ#LÐPT×PhÑPhÑ#hò  $QÔmp×m|Ñm|ð  ~B÷  ~Pñ  ~Pó  nQˆÔ Ü%2×%:Ñ%:¸4×;OÑ;OÓ%PÐTX×TnÑTnÑ%nò  &YÔsv÷  tCñ  tCð  DH÷  DXñ  DXó  tYˆÔ"Ü,×4Ñ4°Q°q¸°W¸qÀÀB¸xÑ5GÈ!ÈBÈrÈ(Ñ5RÓSÐW[×WkÑWkÑkˆÔØ(,×(BÑ(BÀcÑ(IÒ(nÈT×MgÑMgÐknÑMnò  (]Ðtx÷  uIñ  uIð  M]ñ  u]ò 'pÜ&3×&;Ñ&;¸D×<PÑ<PÓ&QÐUY×UoÑUoÑ&oð 	Ô"à#'×#4Ñ#4Ø#'×#;Ñ#;Ø#'×#=Ñ#=Ø#'×#7Ñ#7Ø#'×#=Ñ#=ð	#?ˆÔð
 $)¨B¡;°¸±Ð"<ˆÔØ˜Q™4 3˜;˜-ˆŒØœc $×"9Ñ"9Ó:Ñ:¼SÀ×AXÑAXÓ=YÑYÔ\_Ð`d×`oÑ`oÓ\pÑpÐrsÑsˆÔØ˜t×/Ñ/Ñ/°(Ñ;ˆÔØ×Ñ 3Ñ&Ð&r   c                 óP   — 	 t        j                  | d«       y# t        $ r Y yw xY w)Nz%y%m%dTF)r   ÚstrptimeÚ
ValueError)Úymds    r   rg   zMRZ._check_datee  s,   € ð	Ü×Ñ˜c 8Ô,ØøÜò 	Ùð	ús   ‚ ™	%¤%c                 ó*  — t        |«      t        |«      }}t        |«      |k  r|ddt        |«      z
  z  z   }t        |«      |k  r|ddt        |«      z
  z  z   }|dd | _        |dd | _        |d| j                  dd«      }t        |«      dk  r|dgz  }|\  | _        | _        | j
                  j                  dd	«      j                  «       | _        | j                  j                  dd	«      j                  «       | _        |dd
 | _        |d
   | _	        |dd | _
        |dd | _        |d   | _        |d   | _        |dd | _        |d   | _        |d| | _        t"        j%                  | j                  «      | j                  k(  t"        j%                  | j                  «      | j                  k(  t"        j%                  | j                  «      | j                  k(  g| _        ||k(  ||k(  g| _        |d   dk(  g| _        dt-        | j&                  «      z  t-        | j(                  «      z   t-        | j*                  «      z   dz   | _        d| j.                  z  dz  | _        | j&                  \  | _        | _        | _        | j.                  dk(  S )NrR   r4   r   r&   rS   r[   r(   r\   r]   rs   r_   rt   ru   rv   rw   rx   ry   r)   r`   é"   )r.   r;   r<   rb   r   r   rc   rd   r   rB   r>   r    rC   r   r=   rD   r?   re   rf   rN   rh   ri   rj   r!   rG   rH   rI   )r   rk   rl   r3   rn   ro   rq   s          r   r9   zMRZ._parse_mrvm  sy  € Ü˜1“vœs 1›vˆuˆÜˆq‹6�FŠ?Ø�C˜œc !›f™Ñ%Ñ%ˆAÜˆq‹6�FŠ?Ø�C˜œc !›f™Ñ%Ñ%ˆAØ�a˜�FˆŒ	Ø˜˜1�vˆŒØ˜!˜F˜×)Ñ)¨$°Ó2ˆÜˆ}Ó Ò!Ø˜b˜TÑ!ˆMØ#0Ñ ˆŒ�d”jØ—Z‘Z×'Ñ'¨¨SÓ1×7Ñ7Ó9ˆŒ
Ø—|‘|×+Ñ+¨C°Ó5×;Ñ;Ó=ˆŒØ˜˜!�fˆŒØ˜a™DˆÔØ˜R ˜8ˆÔØ˜r "˜XˆÔØ#$ R¡5ˆÔ Ø�R‘5ˆŒØ   B˜xˆÔØ%& r¡UˆÔ"Ø˜2˜f˜ˆŒÜ#0×#8Ñ#8¸¿¹Ó#EÈ×IZÑIZÑ#ZÜ#0×#8Ñ#8¸×9KÑ9KÓ#LÐPT×PhÑPhÑ#hÜ#0×#8Ñ#8¸×9MÑ9MÓ#NÐRV×RlÑRlÑ#lð#nˆÔð $)¨F¡?°E¸V±OÐ"DˆÔØ˜Q™4 3™;˜-ˆŒØœc $×"9Ñ"9Ó:Ñ:¼SÀ×AXÑAXÓ=YÑYÔ\_Ð`d×`oÑ`oÓ\pÑpÐstÑtˆÔØ˜t×/Ñ/Ñ/°(Ñ;ˆÔØRV×RiÑRiÑOˆÔ˜4Ô3°TÔ5OØ×Ñ 3Ñ&Ð&r   N)r4   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ústaticmethodr   r"   r1   r   rO   r6   r7   r8   rg   r9   © r   r   r   r      sr   „ ñZòxð ñó ðò"ð ñó ðòB!ò*.ò`('òT$'òL('ðT ñó ðô'r   r   c                   ó>   — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ Ze	d„ «       Z
y)	r   a  
    The __call__ method of this class implements the "cleaning" of an OCR-obtained string in preparation for MRZ parsing.
    This is a singleton class, so rather than creating an instance, simply use its `apply` static method.



    >>> MRZOCRCleaner.apply('\nuseless lines\n  P<POLKOWALSKA < KWIATKOWSKA<<JOANNA<<<<<<<<<<<extrachars \n  AA0000000OP0L6OOzoB4Fi4iz3I4<<<<<<<<<<<<<<<4  \n  asdf  ')
    ['P<POLKOWALSKA<KWIATKOWSKA<<JOANNA<<<<<<<<<<<extrachars', 'AA00000000POL6002084F1412314<<<<<<<<<<<<<<<4']

    c           
      ó„   — g d¢}ddg}ddg}ddg}|||||dœ| _         dd	d
dddddœ}ddddddddddœ	}||||i dœ| _        y )N)za*AAA*********N***************znnnnnnnAnnnnnnnAAA***********nÚAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÚ$aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAz$*********nAAAnnnnnnnAnnnnnnn*******nÚ,aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAz,*********nAAAnnnnnnnAnnnnnnn**************nnz,*********nAAAnnnnnnnAnnnnnnn****************)r%   r+   r-   r,   r*   ÚOÚIÚZÚAÚSÚGÚB)r€   Ú1Ú2Ú4Ú5Ú6Ú8r    r€   rŸ   r›   rž   rœ   )	rš   ÚCÚDr™   r•   r”   ÚQr˜   r–   )rk   r—   ÚnÚNÚ*)ÚFORMATÚFIXERS)r   r%   r+   r-   ÚMRVrk   r¤   s          r   r   zMRZOCRCleaner.__init__›  sŠ   € òˆð ØHðJˆàØIðKˆàØAðCˆà!¨#°cÀ3ÐPSÑTˆŒð ˜C c°¸#ÀCÈcÑRˆØ˜C c°¸#ÀCÈcÐX[ÐbeÑfˆØ A¨A°A¸BÑ?ˆ�r   c                 ó�   — |j                  dd«      j                  d«      D �cg c]  }t        |«      dk\  sd|v sŒ|‘Œ c}S c c}w )Nr]   r\   ú
rv   r[   )rc   rb   r.   )r   r   Úlns      r   Ú_split_lineszMRZOCRCleaner._split_lines³  sC   € Ø+×3Ñ3°C¸Ó<×BÑBÀ4ÓHÖj�rÌSÐQSËWÐXZÊ]Ð^bÐfhÒ^h’ÒjÐjùÒjs
   ¤A¼Ac                 óº   — | j                  |«      }t        j                  |«      }|�2t        t	        |«      «      D ]  }| j                  ||   ||«      ||<   Œ |S )a  
        Given a string, which is output from an OCR routine, splits it into lines and performs various ad-hoc cleaning on those.
        In particular:
            - Spaces are removed
            - Lines shorter than 30 non-space characters are removed
            - The type of the document is guessed based on the number of lines and their lengths,
              if it is not-none, OCR-fixup is performed on a character-by-character basis depending on
              what characters are allowed at particular positions.
        )r­   r   r1   Úranger.   Ú	_fix_line)r   r   ÚlinesÚtpÚis        r   Ú__call__zMRZOCRCleaner.__call__¶  s^   € ð ×!Ñ! .Ó1ˆÜ�_‰_˜UÓ#ˆØˆ>Üœ3˜u›:Ó&ò ;�ØŸ>™>¨%°©(°B¸Ó:��a’ð;àˆr   c                 ó    — t        |«      }t        t        |«      «      D ]  }| j                  ||   |||«      ||<   Œ dj	                  |«      S )Nr\   )Úlistr¯   r.   Ú	_fix_charÚjoin)r   Úliner;   Úline_idxr¬   Újs         r   r°   zMRZOCRCleaner._fix_lineÇ  sL   € Ü�$‹ZˆÜ”s˜2“w“ò 	=ˆAØ—N‘N 2 a¡5¨$°¸!Ó<ˆBˆqŠEð	=à�w‰w�r‹{Ðr   c                 ó®   — | j                   |   |   }|t        |«      k\  r|S | j                  ||      }|j                  «       }|j	                  ||«      S )N)r§   r.   r¨   r/   Úget)r   Úcharr;   rº   Úchar_idxÚfmtÚfixers          r   r·   zMRZOCRCleaner._fix_charÍ  sT   € Ø�k‰k˜$Ñ Ñ)ˆØ”s˜3“xÒØˆKà—K‘K  H¡Ñ.ˆEØ—:‘:“<ˆDØ—9‘9˜T 4Ó(Ð(r   c                 ót   — t        t        dd «      €t        «       t        _        t        j                  | «      S ©NÚ__instance__)Úgetattrr   rÄ   ©Útxts    r   r   zMRZOCRCleaner.applyÖ  ó,   € ä”= .°$Ó7Ð?Ü)6«ŒMÔ&Ü×)Ñ)¨#Ó.Ð.r   N)r‰   rŠ   r‹   rŒ   r   r­   r´   r°   r·   r�   r   rŽ   r   r   r   r   �  s6   „ ñ	ò@ò0kòò"ò)ð ñ/ó ñ/r   r   c                   ó,   — e Zd ZdZd„ Zd„ Zed„ «       Zy)re   a‚  
    The algorithm used to compute "check digits" within MRZ.
    Its __call__ method, given a string, returns either the single character check digit.
    Rather than creating an instance every time, use the static compute(txt) method (which makes use of a singleton instance).

    # Valid codes
    >>> assert MRZCheckDigit.compute('0') == '0'
    >>> assert MRZCheckDigit.compute('0000000000') == '0'
    >>> assert MRZCheckDigit.compute('00A0A<0A0<<0A0A0<0A') == '0'
    >>> assert MRZCheckDigit.compute('111111111') == '3'
    >>> assert MRZCheckDigit.compute('111<<<111111') == '3'
    >>> assert MRZCheckDigit.compute('BBB<<<1B1<<<BB1') == '3'
    >>> assert MRZCheckDigit.compute('1<<1<<1<<1') == '8'
    >>> assert MRZCheckDigit.compute('1<<1<<1<<1') == '8'
    >>> assert MRZCheckDigit.compute('BCDEFGHIJ') == MRZCheckDigit.compute('123456789')

    # Invalid codes
    >>> assert MRZCheckDigit.compute('') == ''
    >>> assert MRZCheckDigit.compute('0000 0') == ''
    >>> assert MRZCheckDigit.compute('0 0') == ''
    >>> assert MRZCheckDigit.compute('onlylowercase') == ''
    >>> assert MRZCheckDigit.compute('BBb<<<1B1<<<BB1') == ''

    c                 ó$  — t        «       | _        t        d«      D ]  }|| j                  t        |«      <   Œ t        t	        d«      t	        d«      dz   «      D ]  }|dz
  | j                  t        |«      <   Œ d| j                  d<   g d¢| _        y )	Nr_   r—   r–   r(   é7   r   rR   )rW   r$   r(   )ÚdictÚCHECK_CODESr¯   ÚstrÚordÚchrÚCHECK_WEIGHTS)r   r³   s     r   r   zMRZCheckDigit.__init__÷  s…   € Ü›6ˆÔÜ�r“ò 	)ˆAØ'(ˆD×ÑœS ›VÒ$ð	)ä”s˜3“x¤ S£¨!¡Ó,ò 	.ˆAØ'(¨2¡vˆD×ÑœS ›VÒ$ð	.à !ˆ×Ñ˜ÑÚ&ˆÕr   c           
      óê   — |dk(  ryt        t        |«      D ��cg c]4  \  }}| j                  j                  |d«      | j                  |dz     z  ‘Œ6 c}}«      }|dk  ryt        |dz  «      S c c}}w )Nr\   iüÿÿr$   r   r_   )rj   Ú	enumeraterÍ   r½   rÑ   rÎ   )r   rÇ   r³   rm   Úress        r   r´   zMRZCheckDigit.__call__   st   € Ø�"Š9ØÜÔXaÐbeÓXf×gÑPTÐPQÐST�4×#Ñ#×'Ñ'¨¨5Ó1°$×2DÑ2DÀQÈÁUÑ2KÓKÓgÓhˆØ�Š7Øä�s˜R‘x“=Ð ùó	 hs   š9A/
c                 ót   — t        t        dd «      €t        «       t        _        t        j                  | «      S rÃ   )rÅ   re   rÄ   rÆ   s    r   rf   zMRZCheckDigit.compute	  rÈ   r   N)r‰   rŠ   r‹   rŒ   r   r´   r�   rf   rŽ   r   r   re   re   Ý  s%   „ ñò2'ò!ð ñ/ó ñ/r   re   N)rŒ   Úcollectionsr   r   Úobjectr   r   re   rŽ   r   r   ú<module>rØ      s<   ðñõ $Ý ô'ˆ&ô 'ôDK/�Fô K/ô\0/�Fõ 0/r   