§
    ŒWêjçZ  ã                   ó  — d Z ddlZddlmZmZmZ  G d„ d¦  «        Zddœd„Zd	dœd
„Zd	dœd„Z	d„ Z
d„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zddœd„Zd„ Zd„ Zd„ Zd„ Zd„ Zd „ Zd!„ Zd"„ Zd#„ Z d$„ Z!d%„ Z"dd&d'œd(„Z#dS ))aF  
Copyright (C) 2023 Artifex Software, Inc.

This file is part of PyMuPDF.

PyMuPDF is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

PyMuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.

You should have received a copy of the GNU Affero General Public License
along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>

Alternative licensing terms are available from the licensor.
For commercial licensing, see <https://www.artifex.com/> or contact
Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
CA 94129, USA, for further information.

---------------------------------------------------------------------

PyMuPDF table cell-span resolution (opt-in extension).

Provides SpanCell and resolve_spans (plus the _span_* helpers), which
reconstruct a detected table's merged-cell (colspan / rowspan) structure.
Re-exported by pymupdf.table; never runs on the default find_tables() path.
Reuses the word-selection helpers of pymupdf._table_refine.
é    N)Ú_refine_is_vertical_or_rotatedÚ_refine_page_wordsÚ_refine_word_candidatesc                   ó   — e Zd ZdZdd„ZdS )ÚSpanCella¶  One reconstructed table cell after span resolution (PyMuPDF extension).

    ``bbox`` is the placement's ``(x0, y0, x1, y1)`` union rect, ``text`` the
    page text it claims (lines joined by ``\n``), and ``colspan``/``rowspan``
    how many grid columns/rows it covers. resolve_spans always sets a real bbox;
    a caller padding its own grid may construct SpanCells with ``bbox=None``.

    ``tag`` is the cell's HTML tag (``"td"``/``"th"``), defaulting to ``"td"``.
    resolve_spans leaves it at the default; find_tables(refine=True) overwrites
    it from the resolved header region so Table.to_html() can serialize the grid
    directly, and a caller building its own grid may set it too.Útdc                 óL   — || _         || _        || _        || _        || _        d S ©N)ÚbboxÚtextÚcolspanÚrowspanÚtag)Úselfr   r   r   r   r   s         ú†/Users/tedpeters/Documents/Projects/Claude Projects/TPC Houses/house-viewer/.venv/lib/python3.11/site-packages/pymupdf/_table_spans.pyÚ__init__zSpanCell.__init__8   s)   € ØˆŒ	ØˆŒ	ØˆŒØˆŒØˆŒˆˆó    N)r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   © r   r   r   r   +   s4   € € € € € ð
Dð 
Dðð ð ð ð ð r   r   g      @)Ú	tolerancec                ó–   — g }t          | ¦  «        D ]6}|r||d         z
  |k    rŒ|                     t          |¦  «        ¦  «         Œ7|S )zÛGreedy 1-D clustering of edge coordinates into slot boundaries.

    Keeps a sorted value only when it is more than ``tolerance`` from the last
    kept boundary, so the first value of each run is the retained boundary.éÿÿÿÿ)ÚsortedÚappendÚfloat)Úvaluesr   Ú
boundariesÚvalues       r   Ú_span_clustered_boundariesr"   A   s_   € ð
 €JÝ˜‘”ð (ð (ˆØð 	˜% *¨R¤.Ñ0°IÒ=Ð=ØØ×Ò�% ™,œ,Ñ'Ô'Ð'Ð'ØÐr   ç      ð?c                óÎ   — d}t          t          |¦  «        dz
  ¦  «        D ]4}||         ||dz            z   dz  }| |z
  |cxk    r	||z   k    rn Œ/|dz  }Œ5t          d|¦  «        S )zKHow many [boundaries[i], boundaries[i+1]] slots the span start..end covers.r   é   ç       @)ÚrangeÚlenÚmax)ÚstartÚendr    r   ÚcountÚindexÚmidpoints          r   Ú_span_covered_slot_countr/   N   s‰   € à€EÝ•s˜:‘”¨Ñ*Ñ+Ô+ð ð ˆØ˜uÔ%¨
