§
    �WêjÈe  ã                   óÒ   — d Z ddlZddlZddlZddlmZ d(d„Z	 dZdZ	dZ
dZd	„ Zd
„ Zd„ Zd„ Zd„ Zd„ Zd)d„Zee	fd„Zd*d„Zd+d„Zd„ Zd,d„Zd„ Zd„ Z	 	 d-d „Zd!Zd"Zd#Zd$Zd%Zd.d'„Z dS )/z�
PDF parsing utilities - Base module
Maintained by: PDF Engineers
Purpose: Stable PDF element extraction (text, images, vectors, checkboxes)
é    N)ÚdefaultdictTc                 óB  — t           j                             d¦  «         |                      ¦   «         }|                     ¦   «         \  }}|d         dz  |d         dz  z  |d         z  }t          j        |¦  «        }d„ |                      ¦   «         D ¦   «         }g }g }	|D �]ø}
	 	 |r1|
d         |k    r|
d         d	|fv s|
d         |k    r|
d         d	|fv rŒ8|
d
         j        dk     r-|
d
         j        dk    r| 	                    |
d
         ¦  «         Œv|
d
         j        dk     r-|
d
         j        dk    r|	 	                    |
d
         ¦  «         Œ´|
d         D �]:}|d         dk    �r|dd	…         \  }}t          |j        |j        z
  ¦  «        dk    r[t          |j        |j        z
  ¦  «        dk    r;| 	                    t          j        ||¦  «                             ¦   «         ¦  «         nzt          |j        |j        z
  ¦  «        dk    rZt          |j        |j        z
  ¦  «        dk    r:|	 	                    t          j        ||¦  «                             ¦   «         ¦  «         �Œ|d         dk    r�Œ"|d         }| 	                    t          j        |j        |j        ¦  «                             ¦   «         ¦  «         | 	                    t          j        |j        |j        ¦  «                             ¦   «         ¦  «         |	 	                    t          j        |j        |j        ¦  «                             ¦   «         ¦  «         |	 	                    t          j        |j        |j        ¦  «                             ¦   «         ¦  «         �Œ<�Œú||	fS )a  Return horizontal and vertical vector lines on a page.

    Optionally, omit vectors that have the color of the background.
    The background color is determined as the most frequent color on a pixel
    level as determined from a rasterized image (pixmap) of the page.
    r   é   é   é   é   c                 óV   — g | ]&}|d          j         dk    s|d          j        dk    ¯$|‘Œ'S )Úrecté   )ÚheightÚwidth)Ú.0Úps     ú’/Users/tedpeters/Documents/Projects/Claude Projects/TPC Houses/house-viewer/.venv/lib/python3.11/site-packages/pymupdf/layout/pymupdf_util_base.pyú
<listcomp>z$get_vector_lines.<locals>.<listcomp>    sA   € ð ð ð Ø¨!¨F¬)Ô*:¸QÒ*>Ð*>À!ÀFÄ)Ä/ÐTUÒBUÐBUˆÐBUÐBUÐBUó    ÚfillÚcolorNr
   é   é   ÚitemsÚlÚre)ÚpymupdfÚTOOLSÚset_aa_levelÚ
get_pixmapÚcolor_topusageÚsRGB_to_pdfÚget_drawingsr   r   ÚappendÚabsÚyÚxÚRectÚ	normalizeÚtlÚtrÚblÚbr)ÚpageÚomit_invisibleÚpixÚportionr   ÚsRGBÚbg_colorÚpathsÚh_linesÚv_linesr   ÚitemÚp1Úp2Úrs                  r   Úget_vector_linesr8      sk  € õ „M×Ò˜qÑ!Ô!Ð!Ø
�/Š/Ñ
Ô
€CØ×'Ò'Ñ)Ô)�N€GˆUà�Œ8�r‰>˜E !œH¨™MÑ)¨E°!¬HÑ4€DåÔ" 4Ñ(Ô(€Hðð Ø×$Ò$Ñ&Ô&ðñ ô €Eð €GØ€Gàð %Añ %Aˆàð
	à"ð
	ð �&”	˜XÒ%Ð%Ø�g”J 4¨Ð"2Ð2Ð2Ø�W”: Ò)Ð)Ø�f”I $¨Ð!1Ð1Ð1ð àˆVŒ9Ô˜aÒÐ A f¤I¤O°bÒ$8Ð$8Ø�NŠN˜1˜Vœ9Ñ%Ô%Ð%ØØˆvŒYŒ_˜qÒ Ð  Q v¤YÔ%5¸Ò%:Ð%:Ø�NŠN˜1˜Vœ9Ñ%Ô%Ð%Øð �g”Jð 	Añ 	AˆDà�AŒw˜#Š~‰~Ø˜a˜b˜bœ‘��BÝ�r”t˜bœd‘{Ñ#Ô# qÒ(Ð(­S°´¸¼±Ñ-=Ô-=ÀÒ-BÐ-BØ—N’N¥7¤<°°BÑ#7Ô#7×#AÒ#AÑ#CÔ#CÑDÔDÐDÐDÝ˜œ ¤™Ñ%Ô%¨Ò*Ð*­s°2´4¸"¼$±;Ñ/?Ô/?À"Ò/DÐ/DØ—N’N¥7¤<°°BÑ#7Ô#7×#AÒ#AÑ#CÔ#CÑDÔDÐDÙØ�AŒw˜$ŠˆÙØ�Q”ˆAØ�NŠN�7œ<¨¬¨a¬dÑ3Ô3×=Ò=Ñ?Ô?Ñ@Ô@Ð@Ø�NŠN�7œ<¨¬¨a¬dÑ3Ô3×=Ò=Ñ?Ô?Ñ@Ô@Ð@Ø�NŠN�7œ<¨¬¨a¬dÑ3Ô3×=Ò=Ñ?Ô?Ñ@Ô@Ð@Ø�NŠN�7œ<¨¬¨a¬dÑ3Ô3×=Ò=Ñ?Ô?Ñ@Ô@Ð@Ñ@ñ	Að" �GÐÐr   g      @é@   é    c                 óÊ  — | j         | j        k    r| j        | j         fn| j         | j        f\  }}| j        | j        k    r| j        | j        fn| j        | j        f\  }}|j         |j        k    r|j        |j         fn|j         |j        f\  }}	|j        |j        k    r|j        |j        fn|j        |j        f\  }
}|||z
  k     s||	|z   k    s||
