§
    �Øj†+  ã                   ó|  — d Z ddlZddlZddlmZmZmZmZmZ 	 	 	 	 	 	 dd„Z	dddddd	œd
„Z
edk    r‰ddlZej        d         Z ej        e¦  «        ZdZeD ]$Ze e
ed¬¦  «        dz    ed¦  «        z   dz   z  ZŒ% ej        ej        › d�¦  «                             e                     ¦   «         ¦  «         dS dS )zò
This script accepts a PDF document filename and converts it to a text file.


Dependencies
-------------
PyMuPDF v1.24.2 or later

Copyright and License
----------------------
Copyright 2024 Artifex Software, Inc.
License GNU Affero GPL 3.0
é    N)Úis_whiteÚalmost_in_bboxÚare_disjointÚbbox_is_emptyÚTYPE3_FONT_NAMEé   Tc                 ó˜  — |}d„ }t          | t          j        ¦  «        s|€t          d¦  «        ‚|€	| �| j        }|€$d„ |                      ¦   «         d         D ¦   «         }g }t          |¦  «        D �]Z\  }	}
t          |
d         |¦  «        rŒt          |
d         ¦  «        D �]&\  }}t          |d         |¦  «        rŒ|d         }|rt          d	|d
         z
  ¦  «        dk    rŒDt          |d         ¦  «        D ]Ì\  }}t          |d         ¦  «        rŒ|d          
                    t          ¦  «        s|d         d
k    r|rŒJt          j        |d         ¦  «        }|d         d	         |_        |d         d         |_        ||d<   t          |d         |d¬¦  «        sŒ¨||d<   |	|d<   ||d<   |                     |¦  «         ŒÍ�Œ(�Œ\|sg S |                     d„ ¬¦  «         g }|d
         g}|d
         d         }|d	d…         D ]š}|d         }|d         d         }t          |j        |j        z
  ¦  «        |k    s t          |j        |j        z
  ¦  «        |k    r|                     |¦  «         ||z  }Œs ||¦  «        }|                     ||g¦  «         |g}|}Œ› ||¦  «        }|                     ||g¦  «         |S )a�  Extract the text spans from a TextPage in natural reading sequence.

    All spans roughly on the same line are joined to generate an improved line.
    This copes with MuPDF's algorithm that generates new lines also for spans
    whose horizontal distance is larger than some threshold.

    Result is a sorted list of line objects that consist of the recomputed line
    boundary box and the sorted list of spans in that line.

    This result can then easily be converted e.g. to plain text and other
    formats like Markdown or JSON.

    Args:
        textpage: TextPage object. Can be None if blocks are given.
        blocks: (list) if given, use these blocks instead of extracting them
              from the TextPage. This allows to re-use blocks extracted
              by the caller.
        clip: (Rect) specifies a sub-rectangle of the textpage rect (which in
              turn may be based on a sub-rectangle of the full page).
        tolerance: (float) put spans on the same line if their top or bottom
              coordinate differ by no more than this value.
        ignore_invisible: (bool) if True, invisible text is ignored. This may
              have been set to False for pages with OCR text.

    Returns:
        A sorted list of items (rect, [spans]), each representing one line. The
        spans are sorted left to right. Span dictionaries have been changed:
        - "bbox" has been converted to a Rect object
        - "line" (new) the line number in TextPage.extractDICT
        - "block" (new) the block number in TextPage.extractDICT
        This allows to detect where MuPDF has generated line breaks to indicate
        large inter-span distances.
    c                 óþ  — |                       d„ ¬¦  «         t          t          | ¦  «        dz
  dd¦  «        D ]Ã}| |dz
           }| |         }|d         dz  }|d         j        |z   |d         j        k     s(|d	         |d
         dz  f|d	         |d
         dz  fk    rŒh|d         |d         k    s|d         |d         k    r|dxx         |d         z  cc<   |dxx         |d         z  cc<   | |= || |dz
  <   ŒÄ| S )a†  Sort and join the spans in a re-synthesized line.

        The PDF may contain "broken" text with words cut into pieces.
        This funtion joins spans representing the particles and sorts them
        left to right.

        Arg:
            A list of spans - as derived from TextPage.extractDICT()
        Returns:
            A list of sorted, and potentially cleaned-up spans
        c                 ó   — | d         j         S )NÚbbox©Úx0©Úss    ú—/Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/pymupdf4llm/helpers/get_text_lines.pyú<lambda>z7get_raw_lines.<locals>.sanitize_spans.<locals>.<lambda>S   s   €   &¤	¤€ ó    ©Úkeyé   r   éÿÿÿÿÚsizegš™™™™™¹?r   ÚflagsÚ