°5¸1±9Ô(=Ñ=ÀÑDˆØ�9Ñ Ð;Ð;Ò;Ð;¨C°)©OÒ;Ð;Ð;Ð;Ð;Ø�Q‰JˆEøÝˆq�%‰=Œ=Ðr   c                ó,  — g }t          t          |¦  «        dz
  ¦  «        D ]N}||         ||dz            z   dz  }| j        |z
  |cxk    r| j        |z   k    rn Œ9|                     |¦  «         ŒO|sdS t          |¦  «        t          |¦  «        dz   fS )zIThe half-open column-slot range (first, last+1) a rect's x-extent covers.r%   r&   )r   r%   )r'   r(   Úx0Úx1r   Úminr)   )ÚrectÚx_boundariesr   Úhitsr-   r.   s         r   Ú_span_slot_ranger7   X   s³   € à€DÝ•s˜<Ñ(Ô(¨1Ñ,Ñ-Ô-ð ð ˆØ  Ô'¨,°u¸q±yÔ*AÑAÀSÑHˆØŒ7�YÑ (ÐAÐAÒAÐA¨d¬g¸	Ñ.AÒAÐAÐAÐAÐAØ�KŠK˜ÑÔÐøØð ØˆvÝ�‰IŒI•s˜4‘y”y 1‘}Ð%Ð%r   c                 óJ   — | d         |d         k     o|d         | d         k     S ©Nr   r%   r   ©ÚaÚbs     r   Ú_span_ranges_intersectr=   d   s%   € ØˆQŒ4�!�A”$Š;Ð&˜1˜Qœ4 ! A¤$š;Ð&r   c                 óÒ   — t          |j        ¦  «        | cxk    ot          |j        ¦  «        k    nc o3t          |j        ¦  «        |cxk    ot          |j        ¦  «        k    nc S r
   )r   r1   r2   Úy0Úy1)ÚxÚyr4   s      r   Ú_span_point_in_rectrC   h   sg   € Ý�”‰>Œ>˜QÐ0Ð0Ò0Ð0¥%¨¬¡.¤.Ò0Ð0Ð0Ð0ÐZµU¸4¼7±^´^ÀqÐ5ZÐ5ZÒ5ZÐ5ZÍEÐRVÔRYÉNÌNÒ5ZÐ5ZÐ5ZÐ5ZÐZr   c           	      óè   — t          j        t          d„ | D ¦   «         ¦  «        t          d„ | D ¦   «         ¦  «        t          d„ | D ¦   «         ¦  «        t          d„ | D ¦   «         ¦  «        ¦  «        S )Nc              3   ó$   K  — | ]}|j         V — Œd S r
   )r1   ©Ú.0r4   s     r   ú	<genexpr>z#_span_rect_union.<locals>.<genexpr>n   ó$   è è € Ð&Ð&˜ˆDŒGÐ&Ð&Ð&Ð&Ð&Ð&r   c              3   ó$   K  — | ]}|j         V — Œd S r
   ©r?   rF   s     r   rH   z#_span_rect_union.<locals>.<genexpr>o   rI   r   c              3   ó$   K  — | ]}|j         V — Œd S r
   )r2   rF   s     r   rH   z#_span_rect_union.<locals>.<genexpr>p   rI   r   c              3   ó$   K  — | ]}|j         V — Œd S r
   ©r@   rF   s     r   rH   z#_span_rect_union.<locals>.<genexpr>q   rI   r   )ÚpymupdfÚRectr3   r)   )Úrectss    r   Ú_span_rect_unionrR   l   s~   € ÝŒ<ÝÐ&Ð& Ð&Ñ&Ô&Ñ&Ô&ÝÐ&Ð& Ð&Ñ&Ô&Ñ&Ô&ÝÐ&Ð& Ð&Ñ&Ô&Ñ&Ô&ÝÐ&Ð& Ð&Ñ&Ô&Ñ&Ô&ñ	ô ð r   c                 ól   — t          | j        |j        ¦  «        t          | j        |j        ¦  «        z
  S r
   )r3   r2   r)   r1   r:   s     r   Ú_span_x_overlaprT   u   s%   € ÝˆqŒt�Q”T‰?Œ?�S ¤ q¤t™_œ_Ñ,Ð,r   c                 ón   — t          | |¦  «        }|dk    rdS t          d|j        ¦  «        }||z  dk    S )a  True if a text span overlaps a cell by enough x to signal a merge.

    A few points of overlap is noise (a span drifting across a column line with
    trailing whitespace or a currency glyph); require >2pt and >=15% of the cell
    width before treating it as a merged-cell signal.r&   Fr#   g333333Ã?)rT   r)   Úwidth)Ú	span_rectÚ	cell_rectÚoverlapÚ