|z
  k     s	|||z   k    rdS dS )z?True if r1 and r2 are within tolerance (expanded-rect overlap).FT©Úx1Úx0Úy1Úy0)Úr1Úr2Údelta_xÚdelta_yÚrr1_x0Úrr1_x1Úrr1_y0Úrr1_y1Úrr2_x0Úrr2_x1Úrr2_y0Úrr2_y1s               r   Ú_are_neighborsrM   g   sð   € à')¤u¨r¬u¢} }�b”e˜RœU�^�^¸2¼5À"Ä%¸.�N€FˆFØ')¤u¨r¬u¢} }�b”e˜RœU�^�^¸2¼5À"Ä%¸.�N€FˆFØ')¤u¨r¬u¢} }�b”e˜RœU�^�^¸2¼5À"Ä%¸.�N€FˆFØ')¤u¨r¬u¢} }�b”e˜RœU�^�^¸2¼5À"Ä%¸.�N€FˆFà�˜'Ñ!Ò!Ð!Ø�F˜WÑ$Ò$Ð$Ø�F˜WÑ$Ò$Ð$Ø�F˜WÑ$Ò$Ð$àˆuØˆ4r   c                 ó†   — | j         |z
  }| j        |z   }| j        |z
  }| j        |z   }||k    r||}}||k    r||}}||||fS ©N)r>   r=   r@   r?   )r
   rC   rD   r>   r=   r@   r?   s          r   Ú_expanded_boundsrP   w   se   € Ø	Œ�7Ñ	€BØ	Œ�7Ñ	€BØ	Œ�7Ñ	€BØ	Œ�7Ñ	€BØ	ˆB‚w€wØ�RˆBˆØ	ˆB‚w€wØ�RˆBˆØ��B˜ÐÐr   c                 óÊ   — | j         | j        k    r| j        | j         fn| j         | j        f\  }}| j        | j        k    r| j        | j        fn| j        | j        f\  }}||||fS )z2Return normalized rect bounds as (x0, y0, x1, y1).r<   )r
   r>   r=   r@   r?   s        r   Ú_normalized_boundsrR   ƒ   sm   € à#'¤7¨d¬gÒ#5Ð#5ˆdŒg�t”wÐÐ¸D¼GÀTÄWÐ;M�F€BˆØ#'¤7¨d¬gÒ#5Ð#5ˆdŒg�t”wÐÐ¸D¼GÀTÄWÐ;M�F€BˆØ��B˜ÐÐr   c                 óÞ   — t           dz
  }t           dz
  }t          | |j        z
  |z  ¦  «        }t          ||j        z
  |z  ¦  «        }|dk     rd}n||k    r|}|dk     rd}n||k    r|}||fS )z?Map a point to a containment-grid cell, clamped to page bounds.r   r   )ÚCONTAINMENT_GRID_CELLSÚintr>   r@   )	ÚpxÚpyÚ	page_rectÚcell_wÚcell_hÚmax_xÚmax_yÚcxÚcys	            r   Ú_grid_cell_for_pointr_   Š   s�   € å" QÑ&€EÝ" QÑ&€EÝ	ˆb�9”<Ñ FÑ*Ñ	+Ô	+€BÝ	ˆb�9”<Ñ FÑ*Ñ	+Ô	+€BØ	ˆA‚v€vØˆˆØ	ˆeŠˆØˆØ	ˆA‚v€vØˆˆØ	ˆeŠˆØˆØ�ˆ8€Or   c                 óô   — t          | ||||¦  «        \  }}t          dz
  }t          dz
  }g }	dD ]H}
dD ]C}||
z   }||z   }d|cxk    r|k    r*n Œd|cxk    r|k    rn Œ,|	                     ||f¦  «         ŒDŒI|	S )a  
    Return the point cell plus its immediate neighbors.

    This preserves correctness for degenerate rects (zero width / height) whose
    center may lie exactly on a grid boundary while the accepted container was
    indexed into the adjacent cell.
    r   )éÿÿÿÿr   r   r   )r_   rT   r!   )rV   rW   rX   rY   rZ   r]   r^   r[   r\   ÚcellsÚdxÚdyÚnxÚnys                 r   Ú_candidate_cells_for_pointrg   ›   sÌ   € õ " " b¨)°V¸VÑDÔD�F€BˆÝ" QÑ&€EÝ" QÑ&€EØ€EØð 'ð 'ˆØð 	'ð 	'ˆBØ�b‘ˆBØ�b‘ˆBØ�BÐÐÒÐ˜%ÒÐÐÐÐ A¨Ð$4Ð$4Ò$4Ð$4¨uÒ$4Ð$4Ð$4Ð$4Ð$4Ø—’˜b "˜XÑ&Ô&Ð&øð		'ð
 €Lr   c              #   ó  K  — t          | ¦  «        \  }}}}t          dz
  }t          dz
  }	t          ||j        z
  |z  ¦  «        }
t          ||j        z
  |z  ¦  «        }t          ||j        z
  |z  ¦  «        }t          ||j        z
  |z  ¦  «        }|
dk     rd}
n|
|k    r|}
|dk     rd}n||k    r|}|dk     rd}n||	k    r|	}|dk     rd}||	k    r|	}t          |
|dz   ¦  «        D ]}t          ||dz   ¦  «        D ]}||fV — Œ	ŒdS )z-Yield containment-grid cells touched by rect.r   r   N)rR   rT   rU   r>   r@   Úrange)r
   rX   rY   rZ   r>   r@   r=   r?   r[   r\   Úcx0Úcx1Úcy0Úcy1r]   r^   s                   r   Ú_grid_keys_for_rectrn   °   sa  è è € å'¨Ñ-Ô-�N€BˆˆB�Ý" QÑ&€EÝ" QÑ&€EÝ