char_flagséýÿÿÿÚtext)ÚsortÚrangeÚlenÚx1r   )ÚlineÚiÚs0Ús1Údeltas        r   Úsanitize_spansz%get_raw_lines.<locals>.sanitize_spansF   s>  € ð 	�	Š	Ð,Ð,ˆ	Ñ-Ô-Ð-õ •s˜4‘y”y 1‘} a¨Ñ,Ô,ð 	ð 	ˆAØ�a˜!‘e”ˆBØ�a”ˆBð �v”J Ñ$ˆEØ�&ŒzŒ}˜uÑ$ r¨&¤z¤}Ò4Ð4Ø�7”Ø�<Ô  2Ñ%ð9ð
 �7”Ø�<Ô  2Ñ%ðò	9ð 9ð ð �&Œz˜R œZÒ'Ð'¨2¨f¬:¸¸F¼Ò+CÐ+CØ�6�
�
”
˜b œjÑ(�
�
‘
ØˆvˆJˆJŒJ˜"˜Vœ*Ñ$ˆJˆJ‰JØ�Q�ØˆD��Q‘‰KˆKØˆr   Nz+Either textpage or blocks must be provided.c                 óT   — g | ]%}|d          dk    ¯t          |d         ¦  «        °#|‘Œ&S )Útyper   r   )r   )Ú.0Úbs     r   ú
<listcomp>z!get_raw_lines.<locals>.<listcomp>w   s=   € ð 
ð 
ð 
àØ�Œy˜AŠ~ˆ~¥m°A°f´IÑ&>Ô&>ˆ~ð àˆ~ˆ~r   Úblocksr   ÚlinesÚdirr   r   gü©ñÒMbP?Úspansr   ÚfontÚalphar   gR¸…ëQà?)Úportionr!   Úblockc                 ó:   — | d         d          | d         j         fS )Nr.   r   r   )Úy1r   s    r   r   zget_raw_lines.<locals>.<lambda>¤   s   € ˜q œx¨œ{˜l¨A¨f¬I¬LÐ9€ r   r   r   )Ú
isinstanceÚpymupdfÚTextPageÚ
ValueErrorÚrectÚextractDICTÚ	enumerater   Úabsr   Ú
startswithr   ÚRectÚy0r5   r   Úappendr   )Útextpager,   ÚclipÚ	toleranceÚignore_invisibleÚonly_horizontalÚy_deltar&   r/   Úbnor*   Úlnor!   Úline_dirÚsnor   ÚsbboxÚnlinesÚlrectÚsbbox0s                       r   Úget_raw_linesrP      sR  € ðR €Gð(ð (ð (õT �h¥Ô 0Ñ1Ô1ð H°f°nÝÐFÑGÔGÐGà€|˜Ð,ØŒ}ˆà€~ð
ð 
à×)Ò)Ñ+Ô+¨HÔ5ð
ñ 
ô 
ˆð
 €EÝ˜FÑ#Ô#ð " ñ " ‰ˆˆQÝ˜˜&œ	 4Ñ(Ô(ð 	ØÝ" 1 W¤:Ñ.Ô.ð 	 ñ 	 ‰IˆC�Ý˜D œL¨$Ñ/Ô/ð ØØ˜E”{ˆHàðÝ$'¨¨H°Q¬K©Ñ$8Ô$8¸4Ò$?Ð$?àÝ# D¨¤MÑ2Ô2ð  ð  ‘��QÝ˜A˜fœIÑ&Ô&ð àð ˜&œ	×,Ò,­_Ñ=Ô=ðà˜'œ
 aš˜Ø(ð (ð Ýœ Q v¤YÑ/Ô/�ð   œ<¨œ?�”Ø œ<¨œ?�”Ø!��&‘	Ý% a¨¤i°¸tÐDÑDÔDð àØ��&‘	Ø ��'‘
Ø#��%‘Ø—’˜Q‘”��ñ/ ñ	 ðB ð Øˆ	à	‡J‚JÐ9Ð9€JÑ:Ô:Ð:Ø€FØ�!ŒHˆ:€DØ�!ŒH�VÔ€Eà�1�2�2ŒYð ð ˆØ�&”	ˆØ�b”˜&Ô!ˆåˆuŒx˜&œ)Ñ#Ñ$Ô$¨Ò/Ð/µ3°u´xÀ&Ä)Ñ7KÑ3LÔ3LÐPWÒ3WÐ3WØ�KŠK˜‰NŒNˆNØ�U‰NˆEØð ˆ~˜dÑ#Ô#ˆð 	�Š�u˜d�mÑ$Ô$Ð$àˆsˆØˆˆð ˆ>˜$ÑÔ€DØ
‡M‚M�5˜$�-Ñ Ô Ð à€Mr   ú	F)rB   rC   ÚseprD   Úocrc                óü  — t           j        }|                      ¦   «          |s| j        nt          j        |¦  «        }|dk    r|nd}|€4|du r|                      ||¬¦  «        }n|                      dd¬¦  «        }n|}t          |||¬	¦  «        }	|sd}|	sdS d}
|sjd
}|	D ]^\  }}|d         d         }||k    r|
dz  }
|}|d         d         }|D ](}|d         }|d         }||k    r|
|z  }
n|
||z   z  }
|}Œ)|
dz  }
Œ_|
dz  }
|
S 	 g }g }d}|	D ]ô\  }}t          |¦  «        dk    r|s|
|d         d         dz   z  }
Œ/|s0d„ |D ¦   «         |d
         d         j	        gz   }t          |¦  «        }dg|z  }|D ]u\  }}t          t          |¦  «        dz
  ¦  «        D ]P}||         ||dz            }}t          |j        |z
  ¦  «        dk    st          |j	        |z
  ¦  «        dk    r|||<   ŒQŒv|                     |¦  «         Œõ|rtdd                     |d         ¦  «        z   dz   }|