cell_widths       r   Ú_span_substantial_x_overlapr[   y   sB   € õ ˜i¨Ñ3Ô3€GØ�#‚~€~ØˆuÝ�S˜)œ/Ñ*Ô*€JØ�ZÑ 4Ò'Ð'r   c                 ó¼   — | sg S g }| d         x}}| dd…         D ])}||dz   k    r|}Œ|                      ||f¦  «         |x}}Œ*|                      ||f¦  «         |S )z>Collapse a sorted index list into (start, end) inclusive runs.r   r%   N)r   )ÚindicesÚrangesr*   r+   r-   s        r   Ú_span_contiguous_rangesr_   †   s�   € àð Øˆ	Ø€FØ˜!”*Ð€EˆCØ˜˜˜”ð ð ˆØ�C˜!‘GÒÐØˆCØØ�Š�u˜c�lÑ#Ô#Ð#ØÐˆ��Ø
‡M‚M�5˜#�,ÑÔÐØ€Mr   c                 óò   — g }t          | d„ ¬¦  «        D ]a\  }}}||k    rŒ|r||d         d         k    r|                     |||gf¦  «         Œ;|d         \  }}}|t          ||¦  «        ||gz   f|d<   Œb|S )zIMerge overlapping (start, end, text) intervals, accumulating their texts.c                 ó"   — | d         | d         fS r9   r   ©Úitems    r   ú<lambda>z'_span_merge_intervals.<locals>.<lambda>™   s   € ÀÀQÄÈÈaÌÐ?Q€ r   ©Úkeyr   r%   )r   r   r)   )Ú	intervalsÚmergedr*   r+   r   Ú
prev_startÚprev_endÚtextss           r   Ú_span_merge_intervalsrl   –   s«   € à€FÝ" 9Ð2QÐ2QÐRÑRÔRð Fð FÑˆˆs�DØ�CŠ<ˆ<ØØð 	˜ ¨¤¨A¤Ò.Ð.Ø�MŠM˜5 #¨ vÐ.Ñ/Ô/Ð/ØØ&,¨R¤jÑ#ˆ
�H˜eØ ¥# h°Ñ"4Ô"4°e¸t¸f±nÐEˆˆr‰
ˆ
Ø€Mr   c                 ól   — d„ |                       dg ¦  «        D ¦   «         }d                     |¦  «        S )z=Space-joined non-empty span texts of a get_text('dict') line.c                 óæ   — g | ]n}t          |                     d ¦  «        pd¦  «                             ¦   «         ¯8t          |                     d ¦  «        pd¦  «                             ¦   «         ‘ŒoS )r   Ú )ÚstrÚgetÚstrip©rG   Úspans     r   ú
<listcomp>z#_span_line_text.<locals>.<listcomp>§   sx   € ð ð ð àÝˆt�xŠx˜ÑÔÐ% 2Ñ&Ô&×,Ò,Ñ.Ô.ðÝˆD�HŠH�VÑÔÐ" Ñ#Ô#×)Ò)Ñ+Ô+ðð ð r   Úspansú )rq   Újoin)ÚlineÚpartss     r   Ú_span_line_textr{   ¥   s@   € ðð à—H’H˜W bÑ)Ô)ðñ ô €Eð
 �8Š8�E‰?Œ?Ðr   c                 óè   — |                       d¦  «        }|rt          j        |¦  «        }|j        s|S d„ |                       dg ¦  «        D ¦   «         }d„ |D ¦   «         }|sdS t	          |¦  «        S )zKBounding rect of a dict line (its bbox, else the union of its span bboxes).r   c                 óŠ   — g | ]@}|                      d ¦  «        ¯t          j        |                      d ¦  «        pg ¦  «        ‘ŒAS )r   )rq   rO   rP   rs   s     r   ru   z#_span_line_rect.<locals>.<listcomp>¶   sU   € ð ð ð àØ�8Š8�FÑÔðÝŒ�T—X’X˜fÑ%Ô%Ð+¨Ñ,Ô,ðð ð r   rv   c                 ó    — g | ]}|j         °	|‘ŒS r   )Úis_emptyrF   s     r   ru   z#_span_line_rect.<locals>.<listcomp>»   s   € Ð9Ð9Ð9�d¨4¬=Ð9ˆTÐ9Ð9Ð9r   N)rq   rO   rP   r   rR   )ry   r   r4   rQ   s       r   Ú_span_line_rectr€   ¯   s•   € à�8Š8�FÑÔ€DØð ÝŒ|˜DÑ!Ô!ˆØŒ}ð 	ØˆKðð à—H’H˜W bÑ)Ô)ðñ ô €Eð
 :Ð9˜eÐ9Ñ9Ô9€EØð ØˆtÝ˜EÑ"Ô"Ð"r   c                 óÊ  — t          | dd¦  «        }|�|S g }|                      d¦  «                             dg ¦  «        D ]~}|                     d¦  «        dvrŒ|                     dg ¦  «        D ]M}t          |¦  «        sŒt	          |¦  «        }|sŒ$t          |¦  «        }|€Œ6|                     ||f¦  «         ŒNŒ	 t          | d|¦  «         n# t          $ r Y nw xY w|S )zžVertical/rotated text lines as (rect, text), cached on the page.

    Selects non-horizontal lines whose reading order get_text('dict') already
    preserves.Ú_span_vertical_lines_cacheNÚdictÚblocksÚtype©Nr   Úlines)	ÚgetattrÚget_textrq   r   r{   r€   r   ÚsetattrÚ	Exception)ÚpageÚcachedr‡   Úblockry   r   r4   s          r   Ú_span_vertical_text_linesr�   Á   s&  € õ
 �TÐ7¸Ñ>Ô>€FØÐØˆØ€EØ—’˜vÑ&Ô&×*Ò*¨8°RÑ8Ô8ð 'ð 'ˆØ�9Š9�VÑÔ IÐ-Ð-ØØ—I’I˜g rÑ*Ô*ð 		'ð 		'ˆDÝ1°$Ñ7Ô7ð ØÝ" 4Ñ(Ô(ˆDØð ØÝ" 4Ñ(Ô(ˆDØˆ|ØØ�LŠL˜$ ˜Ñ&Ô&Ð&Ð&ð		'ðÝ�Ð2°EÑ:Ô:Ð:Ð:øÝð ð ð Øˆðøøøà€Ls   ÃC Ã
C ÃC c                 ó®  ‡‡— |sdS g }t          | ¦  «        D ]…\  }}t          |j        ¦  «        t          |j        ¦  «        z   dz  Št          |j        ¦  «        t          |j        ¦  «        z   dz  Št          ‰‰|¦  «        r|                     ||f¦  «         Œ†|sdS d}|D ]<\  }\  }}	}
}}||
z   dz  Š|	|z   dz  Št          ˆˆfd„|D ¦   «         ¦  «        r|dz  }Œ=|t          dt          |¦  «        ¦  «        z  dk     rdS t          d„ |D ¦   «         ¦  «        dk     rdS d	                     d
„ t          |d„ ¬¦  «        D ¦   «         ¦  «        S )a"  Text of vertical lines centered in rect, when they dominate the rect.

    Returns the stacked line text only if vertical lines are centered in the rect,
    cover >=60% of the rect's selected words, and carry >=2 tokens; else None so
    the caller falls back to horizontal line synthesis.Nç      à?r   c              3   ó@   •K  — | ]\  }}t          ‰‰|¦  «        V — Œd S r
   )rC   )rG   Ú	line_rectÚ_ÚcxÚcys      €€r   rH   z/_span_vertical_text_for_rect.<locals>.<genexpr>ò   s4   øè è € ÐUÐU¹,¸)ÀQÕ" 2 r¨9Ñ5Ô5ÐUÐUÐUÐUÐUÐUr   r%   g333333ã?c              3   ó^   K  — | ](\  }}t          |                     ¦   «         ¦  «        V — Œ)d S r
   )r(   Úsplit©rG   r”   r   s      r   rH   z/_span_vertical_text_for_rect.<locals>.<genexpr>ö   s6   è è € Ð
7Ð
7¡  D�3ˆt�zŠz‰|Œ|ÑÔÐ
7Ð
7Ð
7Ð
7Ð
7Ð
7r   é   ú
c              3   ó    K  — | ]	\  }}|V — Œ
d S r
   r   r™   s      r   rH   z/_span_vertical_text_for_rect.<locals>.<genexpr>ø   s:   è è € ð ð áˆAˆtð 	ðð ð ð ð ð r   c                 óŽ   — t          | d         j        ¦  «        t          | d         j        | d         j        z   dz  ¦  «         fS )Nr   r‘   )r   r1   r?   r@   rb   s    r   rd   z._span_vertical_text_for_rect.<locals>.<lambda>ü   s;   € �e D¨¤G¤JÑ/Ô/µ%¸¸a¼¼ÀdÈ1ÄgÄjÑ9PÐTWÑ8WÑ2XÔ2XÐ1XÐY€ r   re   )r�   r   r1   r2   r?   r@   rC   r   Úanyr)   r(   Úsumrx   r   )rŒ   r4   Úselected_wordsÚ