ˆr�I”LÑ  VÑ+Ñ
,Ô
,€CÝ
ˆr�I”LÑ  VÑ+Ñ
,Ô
,€CÝ
ˆr�I”LÑ  VÑ+Ñ
,Ô
,€CÝ
ˆr�I”LÑ  VÑ+Ñ
,Ô
,€CØ
ˆQ‚w€wØˆˆØ	ˆuŠˆØˆØ
ˆQ‚w€wØˆˆØ	ˆuŠˆØˆØ
ˆQ‚w€wØˆˆØ	ˆuŠˆØˆØ
ˆQ‚w€wØˆØ
ˆU‚{€{ØˆÝ�C˜˜q™Ñ!Ô!ð ð ˆÝ˜˜S 1™WÑ%Ô%ð 	ð 	ˆBØ�r�(ˆNˆNˆNˆNð	ðð r   éè  c                 óN  — | sg S t          |j        t          z  d¦  «        }t          |j        t          z  d¦  «        }i }g }| d|…         D ]×}|d         }t	          |¦  «        \  }	}
}}|	|z   dz  }|
|z   dz  }d}t          |||||¦  «        D ]3}|                     |d¦  «        D ]}|||         d         v rd} nŒ|r nŒ4|rŒzt          |¦  «        }|                     |¦  «         t          ||||¦  «        D ]&}||vrg ||<   ||                              |¦  «         Œ'ŒØ|S )a±  
    Faster equivalent of:
        if not any(p["rect"] in f["rect"] for f in filtered_paths):
            filtered_paths.append(p)

    Because candidates are processed in descending area, any containing rectangle
    is already in filtered_paths. A rectangle that contains the candidate must
    also contain the candidate's center point, so we query only the accepted
    rectangles whose coverage includes that point's cell.
    g      ð?Nr
   ç       @F© T)
Úmaxr   rT   r   rR   rg   ÚgetÚlenr!   rn   )Úall_picturesrX   ÚlimitrY   rZ   ÚgridÚfiltered_pathsr   r
   r>   r@   r=   r?   r]   r^   Ú	containedÚcellÚaccepted_idxÚkeys                      r   Ú_filter_contained_paths_fastr~   Î   s™  € ð ð Øˆ	å�”Õ#9Ñ9¸3Ñ?Ô?€FÝ�Ô!Õ$:Ñ:¸CÑ@Ô@€FØ€DØ€Nà˜&˜5˜&Ô!ð +ð +ˆØ�ŒyˆÝ+¨DÑ1Ô1‰ˆˆB��BØ�2‰g˜‰_ˆØ�2‰g˜‰_ˆØˆ	Ý.¨r°2°yÀ&È&ÑQÔQð 	ð 	ˆDØ $§¢¨¨rÑ 2Ô 2ð ð �ð ˜>¨,Ô7¸Ô?Ð?Ð?Ø $�IØ�Eð @ð ð Ø�ðàð 	Øå˜>Ñ*Ô*ˆØ×Ò˜aÑ Ô Ð Ý& t¨Y¸ÀÑGÔGð 	+ð 	+ˆCØ˜$ˆˆØ��S‘	Ø�ŒI×Ò˜\Ñ*Ô*Ð*Ð*ð	+ð
 Ðr   c                 ó.  ‡— | j         Šˆfd„|                      ¦   «         D ¦   «         }ˆfd„|                      ¦   «         D ¦   «         }t          d„ ||z   D ¦   «         d„ d¬¦  «        }t	          |‰d¬¦  «        }|sg S |                      |||¬	¦  «        S )
zJIdentify and group connected vectors on a page (optimized implementation).c                 ó&   •— g | ]}‰|d          z  ‘ŒS )r
   rr   )r   r   rX   s     €r   r   z(get_picture_clusters.<locals>.<listcomp>  s"   ø€ Ð@Ð@Ð@ qˆY˜˜6œÑ"Ð@Ð@Ð@r   c                 ó&   •— g | ]}‰|d          z  ‘ŒS ©Úbboxrr   )r   ÚimgrX   s     €r   r   z(get_picture_clusters.<locals>.<listcomp>  s"   ø€ ÐGÐGÐG¨#ˆi˜#˜fœ+Ñ%ÐGÐGÐGr   c                 óB   — g | ]}|j         d k    s|j        d k    ¯d|i‘ŒS )r   r
   ©r   r   )r   r7   s     r   r   z(get_picture_clusters.<locals>.<listcomp>  s-   € ÐNÐNÐN˜°´¸1²°ÀÄÈAÂÀˆ&�!ˆÀÀÀr   c                 ó8   — | d         j         | d         j        z  S )Nr
   r†   )r$   s    r   ú<lambda>z&get_picture_clusters.<locals>.<lambda>  s   € �a˜”i”o¨¨&¬	Ô(8Ñ8€ r   T©r}   Úreversero   )rw   )ÚdrawingsÚx_toleranceÚy_tolerance)r
   r    Úget_image_infoÚsortedr~   Úcluster_drawings)r+   rŒ   r�   r1   Úimagesrv   ry   rX   s          @r   Úget_picture_clustersr’   ý   sÐ   ø€ à”	€Ià@Ð@Ð@Ð@¨D×,=Ò,=Ñ,?Ô,?Ð@Ñ@Ô@€EØGÐGÐGÐG°×1DÒ1DÑ1FÔ1FÐGÑGÔG€FåØNÐN˜e f™nÐNÑNÔNØ8Ð8Øðñ ô €Lõ 2°,À	ÐQUÐVÑVÔV€Nàð Øˆ	à× Ò ØØØð !ñ ô ð r   Úhr   c                 óê  — |dk    r|                       d„ ¬¦  «         n|                       d„ ¬¦  «         g }| D �]7}|j        |j        |j        |j        f\  }}}}d}	|D ]ç}