|z  }
|
dt          |d         ¦  «        z  dz   z  }
|dd…         D ] }|
dd                     |¦  «        z   dz   z  }
Œ!|
dz  }
|
S )aü  Extract text by line keeping natural reading sequence.

    Notes:
        Internally uses "dict" to select lines and their spans.
        Returns plain text. If originally separate MuPDF lines in fact have
        (approximatly) the same baseline, they are joined into one line using
        the 'sep' character(s).
        This method can be used to extract text in reading sequence - even in
        cases of text replaced by way of redaction annotations.

    Args:
        page: (pymupdf.Page)
        textpage: (TextPage) if None a temporary one is created.
        clip: (rect-like) only consider spans inside this area
        sep: (str) use this string when joining multiple MuPDF lines.
    Returns:
        String of plain text in reading sequence.
    ú|Ú NF)rC   r   i,  T)ÚdpiÚfull)rC   rD   r   r   r3   ú
r!   r   r   z


c                 ó(   — g | ]}|d          j         ‘ŒS )r   r   )r)   r   s     r   r+   z"get_text_lines.<locals>.<listcomp>  s   € Ð2Ð2Ð2¨�q˜”y”|Ð2Ð2Ð2r   r   r   z|
z|---)r7   ÚTEXT_MEDIABOX_CLIPÚremove_rotationr:   r?   Úget_textpageÚget_textpage_ocrrP   r   r    r   r=   r   rA   Újoin)ÚpagerB   rC   rR   rD   rS   Ú	textflagsÚprectÚtpr-   ÚalltextÚprev_bnorN   r!   rH   Úline_nor   rI   ÚstextÚrowsÚxvaluesÚ	col_countÚrowÚrÚlr"   r   r    s                               r   Úget_text_linesrn   Â   sJ  € õ& Ô*€IØ×ÒÑÔÐØ!Ð9ˆDŒIˆI¥w¤|°DÑ'9Ô'9€Eà˜’�ˆ#ˆ# €Cð ÐØ�%ˆ<ˆ<Ø×"Ò"¨°YÐ"Ñ?Ô?ˆBˆBà×&Ò&¨3°TÐ&Ñ:Ô:ˆBˆBàˆå˜" 5°IÐ>Ñ>Ô>€Eàð Øˆàð Øˆrð €Gàð ØˆØ ð 	ð 	‰KˆE�4à�q”'˜'Ô"ˆCØ�hŠˆØ˜4‘�ØˆHà˜1”g˜f”oˆGØð ð �Ø˜”i�Ø˜&œ	�Ø˜c’>�>Ø˜uÑ$�G�Gà˜s U™{Ñ*�GØ��Ø�t‰OˆGˆGØ�4‰ˆØˆðð €DØ€GØ€IØð ð ‰ˆˆtåˆt‰9Œ9˜Š>ˆ> 'ˆ>Ø�t˜A”w˜v”¨Ñ1Ñ1ˆGØàð 	"Ø2Ð2¨TÐ2Ñ2Ô2°d¸2´h¸vÔ6FÔ6IÐ5JÑJˆGÝ˜D™	œ	ˆIØˆd�YÑˆØð 	ð 	‰DˆAˆqÝ�3˜w™<œ<¨!Ñ+Ñ,Ô,ð ð �Ø  œ W¨Q°©U¤^�B�Ý�q”t˜b‘y‘>”> QÒ&Ð&­#¨a¬d°R©i©.¬.¸AÒ*=Ð*=Ø�C˜‘Føðð 	�Š�CÑÔÐÐØð Ø�C—H’H˜T !œWÑ%Ô%Ñ%¨Ñ-ˆØ�3‰ˆØ�6�C  Q¤™LœLÑ(¨5Ñ0Ñ0ˆØ˜˜˜”8ð 	3ð 	3ˆCØ�s˜SŸXšX c™]œ]Ñ*¨UÑ2Ñ2ˆGˆGØ�4‰ˆØ€Nr   Ú__main__r   rV   ú )rR   rY   é   z.txt)NNNr   TT)Ú__doc__Úsysr7   Úpymupdf4llm.helpers.utilsr   r   r   r   r   rP   rn   Ú__name__ÚpathlibÚargvÚfilenameÚopenÚdocr   r`   ÚchrÚPathÚnameÚwrite_bytesÚencode© r   r   ú<module>r�      s~  ððð ð €
€
€
à €€€ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ØØ	ØØØðdð dð dð dðN &*°¸$È!ÐQVð `ð `ð `ð `ð `ðF ˆzÒÐØ€N€N€NàŒx˜Œ{€HØ
ˆ'Œ,�xÑ
 Ô
 €CØ€DØð Fð FˆØ��˜t¨Ð-Ñ-Ô-°Ñ4°s°s¸2±w´wÑ>ÀÑEÑEˆˆØ€G„L�C”HÐ"Ð"Ð"Ñ#Ô#×/Ò/°·²±´Ñ>Ô>Ð>Ð>Ð>ð Ðr   