candidatesr“   r   Úvertical_hitsr”   Úwx0Úwy0Úwx1Úwy1r•   r–   s               @@r   Ú_span_vertical_text_for_rectr§   Þ   s¿  øø€ ð ð ØˆtØ€JÝ4°TÑ:Ô:ð 1ð 1‰ˆ	�4Ý�I”LÑ!Ô!¥E¨)¬,Ñ$7Ô$7Ñ7¸3Ñ>ˆÝ�I”LÑ!Ô!¥E¨)¬,Ñ$7Ô$7Ñ7¸3Ñ>ˆÝ˜r 2 tÑ,Ô,ð 	1Ø×Ò˜y¨$Ð/Ñ0Ô0Ð0øØð ØˆtØ€MØ&4ð ð Ñ"ˆÑ"ˆC��c˜3 Ø�C‰i˜3ÑˆØ�C‰i˜3ÑˆÝÐUÐUÐUÐUÐUÈ*ÐUÑUÔUÑUÔUð 	Ø˜QÑˆMøØ•s˜1�c .Ñ1Ô1Ñ2Ô2Ñ2°SÒ8Ð8ØˆtÝ
Ð
7Ð
7¨JÐ
7Ñ
7Ô
7Ñ7Ô7¸!Ò;Ð;ØˆtØ�9Š9ð ð åØØYÐYð
ñ 
ô 
ðñ ô ñ ô ð r   c           
      ó  — | sdS d„ | D ¦   «         }t          |¦  «        t          |¦  «        dz           }t          d|dz  ¦  «        }g }t          | d„ ¬¦  «        D ]»\  }}}}||z   dz  }	d}
|}|D ]1}t          |	t	          |d	         ¦  «        z
  ¦  «        }||k    r|}
|}Œ2|
€|                     |	||fgd
œ¦  «         Œe|
d                              ||f¦  «         t          |
d         ¦  «        }t	          |
d	         ¦  «        |dz
  z  |	z   |z  |
d	<   Œ¼g }t          |d„ ¬¦  «        D ]J}|                     d                     d„ t          |d         d„ ¬¦  «        D ¦   «         ¦  «        ¦  «         ŒKd                     |¦  «        S )a  Join center-point-selected cell words into text, re-synthesizing lines.

    ``words`` are (y0, x0, y1, text) tuples; they are grouped into lines by an
    adaptive median-height nearest-line rule, each line's words ordered by x, and
    the lines joined by newlines.ro   c                 ó>   — g | ]\  }}}}t          d ||z
  ¦  «        ‘ŒS )gš™™™™™¹?©r)   )rG   r?   r”   r@   s       r   ru   z,_span_words_to_line_text.<locals>.<listcomp>	  s.   € Ð;Ð;Ð;¡\ R¨¨B°�s�3˜˜R™Ñ Ô Ð;Ð;Ð;r   rš   r&   gš™™™™™á?c                 ó"   — | d         | d         fS r9   r   rb   s    r   rd   z*_span_words_to_line_text.<locals>.<lambda>  s   € ¸DÀ¼GÀTÈ!ÄWÐ;M€ r   re   NÚcenter_y)r¬   Úwordsr­   r%   c                 ó,   — t          | d         ¦  «        S )Nr¬   )r   rb   s    r   rd   z*_span_words_to_line_text.<locals>.<lambda>  s   € ­u°T¸*Ô5EÑ/FÔ/F€ r   rw   c              3   ó    K  — | ]	\  }}|V — Œ
d S r
   r   r™   s      r   rH   z+_span_words_to_line_text.<locals>.<genexpr>  s&   è è € Ð"gÐ"g©G¨A¨t 4Ð"gÐ"gÐ"gÐ"gÐ"gÐ"gr   c                 ó   — | d         S r†   r   rb   s    r   rd   z*_span_words_to_line_text.<locals>.<lambda>  s   € Ð^bÐcdÔ^e€ r   r›   )r   r(   r)   Úabsr   r   rx   )r­   ÚheightsÚmedian_heightÚline_thresholdr‡   r?   r1   r@   r   r–   Ú	best_lineÚbest_distancery   Údistancer,   Ú
text_liness                   r   Ú_span_words_to_line_textr¹     sä  € ð ð ØˆrØ;Ð;°UÐ;Ñ;Ô;€GÝ˜7‘O”O¥C¨¡L¤L°AÑ$5Ô6€MÝ˜˜m¨dÑ2Ñ3Ô3€NØ€EÝ" 5Ð.MÐ.MÐNÑNÔNð Zð ZÑˆˆB��DØ�2‰g˜‰_ˆØˆ	Ø&ˆØð 	)ð 	)ˆDÝ˜2¥ d¨:Ô&6Ñ 7Ô 7Ñ7Ñ8Ô8ˆHØ˜=Ò(Ð(Ø �	Ø (�øØÐØ�LŠL b°R¸°J°<Ð@Ð@ÑAÔAÐAØØ�'Ô×!Ò! 2 t *Ñ-Ô-Ð-Ý�I˜gÔ&Ñ'Ô'ˆÝ!& y°Ô'<Ñ!=Ô!=ÀÈÁÑ!KÈbÑ!PÐTYÑ Yˆ	�*ÑÐØ€JÝ�uÐ"FÐ"FÐGÑGÔGð ið iˆØ×Ò˜#Ÿ(š(Ð"gÐ"gµv¸dÀ7¼mÐQeÐQeÐ7fÑ7fÔ7fÐ"gÑ"gÔ"gÑgÔgÑhÔhÐhÐhØ�9Š9�ZÑ Ô Ð r   c                 ó†   — | \  }}}}}t          |¦  «        t          |¦  «        t          |¦  «        t          |¦  «        fS )zKReorder a (x0, y0, x1, y1, text) word to the (y0, x0, y1, text) line tuple.)r   rp   )Úwordr1   r?   Ú_x1r@   r   s         r   Ú_span_word_line_tupler½   "  s9   € à Ñ€BˆˆC��TÝ�"‰IŒI•u˜R‘y”y¥%¨¡)¤)­S°©Y¬YÐ7Ð7r   ©rŒ   c                óü   — g }t          || |¦  «        D ]g\  }}|\  }}}}	}
t          |
¦  «                             ¦   «         sŒ/||z   dz  }||	z   dz  }t          |||¦  «        r|                     ||f¦  «         Œh|S )aR  (index, word) pairs whose center lies in rect, index into ``page_words``.

    The index is what lets resolve_spans claim each page word for exactly one
    placement (an earlier cell's word is not re-claimed by a later one). ``page``
    only supplies the cached word-center index, which narrows the scan without
    changing the result.r‘   )r   rp   rr   rC   r   )Ú