|dk    rpt          |
j        |z
  ¦  «        |k    rT||
j        |z
  k     sF||
j        |z   k    s8t          |
j        |¦  «        |
_        t          |
j        |¦  «        |
_        d}	 nqŒxt          |
j        |z
  ¦  «        |k    rT||
j        |z
  k     sF||
j        |z   k    s8t          |
j        |¦  «        |
_        t          |
j        |¦  «        |
_        d}	 nŒè|	s'|                     t          j
        |¦  «        ¦  «         �Œ9|S )zú
    Merge fragmented lines into longer continuous lines.

    lines: list of line objects with x0, y0, x1, y1 attributes
    orientation: 'h' for horizontal, 'v' for vertical
    tolerance: allowable gap or offset to consider lines as mergeable
    r“   c                 ó   — | j         | j        fS rO   )r@   r>   ©r7   s    r   rˆ   zmerge_lines.<locals>.<lambda>!  ó   €  !¤$¨¬ € r   )r}   c                 ó   — | j         | j        fS rO   )r>   r@   r–   s    r   rˆ   zmerge_lines.<locals>.<lambda>#  r—   r   FT)Úsortr>   r@   r=   r?   r"   Úminrs   r!   ÚcopyÚdeepcopy)ÚlinesÚorientationÚ	toleranceÚmergedÚliner>   r@   r=   r?   ÚmatchedÚms              r   Úmerge_linesr¤     s“  € ð �cÒÐØ�
Š
Ð-Ð-ˆ
Ñ.Ô.Ð.Ð.à�
Š
Ð-Ð-ˆ
Ñ.Ô.Ð.à€FØð /ñ /ˆØœ $¤'¨4¬7°D´GÐ;‰ˆˆB��BØˆàð 	ð 	ˆAØ˜cÒ!Ð!å�q”t˜b‘y‘>”> YÒ.Ð.¸¸Q¼TÀIÑ=MÒ8MÐ8MÐQSÐVWÔVZÐ]fÑVfÒQfÐQfÝ˜qœt R™=œ=�A”DÝ˜qœt R™=œ=�A”DØ"�GØ�Eøõ �q”t˜b‘y‘>”> YÒ.Ð.¸¸Q¼TÀIÑ=MÒ8MÐ8MÐQSÐVWÔVZÐ]fÑVfÒQfÐQfÝ˜qœt R™=œ=�A”DÝ˜qœt R™=œ=�A”DØ"�GØ�Eøàð 	/à�MŠM�$œ-¨Ñ-Ô-Ñ.Ô.Ð.ùà€Mr   ç      è?c           	      óê  ‡— ˆfd„}t          t          ¦  «        }| D ]a}t          |j        |j        f¦  «        \  }}|j        |j        z   dz  }| ||¦  «         ||¦  «        f                              |¦  «         Œbt          t          ¦  «        }	|D ]j}t          |j        |j        f¦  «        \  }
}|j        |j        z   dz  }|	 ||
¦  «         ||¦  «        f          	                     ||¦  «        ¦  «         Œkg }| 
                    ¦   «         D ]Ü\  \  }}}||k    st          |¦  «        dk     rŒ"t          t          |¦  «        ¦  «        }t          t          |¦  «        ¦  «        D ]�}t          |dz   t          |¦  «        ¦  «        D ]^}||         ||         }}|	                      ||¦  «         ||¦  «        f¦  «        }|r!||v r||v r|                     ||||f¦  «         Œ_Œ‚ŒÝ|S )a>  
    Find fully-closed border rectangles (e.g. full-border tables) using
    exact coordinate matching -- NOT proximity/tolerance-based clustering
    like page.cluster_drawings(). See design discussion: clustering's
    tolerance is itself an unvalidated heuristic, and real tables are built
    in inconsistent ways (lines-only, filled cells, text-wrapping vectors),
    so no single tolerance is guaranteed to generalize. Exact-match closure
    instead only asserts something narrower and more defensible: "these
    lines were clearly drawn to form a rectangle" -- true regardless of how
    close together two SEPARATE closed rectangles happen to sit.

    Core idea: a full-border rectangle's outer boundary is formed by two
    horizontal lines sharing the exact same x-span, and two vertical lines
    sharing the exact same y-span (the horizontal pair's y-values),
    positioned at exactly those two x-coordinates.

    Args:
        h_lines: list of objects with x0, y0, x1, y1 (horizontal lines,
            y0 == y1 up to float noise). As returned by
            get_vector_lines()/merge_lines().
        v_lines: list of objects with x0, y0, x1, y1 (vertical lines,
            x0 == x1 up to float noise).
        tol: float. Coordinate-snapping tolerance to absorb float rounding
            noise ONLY (e.g. two lines both meant to be drawn at x=100.0
            landing at 100.0 and 100.03) -- NOT a proximity threshold for
            deciding whether two nearby-but-distinct coordinates should be
            treated as "close enough" structurally. Keep well below the
            smallest real gap you'd expect between two genuinely different
            table/cell edges.

    Returns:
        list of (x0, y_top, x1, y_bottom) tuples: one closed rectangle per
        matching horizontal-line x-span/vertical-line y-span pair found.
        Checks every pair of horizontal lines sharing an x-span (not just
        the global min/max y for that span), so two separate rectangles
        that happen to share the same width don't get silently merged into
        one spanning both.
    c                 ó.   •— t          | ‰z  ¦  «        ‰z  S rO   )Úround)ÚvÚtols    €r   Úsnapz&find_closed_border_rects.<locals>.snaph  s   ø€ Ý�Q˜‘W‰~Œ~ Ñ#Ð#r   rq   r   r   )r   Úlistr�   r>   r=   r@   r?   r!   ÚsetÚaddr   ru   ri   rt   )r2   r3   rª   r«   Ú	h_by_spanr7   r>   r=   r#   Ú	v_by_spanr@   r?   r$   Úclosed_rectsÚx0kÚx1kÚysÚ	ys_sortedÚaÚbÚy_topÚy_bottomÚv_xss     `                    r   Úfind_closed_border_rectsr»   A  s0  ø€ ðN$ð $ð $ð $ð $õ �DÑ!Ô!€IØð 2ð 2ˆÝ˜œ˜qœt˜Ñ%Ô%‰ˆˆBØŒT�A”D‰[˜CÑˆØ�4�4˜‘8”8˜T˜T "™XœXÐ&Ô'×.Ò.¨qÑ1Ô1Ð1Ð1å�CÑ Ô €IØð 5ð 5ˆÝ˜œ˜qœt˜Ñ%Ô%‰ˆˆBØŒT�A”D‰[˜CÑˆØ�4�4˜‘8”8˜T˜T "™XœXÐ&Ô'×+Ò+¨D¨D°©G¬GÑ4Ô4Ð4Ð4à€LØ#Ÿ/š/Ñ+Ô+ð 