page_wordsr4   rŒ   Úselectedr-   r»   r£   r¤   r¥   r¦   r   r•   r–   s                r   Ú_span_select_words_in_rectrÂ   (  sŸ   € ð €HÝ.¨t°ZÀÑFÔFð +ð +‰ˆˆtØ#'Ñ ˆˆS�#�s˜DÝ�4‰yŒy�ŠÑ Ô ð 	ØØ�C‰i˜3ÑˆØ�C‰i˜3ÑˆÝ˜r 2 tÑ,Ô,ð 	+Ø�OŠO˜U D˜MÑ*Ô*Ð*øØ€Or   c                 ó^   — t          | ||¦  «        }|�|S t          d„ |D ¦   «         ¦  «        S )zIText for a rect: vertical-line text if it dominates, else line synthesis.Nc                 ó2   — g | ]\  }}t          |¦  «        ‘ŒS r   )r½   )rG   r”   r»   s      r   ru   z-_span_words_text_for_rect.<locals>.<listcomp>@  s%   € Ð$_Ð$_Ð$_ÁWÀQÈÕ%:¸4Ñ%@Ô%@Ð$_Ð$_Ð$_r   )r§   r¹   )rŒ   r4   r    Úvertical_texts       r   Ú_span_words_text_for_rectrÆ   ;  s=   € å0°°t¸^ÑLÔL€MØÐ ØÐÝ#Ð$_Ð$_ÐP^Ð$_Ñ$_Ô$_Ñ`Ô`Ð`r   c                 óœ   ‡— ˆfd„t          ||| ¬¦  «        D ¦   «         }|D ]\  }}‰                     |¦  «         Œt          | ||¦  «        S )zKText of rect's words, skipping words already claimed and claiming the rest.c                 ó&   •— g | ]\  }}|‰v¯	||f‘ŒS r   r   )rG   r-   r»   Úclaimed_wordss      €r   ru   z,_span_claim_text_in_rect.<locals>.<listcomp>E  s6   ø€ ð ð ð áˆE�4Ø˜Ð%Ð%ð 
�ˆà%Ð%Ð%r   r¾   )rÂ   ÚaddrÆ   )rŒ   r4   rÀ   rÉ   rÁ   r-   r”   s      `   r   Ú_span_claim_text_in_rectrË   C  sx   ø€ ðð ð ð å5°jÀ$ÈTÐRÑRÔRðñ ô €Hð
 ð !ð !‰ˆˆqØ×Ò˜%Ñ Ô Ð Ð Ý$ T¨4°Ñ:Ô:Ð:r   c                 ó`  — t          | dd¦  «        }|�|S g }|                      d¦  «                             dg ¦  «        D ]É}|                     dg ¦  «        D ]°}|                     dg ¦  «        D ]—}t          |                     d¦  «        pd¦  «                             ¦   «         }t          |¦  «        d	k     rŒL|                     d
¦  «        }|sŒdt          j        |¦  «        }|j        rŒ€| 	                    ||f¦  «         Œ˜Œ±ŒÊ	 t          | d|¦  «         n# t          $ r Y nw xY w|S )zÕPage text spans as (rect, text), length>=2, cached on the page.

    These drive merged-cell detection: a single span whose x-extent crosses a
    grid column line signals cells the line grid split but text joins.Ú_span_text_spans_cacheNrƒ   r„   r‡   rv   r   ro   rš   r   )rˆ   r‰   rq   rp   rr   r(   rO   rP   r   r   rŠ   r‹   )	rŒ   r�   rv   rŽ   ry   rt   r   r   r4   s	            r   Ú_span_text_spansrÎ   O  sb  € õ
 �TÐ3°TÑ:Ô:€FØÐØˆØ€EØ—’˜vÑ&Ô&×*Ò*¨8°RÑ8Ô8ð +ð +ˆØ—I’I˜g rÑ*Ô*ð 	+ð 	+ˆDØŸš ¨"Ñ-Ô-ð 
+ð 
+�Ý˜4Ÿ8š8 FÑ+Ô+Ð1¨rÑ2Ô2×8Ò8Ñ:Ô:�Ý�t‘9”9˜q’=�=ØØ—x’x Ñ'Ô'�Øð ØÝ”| DÑ)Ô)�Ø”=ð ØØ—’˜d D˜\Ñ*Ô*Ð*Ð*ð
+ð	+ðÝ�Ð.°Ñ6Ô6Ð6Ð6øÝð ð ð Øˆðøøøà€Ls   ÄD Ä
D+Ä*D+c                 óÒ  ‡
— t          | ¦  «        dk     rg S t          d„ | D ¦   «         ¦  «        }t          d„ | D ¦   «         ¦  «        }g }|D ]Œ\  Š
}‰
j        ‰
j        z   dz  }||dz
  k     s	||dz   k    rŒ*ˆ
fd„t          | ¦  «        D ¦   «         }t          |¦  «        dk     rŒYt          |¦  «        D ]#\  }}	|	|k    r|                     ||	|f¦  «         Œ$Œ�t          |¦  «        S )aN  Column ranges within one grid row that a single text span crosses.

    ``entries`` are the row's cell rects (in column order). A text span centered
    in the row band and substantially overlapping >=2 adjacent cells marks those
    cells for merging; overlapping intervals are merged. Returns
    (start, end, texts) with end>start.rš   c              3   ó$   K  — | ]}|j         V — Œd S r
   rK   ©rG   Úentrys     r   rH   z+_span_crossing_intervals.<locals>.<genexpr>u  ó$   è è € Ð/Ð/˜e�”Ð/Ð/Ð/Ð/Ð/Ð/r   c              3   ó$   K  — | ]}|j         V — Œd S r
   rN   rÑ   s     r   rH   z+_span_crossing_intervals.<locals>.<genexpr>v  rÓ   r   r&   r#   c                 ó:   •— g | ]\  }}t          ‰|¦  «        ¯|‘ŒS r   )r[   )rG   ÚpositionrÒ   rW   s      €r   ru   z,_span_crossing_intervals.<locals>.<listcomp>|  s=   ø€ ð 
ð 
ð 
á�˜%Ý*¨9°eÑ<Ô<ð
Øð
ð 
ð 
r   )	r(   r3   r)   r?   r@   Ú	enumerater_   r   rl   )ÚentriesÚ
text_spansÚrow_y0Úrow_y1rg   Ú	span_textr¬   Úhit_positionsr*   r+   rW   s             @r   Ú_span_crossing_intervalsrÞ   l  s<  ø€ õ ˆ7�|„|�aÒÐØˆ	ÝÐ/Ð/ wÐ/Ñ/Ô/Ñ/Ô/€FÝÐ/Ð/ wÐ/Ñ/Ô/Ñ/Ô/€FØ€IØ *ð :ð :Ñˆ	�9Ø”L 9¤<Ñ/°3Ñ6ˆØ�f˜s‘lÒ"Ð" h°¸#±Ò&=Ð&=Øð
ð 
ð 
ð 
å#,¨WÑ#5Ô#5ð
ñ 
ô 
ˆõ
 ˆ}ÑÔ Ò!Ð!ØÝ1°-Ñ@Ô@ð 	:ð 	:‰JˆE�3Ø�UŠ{ˆ{Ø× Ò  %¨¨iÐ!8Ñ9Ô9Ð9øð	:õ ! Ñ+Ô+Ð+r   c                 óš   — | |k    rdS |D ]?}|d         dvrŒ|d         | k    s|d         |k    rŒ&t          ||d         ¦  «        r dS Œ@dS )NFÚrole>   ÚheaderÚheader_leafÚheader_groupÚrowÚcolsT©r=   )Úrow_idxrå   ÚbaseÚ
body_startÚothers        r   Ú_span_is_leaf_header_rangerë   �  s{   € Ø�*ÒÐØˆuØð ð ˆØ�Œ=Ð IÐIÐIØØ�Œ<˜7Ò"Ð" e¨E¤l°jÒ&@Ð&@ØÝ! $¨¨f¬Ñ6Ô6ð 	Ø�5�5ð	àˆ4r   c                 óŽ  — g }t          | ¦  «        D ]{\  }}t          d„ |D ¦   «         ¦  «        D ]\\  }}t          j        |¦  «        }t          ||¦  «        }	|                     ||||	|	d         |	d         z
  ||k    rdnddœ¦  «         Œ]Œ||D ]4}
|
d         dk    rŒt          |
d         |
d	         ||¦  «        rd
nd|
d<   Œ5|S )a
  Describe every grid cell by row/slot-range/role for the strict-colspan gate.

    Header cells split into ``header_leaf`` (no header cell below within body) vs
    ``header_group`` (spans over a lower leaf), which the reject rules compare
    against the body split.c                 ó   — g | ]}|®|‘ŒS r
   r   ©rG   Úcells     r   ru   z*_span_build_base_cells.<locals>.<listcomp>¢  s   € Ð(RÐ(RÐ(R°$ÀÐAQ¨ÐAQÐAQÐAQr   r%   r   Úbodyrá   )rä   rÖ   r4   rå   r   rà   rà   rä   rå   râ   rã   )r×   rO   rP   r7   r   rë   )Úcellsr5   ré   rè   rç   rä   rÖ   rï   r4   rå   rc   s              r   Ú_span_build_base_cellsrò   š  s  € ð €DÝ! %Ñ(Ô(ð ð ‰ˆ�Ý'Ð(RÐ(R¸#Ð(RÑ(RÔ(RÑSÔSð 	ð 	‰NˆH�dÝ”< Ñ%Ô%ˆDÝ# D¨,Ñ7Ô7ˆDØ�KŠKà"Ø (Ø Ø Ø# Aœw¨¨a¬Ñ0Ø&-°Ò&;Ð&;˜F˜FÀðð ñ	ô 	ð 	ð 	ð	ð ð 
ð 
ˆØ�Œ<˜8Ò#Ð#Øõ *¨$¨u¬+°t¸F´|ÀTÈ:ÑVÔVð ˆMˆMàð 	ˆV‰ˆð
 €Kr   c                 ó^  ‡ ‡‡— t          ˆfd„|D ¦   «         ¦  «        }|D ]‹Šˆ ˆfd„|D ¦   «         }|sŒt          |¦  «        dk    r|d         d         ‰ k    rŒ:t          ˆ fd„|D ¦   «         ¦  «        }t          ˆ fd„|D ¦   «         ¦  «        }|‰ d         k    r|‰ d         k    r dS ŒŒd	S )