Eð 
E‰‰
ˆˆc�BØ�#Š:ˆ:�˜R™œ 1š˜Øå�3˜r™7œ7‘O”Oˆ	Ý•s˜9‘~”~Ñ&Ô&ð 	Eð 	EˆAÝ˜1˜q™5¥# i¡.¤.Ñ1Ô1ð Eð E�Ø"+¨A¤,°	¸!´�x�Ø —}’} d d¨5¡k¤k°4°4¸±>´>Ð%BÑCÔC�Øð E˜C 4˜K˜K¨C°4¨K¨KØ ×'Ò'¨¨e°S¸(Ð(CÑDÔDÐDøð	Eð	Eð Ðr   c                 óÒ   — | d         | d         z   dz  }| d         | d         z   dz  }t          |¦  «        D ].\  }\  }}}}||cxk    r|k    rn Œ||cxk    r|k    rn Œ*|c S Œ/dS )aG  
    Which closed rect (from find_closed_border_rects) contains this bbox's
    center, if any. Returns the index into closed_rects, or -1 if the bbox
    isn't inside any of them. Used to build the 'same_rect_member' edge
    feature (see pymupdf_util_edge.get_edge_transform_yf): two nodes with
    the same (non -1) rect member id sit inside the same closed
    rectangle -- e.g. two cells of the same table -- while two nodes in
    different (or no) closed rectangles don't, even if they happen to be
    geometrically close (e.g. cells from two separate, adjacent tables).
    r   r   rq   r   r   ra   )Ú	enumerate)	rƒ   r±   r]   r^   Úidxr>   r@   r=   r?   s	            r   Úget_rect_member_idr¿   ‡  s—   € ð ˆqŒ'�D˜”GÑ