zDTrue if some body row splits the column range ``cols`` into >1 cell.c                 óP   •— h | ]"}|d          dk    ¯|d         ‰k    ¯|d         ’Œ#S )rà   rð   rä   r   )rG   rc   ré   s     €r   ú	<setcomp>z._span_body_rows_split_under.<locals>.<setcomp>¼  s=   ø€ ÐmÐmÐm¨¸¸V¼ÈÒ8NÐ8NÐSWÐX]ÔS^ÐblÒSlÐSl˜˜UœÐSlÐSlÐSlr   c                 óp   •— g | ]2}|d          dk    ¯|d         ‰k    ¯t          ‰|d         ¦  «        ¯0|‘Œ3S )rà   rð   rä   rå   ræ   )rG   rc   rå   rç   s     €€r   ru   z/_span_body_rows_split_under.<locals>.<listcomp>¾  sX   ø€ ð 
ð 
ð 
àØ�FŒ|˜vÒ%Ð%¨$¨u¬+¸Ò*@Ð*@ÕE[Ð\`ÐbfÐgmÔbnÑEoÔEoÐ*@ð à*@Ð*@Ð*@r   r%   r   rå   c              3   ó\   •K  — | ]&}t          ‰d          |d         d          ¦  «        V — Œ'dS ©r   rå   Nrª   ©rG   rc   rå   s     €r   rH   z._span_body_rows_split_under.<locals>.<genexpr>Ç  s9   øè è € ÐKÐK¸d�C  Q¤¨¨f¬°a¬Ñ9Ô9ÐKÐKÐKÐKÐKÐKr   c              3   ó\   •K  — | ]&}t          ‰d          |d         d          ¦  «        V — Œ'dS ©r%   rå   N©r3   rù   s     €r   rH   z._span_body_rows_split_under.<locals>.<genexpr>È  s9   øè è € ÐIÐI¸D�#˜d 1œg t¨F¤|°A¤Ñ7Ô7ÐIÐIÐIÐIÐIÐIr   TF)r   r(   r3   r)   )rå   rè   ré   Ú	body_rowsr6   Úcovered_startÚcovered_endrç   s   ` `    @r   Ú_span_body_rows_split_underr   º  s  øøø€ åÐmÐmÐmÐm°ÐmÑmÔmÑnÔn€IØð ð ˆð
ð 
ð 
ð 
ð 
àð
ñ 
ô 
ˆð
 ð 	ØÝˆt‰9Œ9˜Š>ˆ>˜d 1œg fœo°Ò5Ð5ØÝÐKÐKÐKÐKÀdÐKÑKÔKÑKÔKˆÝÐIÐIÐIÐIÀDÐIÑIÔIÑIÔIˆØ˜D œGÒ#Ð#¨°t¸A´wÒ(>Ð(>Ø�4�4øØˆ5r   c                 ó  ‡ — ˆ fd„|D ¦   «         }|sdS t          |¦  «        dk    r|d         d         ‰ k    rdS t          ˆ fd„|D ¦   «         ¦  «        }t          ˆ fd„|D ¦   «         ¦  «        }|‰ d         k    o|‰ d         k    S )zGTrue if leaf-header cells split the column range ``cols`` into >1 cell.c                 óX   •— g | ]&}|d          dk    ¯t          ‰|d         ¦  «        ¯$|‘Œ'S )rà   râ   rå   ræ   rù   s     €r   ru   z1_span_header_leafs_split_over.<locals>.<listcomp>Ð  s?   ø€ ÐrÐrÐr�T T¨&¤\°]Ò%BÐ%BÕG]Ð^bÐdhÐioÔdpÑGqÔGqÐ%BˆDÐ%BÐ%BÐ%Br   Fr%   r   rå   c              3   ó\   •K  — | ]&}t          ‰d          |d         d          ¦  «        V — Œ'dS rø   rª   rù   s     €r   rH   z0_span_header_leafs_split_over.<locals>.<genexpr>Õ  s9   øè è € ÐGÐG¸$�˜D œG T¨&¤\°!¤_Ñ5Ô5ÐGÐGÐGÐGÐGÐGr   c              3   ó\   •K  — | ]&}t          ‰d          |d         d          ¦  «        V — Œ'dS rû   rü   rù   s     €r   rH   z0_span_header_leafs_split_over.<locals>.<genexpr>Ö  s9   øè è € ÐEÐE¸•c˜$˜qœ' 4¨¤<°¤?Ñ3Ô3ÐEÐEÐEÐEÐEÐEr   )r(   r3   r)   )rå   rè   r6   rþ   rÿ   s   `    r   Ú_span_header_leafs_split_overr  Î  s°   ø€ àrÐrÐrÐr˜TÐrÑrÔr€DØð ØˆuÝ
ˆ4�y„y�A‚~€~˜$˜qœ' &œ/¨TÒ1Ð1ØˆuÝÐGÐGÐGÐGÀ$ÐGÑGÔGÑGÔG€MÝÐEÐEÐEÐEÀÐEÑEÔEÑEÔE€KØ˜D œGÒ#Ð>¨°t¸A´wÒ(>Ð>r   c                 ó¶   — |d         |d         z
  dk    rdS | |k     r)t          | |||¦  «        sdS t          |||¦  «        rdS dS t          ||¦  «        rdS dS )zJWhether to reject a candidate merge over ``cols`` as a (reason,) mismatch.r%   r   )Fro   )TÚ.header_leaf_colspan_changed_against_body_split)TÚ.body_colspan_changed_against_header_leaf_split)rë   r   r  ©rç   rå   rè   ré   s       r   Ú#_span_reject_colspan_mismatch_merger
  Ú  sƒ   € àˆA„w��a”Ñ˜AÒÐØˆyØ�ÒÐÝ)¨'°4¸¸zÑJÔJð 	Ø�9Ý& t¨T°:Ñ>Ô>ð 	JØIÐIØˆyÝ$ T¨4Ñ0Ô0ð FØEÐEØˆ9r   c                 ó–   — g }|||dz   …         D ]8}t          ||| ¬¦  «        }|                     t          | ||¦  «        ¦  «         Œ9|S )Nr%   r¾   )rÂ   r   rÆ   )rŒ   rØ   r*   r+   rÀ   rk   rÒ   r­   s           r   Ú_span_cell_texts_for_entriesr  é  s`   € Ø€EØ˜  q¡˜Ô)ð Dð DˆÝ*¨:°uÀ4ÐHÑHÔHˆØ�ŠÕ.¨t°U¸EÑBÔBÑCÔCÐCÐCØ€Lr   c                 ól   — | dk    rdS d„ t          |||||¦  «        D ¦   «         }t          |¦  «         S )zçAllow an otherwise-rejected header merge when a covered part is empty.

    A header leaf that would be rejected against a body split is still merged if
    one of the merged parts has no text (an empty header slot the body fills).r  Fc                 ó6   — g | ]}|                      ¦   «         ‘ŒS r   )rr   )rG   r   s     r   ru   zD_span_allow_header_colspan_merge_with_empty_part.<locals>.<listcomp>ø  s    € ÐoÐoÐo 4�$—*’*‘,”,ÐoÐoÐor   )r  Úall)ÚreasonrŒ   rØ   r*   r+   rÀ   Ú