˜sÑ	"€BØ
ˆqŒ'�D˜”GÑ
˜sÑ	"€BÝ!*¨<Ñ!8Ô!8ð ð ÑˆÑˆb�"�b˜"Ø�ˆ>ˆ>Š>ˆ>�rŠ>ˆ>ˆ>ˆ>ˆ>˜b B˜n˜nšn˜n¨"šn˜n˜n˜n˜nØˆJˆJˆJøØˆ2r   ç      à?c           	      ó¶  — ddl m} g }| rÎ|                      d¦  «        }d}t          | ¦  «        D ]Ž\  }} |||¦  «        }||k    rwt	          |d         |d         ¦  «        t	          |d         |d         ¦  «        t          |d         |d         ¦  «        t          |d         |d         ¦  «        g}	|	| |<   d} nŒ�|s|                     |¦  «         | °Î|S )z8Merge overlapping bounding boxes based on IoU threshold.r   )Úcompute_iour   Fr   r   T)Úcommon_utilrÂ   Úpopr½   rš   rs   r!   )
ÚboxesÚiou_thresholdrÂ   r    ÚbaseÚ
has_mergedÚiÚotherÚiouÚnew_boxs
             r   Úmerge_boxesrÍ   š  s  € à(Ð(Ð(Ð(Ð(Ð(à€FØ
ð  Ø�yŠy˜‰|Œ|ˆØˆ
Ý! %Ñ(Ô(ð 	ð 	‰HˆAˆuØ�+˜d EÑ*Ô*ˆCØ�]Ò"Ð"å˜˜Qœ  q¤Ñ*Ô*Ý˜˜Qœ  q¤Ñ*Ô*Ý˜˜Qœ  q¤Ñ*Ô*Ý˜˜Qœ  q¤Ñ*Ô*ð	�ð #��a‘Ø!�
Ø�ð #ð ð 	 Ø�MŠM˜$ÑÔÐð! ð  ð" €Mr   c           	      ó¶  — |D �]T}|d         dk    rt          | ||||d         ¦  «         Œ)|d         dk    rŒ6|d         D �]}|d         d         }|d         d         }|d         d         }	|d         d         }
g }|d	         D ]}|                     |d
         ¦  «         Œd                     |¦  «                             ¦   «         }|dk    r„d|	k    r~||	k    rx||k    rrd|
k    rl||
k    rf||k    r`|||	|
g}|| d         vrP| d                              |¦  «         | d
                              |¦  «         |                     t          ¦  «         �Œ�ŒVdS )z"Extract text blocks from PDF page.Útyper   Úblocksr   r�   rƒ   r   r   ÚspansÚtextú Ú ÚbboxesN)Útext_extractr!   ÚjoinÚstripÚBOX_TEXT)Ú	data_dictÚbox_typeÚ
page_widthÚpage_heightrÐ   Úblockr¡   r=   r?   Úx2Úy2ÚtxtÚspanrƒ   s                 r   rÖ   rÖ   ³  sš  € àð .ñ .ˆØ�Œ=˜AÒÐÝ˜ H¨j¸+ÀuÈXÄÑWÔWÐWØØ�Œ=˜AÒÐØØ˜'”Nð 	.ñ 	.ˆDØ�f”˜a”ˆBØ�f”˜a”ˆBØ�f”˜a”ˆBØ�f”˜a”ˆBàˆCØ˜Wœð )ð )�Ø—
’
˜4 œ<Ñ(Ô(Ð(Ð(à—(’(˜3‘-”-×%Ò%Ñ'Ô'ˆCð �bŠyˆy˜Q "šW˜W¨¨rª¨°b¸JÒ6FÐ6FÈ1ÐPRÊ7È7ÐWYÐ]_ÒW_ÐW_ÐdfÐjuÒduÐduØ˜B  BÐ'�Ø˜y¨Ô2Ð2Ð2Ø˜hÔ'×.Ò.¨tÑ4Ô4Ð4Ø˜fÔ%×,Ò,¨SÑ1Ô1Ð1Ø—O’O¥HÑ-Ô-Ð-ùñ%	.ð.ð .r   c                 ó.   — |                       |¬¦  «        S )N)Úflags)Úget_textpage)r+   rä   s     r   Úcreate_stext_pageræ   Ð  s   € Ø×Ò 5ÐÑ)Ô)Ð)r   ©rÒ   Úrféô  éÈ   c                 ó^  — g g dddœ}g }| j         d         | j         d         }}||d<   ||d<   |                      ¦   «         }	t          j        |	j        t          j        ¬¦  «        }
|
                     |	j        |	j        |	j	        ¦  «        }||d<   	 dt          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  }t          j        d	k    r|t          j        z  }|t          j        z  }n|d
z  }t          j        dk    r|t          j        z  }|dk    rPdt          j        z  t          j        z  t          j        z  t          j        z  t          j        z  t          j        z  }t5          | |¦  «        }||d<   	 d|v �r<d„ |                      ¦   «         D ¦   «         }t?          |d¬¦  «        }|                      d„ d¬¦  «         |d|…         }|D ]è}tC          d|d         ¦  «        }tC          d|d         ¦  «        }tC          d|d         ¦  «        }tC          d|d         ¦  «        }d|cxk    r|cxk     r|k    rzn Œqd|cxk    r|cxk    r|k    rcn Œˆ||||g}||d         vrP|d          "                    |¦  «         |d          "                    d¦  «         | "                    tF          ¦  «         Œéd|v rätI          | ¦  «        }tK          |d„ d¬¦  «        d|…         }|D ]·}|j&        }|j'        }|j(        }|j)        }d|cxk    r|cxk     r|k    r…n Œ5d|cxk    r|cxk    r|k    rnn ŒL||k    r|dz   }||||g}||d         vrP|d          "                    |¦  «         |d          "                    d¦  «         | "                    tF          ¦  «         Œ¸d|v �rütU          | d¬¦  «        \  }}tW          |dd¬¦  «        }tW          |d d¬¦  «        }||f|d!<   tY          ||¦  «        |d"<   tK          |d#„ d¬¦  «        d|…         }|D ]·}|j&        }|j'        }|j(        }|j)        }d|cxk    r|cxk     r|k    r…n Œ5d|cxk    r|cxk    r|k    rnn ŒL||k    r|dz   }||||g}||d         vrP|d          "                    |¦  «         |d          "                    d¦  «         | "                    tZ          ¦  «         Œ¸tK          |d$„ d¬¦  «        d|…         }|D ]·}|j&        }|j'        }|j(        }|j)        }d|cxk    r|cxk    r|k    r…n Œ5d|cxk    r|cxk     r|k    rnn ŒL||k    r|dz   }||||g}||d         vrP|d          "                    |¦  «         |d          "                    d¦  «         | "                    t\          ¦  «         Œ¸d|v sd%|v rPd%|v r|  /                    d&¦  «        d'         }n|  /                    d&|¬(¦  «        d'         }ta          |||||¦  «         d)„ |  1                    ¦   «         D ¦   «         }|D ]¯\  }}|j&        }|j'        }|j(        }|j)        }d|cxk    r|cxk     r|k    rzn Œ8d|cxk    r|cxk     r|k    rcn ŒO||||g}||d         vrP|d          "                    |¦  «         |d          "                    d¦  «         | "                    td          ¦  «         Œ°||d*<   |S )+aÚ  
    Extract basic PDF elements without any feature extraction.

    Args:
        page: PyMuPDF page object
        input_type: Tuple of element types to extract ('text', 'image', 'picture_clusters', 'vec_line')
        max_image_num: Maximum number of images to extract
        max_vec_line_num: Maximum number of vector lines to extract

    Returns:
        data_dict: Dictionary with 'bboxes', 'text', 'box_type', 'page_width', 'page_height', 'image', 'stext_page'
    r   )rÕ   rÒ   rÜ   rÝ   r   r   rÜ   rÝ   )ÚdtypeÚimage)r   é   r   r9   )r   rî   r   ÚimfÚ
stext_pageFNc                 ó   — g | ]
}|d          ‘ŒS r‚   rr   )r   Úitms     r   r   z)extract_base_elements.<locals>.<listcomp>(  s   € ÐCÐCÐC c�c˜&”kÐCÐCÐCr   rÀ   )rÆ   c                 óH   — | d         | d         z
  | d         | d         z
  z  S )Nr   r   r   r   rr   )Úboxs    r   rˆ   z'extract_base_elements.<locals>.<lambda>*  s#   € ¨¨Q¬°#°a´&©¸SÀ¼VÀcÈ!Äf¹_Ñ(M€ r   Tr‰   r   rÕ   rÒ   rÔ   Úpicture_clustersc                 ó    — | j         | j        z  S rO   r†   r–   s    r   rˆ   z'extract_base_elements.<locals>.<lambda>>  s   € ˜!œ' A¤HÑ,€ r   Úvec_line)r,   r“   )rž   rŸ   r©   Úvector_linesr±   c                 ó    — | j         | j        z  S rO   r†   r–   s    r   rˆ   z'extract_base_elements.<locals>.<lambda>d  ó   € °´¸!¼(Ñ0B€ r   c                 ó    — | j         | j        z  S rO   r†   r–   s    r   rˆ   z'extract_base_elements.<locals>.<lambda>s  rú   r   Útext_pmÚdictrÐ   )Útextpagec                 óT   — g | ]%}|j         t          j        k    ¯|j        |j        f‘Œ&S rr   )Ú
field_typer   ÚPDF_WIDGET_TYPE_CHECKBOXr
   Úfield_value)r   Úwidgets     r   r   z)extract_base_elements.<locals>.<listcomp>‹  s?   € ð Ið Ið I¸ØÔ#¥wÔ'GÒGÐGð ”; Ô 2Ð3ØGÐGÐGr   rÛ   )3r
   r   ÚnpÚ
frombufferÚsamplesÚuint8Úreshaper   r   Únr   ÚTEXT_PRESERVE_IMAGESÚTEXT_PRESERVE_WHITESPACEÚTEXT_PRESERVE_LIGATURESÚTEXT_ACCURATE_BBOXESÚTEXT_COLLECT_VECTORSÚTEXT_COLLECT_STYLESÚTEXT_SEGMENTÚTEXT_PARAGRAPH_BREAKÚTEXT_COLLECT_STRUCTUREÚTEXT_TABLE_HUNTÚmupdf_version_tupleÚTEXT_LAZY_VECTORSÚ	TEXT_CLIPÚTEXT_FUZZY_VECTORSÚTEXT_INHIBIT_SPACESræ   ÚpprintÚppÚextractDICTrŽ   rÍ   r™   rs   r!   Ú	BOX_IMAGEr’   r�   r>   r@   r=   r?   r8   r¤   r»   Ú	BOX_HLINEÚ	BOX_VLINEÚget_textrÖ   ÚwidgetsÚBOX_CHECKBOX)r+   Ú
input_typeÚfeature_set_nameÚmax_image_numÚmax_vec_line_numrÚ   rÛ   rÜ   rÝ   r-   Ú
bytes_dataÚpage_imgÚstext_flagsrð   r  Ú
img_bboxesr
   r=   r?   rß   rà   rƒ   Ú
pic_bboxesr2   r3   rÐ   Ú
checkboxesÚvalues                               r   Úextract_base_elementsr-  Ô  s	  € ð ØØØð	ð €Ið €Hà"œi¨œl¨D¬I°a¬L�€JØ(€IˆlÑØ*€IˆmÑð �/Š/Ñ
Ô
€CÝ”˜sœ{µ"´(Ð;Ñ;Ô;€JØ×!Ò! #¤*¨c¬i¸¼Ñ?Ô?€HØ!€IˆgÑð :àÝÔ*ñ+åÔ.ñ/õ Ô-ñ.õ Ô*ñ	+õ
 Ô*ñ+õ Ô)ñ*õ Ô"ñ#õ Ô*ñ+õ Ô,ñ	-õ Ô%ñ
&ð õ Ô" jÒ0Ð0Ø•wÔ0Ñ0ˆØ•wÔ(Ñ(ˆˆð 	�rÑˆÝÔ" jÒ0Ð0Ø•wÔ1Ñ1ˆð ˜5Ò Ð àÝÔ2ñ3åÔ1ñ2õ Ô-ñ.õ Ô.ñ	/õ Ô.ñ/õ Ô0ñ	1ð 	õ # 4¨Ñ5Ô5€JØ(€IˆlÑð,ð �*ÐÑØCÐC¨T×-@Ò-@Ñ-BÔ-BÐCÑCÔCˆ
Ý  ¸3Ð?Ñ?Ô?ˆ
Ø�ŠÐMÐMÐW[ˆÑ\Ô\Ð\Ø   Ô/ˆ
àð 
	/ð 
	/ˆDÝ�Q˜˜Qœ‘”ˆBÝ�Q˜˜Qœ‘”ˆBÝ�Q˜˜Qœ‘”ˆBÝ�Q˜˜Qœ‘”ˆBØ�BÐ)Ð)Ò)Ð)˜Ð)Ð)Ò)Ð)˜zÒ)Ð)Ð)Ð)Ð)¨a°2Ð.JÐ.JÒ.JÐ.J¸Ð.JÐ.JÒ.JÐ.J¸{Ò.JÐ.JÐ.JÐ.JÐ.JØ˜B  BÐ'�Ø˜y¨Ô2Ð2Ð2Ø˜hÔ'×.Ò.¨tÑ4Ô4Ð4Ø˜fÔ%×,Ò,¨RÑ0Ô0Ð0Ø—O’O¥IÑ.Ô.Ð.øð ˜ZÐ'Ð'Ý)¨$Ñ/Ô/ˆ
ÝØØ,Ð,Øð
ñ 
ô 
ð ˆ=ˆ.ô	ˆ
ð ð 	/ð 	/ˆDØ”ˆBØ”ˆBØ”ˆBØ”ˆBØ�BÐ)Ð)Ò)Ð)˜Ð)Ð)Ò)Ð)˜zÒ)Ð)Ð)Ð)Ð)¨a°2Ð.JÐ.JÒ.JÐ.J¸Ð.JÐ.JÒ.JÐ.J¸{Ò.JÐ.JÐ.JÐ.JÐ.JØ˜’8�8Ø˜a™�BØ˜B  BÐ'�Ø˜y¨Ô2Ð2Ð2Ø˜hÔ'×.Ò.¨tÑ4Ô4Ð4Ø˜fÔ%×,Ò,¨RÑ0Ô0Ð0Ø—O’O¥IÑ.Ô.Ð.øð �ZÐÑÝ+¨DÀÐFÑFÔFÑˆ�Ý˜g°3À!ÐDÑDÔDˆÝ˜g°3À!ÐDÑDÔDˆð &-¨gÐ$6ˆ	�.Ñ!õ %=¸WÀgÑ$NÔ$Nˆ	�.Ñ!å˜Ð&BÐ&BÈDÐQÑQÔQÐRcÐScÐRcÔdˆØð 	/ð 	/ˆDØ”ˆBØ”ˆBØ”ˆBØ”ˆBØ�BÐ)Ð)Ò)Ð)˜Ð)Ð)Ò)Ð)˜zÒ)Ð)Ð)Ð)Ð)¨a°2Ð.JÐ.JÒ.JÐ.J¸Ð.JÐ.JÒ.JÐ.J¸{Ò.JÐ.JÐ.JÐ.JÐ.JØ˜’8�8Ø˜a™�BØ˜B  BÐ'�Ø˜y¨Ô2Ð2Ð2Ø˜hÔ'×.Ò.¨tÑ4Ô4Ð4Ø˜fÔ%×,Ò,¨RÑ0Ô0Ð0Ø—O’O¥IÑ.Ô.Ð.øå˜Ð&BÐ&BÈDÐQÑQÔQÐRcÐScÐRcÔdˆØð 	/ð 	/ˆDØ”ˆBØ”ˆBØ”ˆBØ”ˆBØ�BÐ*Ð*Ò*Ð*˜"Ð*Ð*Ò*Ð* 
Ò*Ð*Ð*Ð*Ð*¨q°BÐ/JÐ/JÒ/JÐ/J¸Ð/JÐ/JÒ/JÐ/J¸{Ò/JÐ/JÐ/JÐ/JÐ/JØ˜’8�8Ø˜a™�BØ˜B  BÐ'�Ø˜y¨Ô2Ð2Ð2Ø˜hÔ'×.Ò.¨tÑ4Ô4Ð4Ø˜fÔ%×,Ò,¨RÑ0Ô0Ð0Ø—O’O¥IÑ.Ô.Ð.øð �ÐÐ˜y¨JÐ6Ð6Ø˜
Ð"Ð"Ø—]’] 6Ñ*Ô*¨8Ô4ˆFˆFà—]’] 6°J�]Ñ?Ô?ÀÔIˆFÝ�Y ¨*°kÀ6ÑJÔJÐJðIð IÀ$Ç,Â,Á.Ä.ð Iñ Iô I€Jà!ð 
.ð 
.‰ˆˆeØŒWˆØŒWˆØŒWˆØŒWˆØ�Ð%Ð%Ò%Ð%�RÐ%Ð%Ò%Ð%˜:Ò%Ð%Ð%Ð%Ð%¨!¨rÐ*EÐ*EÒ*EÐ*E°BÐ*EÐ*EÒ*EÐ*E¸+Ò*EÐ*EÐ*EÐ*EÐ*EØ˜˜B Ð#ˆDØ˜9 XÔ.Ð.Ð.Ø˜(Ô#×*Ò*¨4Ñ0Ô0Ð0Ø˜&Ô!×(Ò(¨Ñ,Ô,Ð,Ø—’¥Ñ-Ô-Ð-øà$€IˆjÑØÐr   rÒ   rí   zh-line-vectorzv-line-vectorz	check-boxrÔ   c           	      ó  — | t           k    rdnd}| t          k    rdnd}| t          k    rdnd}| t          k    rdnd}t	          |¦  «        dk    r,t          d„ |D ¦   «         ¦  «        }|t	          |¦  «        z  }nd}| ||||||||z   dœS )uð   
    Create a custom_feature dict from a box_type string.

    This is the SINGLE SOURCE OF TRUTH for box_type â†’ flag mapping.
    All modules (pymupdf_util.py, pymupdf_util_ext.py) must use this
    instead of duplicating the logic.
    r   r   c              3   óB   K  — | ]}|                      ¦   «         ¯d V — ŒdS )r   N)Úisdigit)r   Úcs     r   ú	<genexpr>z&make_custom_feature.<locals>.<genexpr>³  s/   è è € Ð7Ð7˜a¨1¯9ª9©;¬;Ð7˜Ð7Ð7Ð7Ð7Ð7Ð7r   g        )rÛ   Ú	num_ratioÚis_textÚis_imageÚis_hline_vectorÚis_vline_vectorÚis_lineÚ	is_vector)rÙ   r  r  r  ru   Úsum)Úbox_type_strrÒ   r4  r5  r6  r7  Ú	num_countr3  s           r   Úmake_custom_featurer=  ¥  sÀ   € ð  ¥8Ò+Ð+ˆaˆa°€GØ ¥IÒ-Ð-ˆqˆq°1€HØ'­9Ò4Ð4�a�a¸!€OØ'­9Ò4Ð4�a�a¸!€Oå
ˆ4�y„y�1‚}€}ÝÐ7Ð7 4Ð7Ñ7Ô7Ñ7Ô7ˆ	Ø¥ D¡	¤	Ñ)ˆ	ˆ	àˆ	ð !ØØØØ*Ø*ØØ$ Ñ6ð	ð 	ð 	r   )T)ro   )r“   r   )r¥   )rÀ   )rç   rè   ré   rê   )rÔ   )!Ú__doc__r›   Únumpyr  r   Úcollectionsr   r8   ÚX_TOLERANCEÚY_TOLERANCEÚMAX_CELLS_FOR_GRIDrT   rM   rP   rR   r_   rg   rn   r~   r’   r¤   r»   r¿   rÍ   rÖ   ræ   r-  rÙ   r  r  r  r!  r=  rr   r   r   ú<module>rD     sÐ  ððð ð €€€Ø Ð Ð Ð Ø €€€Ø #Ð #Ð #Ð #Ð #Ð #ðAð Að Að AðH
ð €Ø€ð Ð ð Ð ðð ð ð 	ð 	ð 	ðð ð ðð ð ð"ð ð ð*ð ð ð<,ð ,ð ,ð ,ð^ ,7ÀKð ð ð ð ð4'ð 'ð 'ð 'ðTCð Cð Cð CðLð ð ð&ð ð ð ð2.ð .ð .ð:*ð *ð *ð HLØ>AðFð Fð Fð FðT €Ø€	Ø€	Ø€	Ø€ðð ð ð ð ð r   