part_textss          r   Ú0_span_allow_header_colspan_merge_with_empty_partr  ñ  sK   € ð
 ÐAÒAÐAØˆuØoÐoÕ+GÈÈgÐW\Ð^aÐcmÑ+nÔ+nÐoÑoÔo€JÝ�:‰ŒÐÐr   F)Úheader_row_countÚstrict_colspanc                óX  — t          |¦  «        }g }g }|D ]`}|D ][}|€Œt          j        |¦  «        }	|                     |	j        |	j        g¦  «         |                     |	j        |	j        g¦  «         Œ\Œat          |¦  «        }
t          |¦  «        }|r-t          dt          t          |pd¦  «        |¦  «        ¦  «        nd}|rt          ||
|¦  «        ng }t          | ¦  «        }t          | ¦  «        }t          ¦   «         }g }t!          |¦  «        D �]�\  }}g }d„ |D ¦   «         }t#          ||¦  «        }d„ |D ¦   «         }d}|t          |¦  «        k     �r5|                     |¦  «        }|�||\  }}t'          |||dz   …         ¦  «        }	|rUt)          |	|
¦  «        }t+          ||||¬¦  «        \  }}|rt-          || ||||¬¦  «        rd}|r||         }	|dz  }n|dz   }n|dz   }n||         }	|dz  }|	j        rŒ¼t1          |	j        |	j        |
¦  «        }t1          |	j        |	j        |¦  «        }|                     t5          t7          |	¦  «        t9          | |	||¦  «        ||¬	¦  «        ¦  «         |t          |¦  «        k     �°5|                     |¦  «         �Œ’|S )
a¡  Resolve a detected table's merged-cell (colspan/rowspan) structure.

    ``cells`` is a row-major grid: a list of rows, each a list of ``[x0, y0, x1,
    y1]`` cell rectangles (``None`` for a gap) -- the same grid shape
    :func:`refine_grid` accepts. Returns a row-major grid of :class:`SpanCell`
    placements, ragged where cells span: each placement carries its union
    ``bbox``, the page ``text`` it claims, and its ``colspan``/``rowspan`` (how
    many clustered column/row slots it covers). Every page word is claimed by at
    most one placement.

    ``header_row_count`` is the number of leading header rows (default a
    conservative 1); it only matters together with ``strict_colspan``.
    ``strict_colspan`` (default False), when set, refuses a merge whose colspan
    would contradict the header/body column split. This is a PyMuPDF extension;
    it reads page text/graphics but does not mutate the page.
    Nr%   r   c                 ó:   — g | ]}|®t          j        |¦  «        ‘ŒS r
   )rO   rP   rî   s     r   ru   z!resolve_spans.<locals>.<listcomp>$  s'   € ÐJÐJÐJ¨$¸Ð9I•7”< Ñ%Ô%Ð9IÐ9IÐ9Ir   c                 ó    — i | ]\  }}}|||f“ŒS r   r   )rG   r*   r+   rk   s       r   ú
<dictcomp>z!resolve_spans.<locals>.<dictcomp>&  s%   € ÐUÐUÐUÑ6G°e¸SÀ%˜e c¨5 \ÐUÐUÐUr   r	  )r  rŒ   rØ   r*   r+   rÀ   F)r   r   r   r   )r(   rO   rP   Úextendr1   r2   r?   r@   r"   r)   r3   Úintrò   rÎ   r   Úsetr×   rÞ   rq   rR   r7   r
  r  r   r/   r   r   ÚtuplerË   ) rŒ   rñ   r  r  ÚrowsÚx_edgesÚy_edgesrä   rï   r4   r5   Úy_boundariesré   Ú
base_cellsrÙ   rÀ   rÉ   Ú
placementsrç   Úplacement_rowrØ   rg   Úintervals_by_startrÖ   ÚintervalÚend_positionÚ_crossing_textsÚcandidate_colsÚrejectr  r   r   s                                    r   Úresolve_spansr*  ü  s;  € õ" ˆu‰:Œ:€Dà€GØ€GØð /ð /ˆØð 	/ð 	/ˆDØˆ|ØÝ”< Ñ%Ô%ˆDØ�NŠN˜DœG T¤WÐ-Ñ.Ô.Ð.Ø�NŠN˜DœG T¤WÐ-Ñ.Ô.Ð.Ð.ð	/õ .¨gÑ6Ô6€LÝ-¨gÑ6Ô6€LØBFÐM•�Q��CÐ 0Ð 5°AÑ6Ô6¸Ñ=Ô=Ñ>Ô>Ð>ÈA€JØLZÐbÕ'¨¨|¸ZÑHÔHÐHÐ`b€Jå! $Ñ'Ô'€JÝ# DÑ)Ô)€JÝ‘E”E€MØ€JÝ! %Ñ(Ô(ð 3)ñ 3)‰ˆ�ØˆØJÐJ°#ÐJÑJÔJˆÝ,¨W°jÑAÔAˆ	ØUÐUÈ9ÐUÑUÔUÐØˆØ�˜W™œÒ%Ñ%Ø)×-Ò-¨hÑ7Ô7ˆHØÐ#Ø08Ñ-�˜oÝ'¨°¸<È!Ñ;KÐ0KÔ(LÑMÔM�Ø!ð 0Ý%5°d¸LÑ%IÔ%I�NÝ%HØ 'Ø+Ø'Ø#-ð	&ñ &ô &‘N�F˜Fð ð 'Õ"RØ%Ø!Ø 'Ø&Ø(Ø#-ð#ñ #ô #ð 'ð "'˜Øð 4Ø& xÔ0˜Ø  A™˜˜à#/°!Ñ#3˜˜à+¨aÑ/�H�Hà˜xÔ(�Ø˜A‘�àŒ}ð ØÝ.¨t¬w¸¼ÀÑNÔNˆGÝ.¨t¬w¸¼ÀÑNÔNˆGØ× Ò ÝÝ˜t™œÝ1°$¸¸jÈ-ÑXÔXØ#Ø#ð	ñ ô ñô ð ðK �˜W™œÒ%Ñ%ðZ 	×Ò˜-Ñ(Ô(Ð(Ñ(àÐr   )$r   rO   Úpymupdf._table_refiner   r   r   r   r"   r/   r7   r=   rC   rR   rT   r[   r_   rl   r{   r€   r�   r§   r¹   r½   rÂ   rÆ   rË   rÎ   rÞ   rë   rò   r   r  r
  r  r  r*  r   r   r   ú<module>r,     s’  ððð ðB €€€ðð ð ð ð ð ð ð ð ð ðð ð ð ð ñ ô ð ð, 58ð 
ð 
ð 
ð 
ð 
ð CFð ð ð ð ð ð 7:ð 	&ð 	&ð 	&ð 	&ð 	&ð'ð 'ð 'ð[ð [ð [ðð ð ð-ð -ð -ð
(ð 
(ð 
(ðð ð ð ð ð ðð ð ð#ð #ð #ð$ð ð ð: ð  ð  ðF!ð !ð !ðB8ð 8ð 8ð :>ð ð ð ð ð ð&að að að	;ð 	;ð 	;ðð ð ð:,ð ,ð ,ðB
ð 
ð 
ðð ð ð@ð ð ð(	?ð 	?ð 	?ðð ð ðð ð ðð ð ð 48Èð [ð [ð [ð [ð [ð [ð [r   