
    Ipj0|                        S r SSKrSSKrSSKrSrS rS rS rS rS r	S	 r
S
 rSS.S jrS rS rSS.S jrS rS rS rS rS r SCSSSSS.S jjrS rS rS rS S!S"S#S$.S% jrS& rSS'.S( jrSSS).S* jrS S!S"S#SS+.S, jrS- rS. rS/ r S0 r!S1 r"S2 r#S3 r$S4S5S6.S7 jr%S4S8S5S9.S: jr&SS;.S< jr'S5S4S8S=.S> jr(SS5S?.S@ jr)SA r*SB r+g)Da  
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 grid refinement (opt-in extension).

Provides the refine_grid / refine_grid_structure / refine_grid_rows API that
refines a detected table's cell grid using page text and vector-graphics
geometry. Re-exported by pymupdf.table; never runs on the default
find_tables() path.
    Ng      @c                 <   / nU R                  S5      nUR                  S/ 5       H  nUR                  S5      S:w  a  M  UR                  S/ 5       H  nUR                  S5      nUR                  S5      nUR                  S/ 5       H  nS	R                  S
 UR                  S/ 5       5       5      nUR                  5       (       d  MB  UR	                  UR                  S/ 5       V	s/ s H  n	[        U	5      PM     sn	UUUS.5        M     M     M     U$ s  sn	f )zFlattened rawdict text spans carrying line-direction metadata.

Only used to locate vertical/rotated text so those words can be replaced by
span-level bboxes in _refine_page_words. Minimal fields (bbox/text/dir/wmode)
-- the only ones the consumers read.rawdictblockstyper   linesdirwmodespans c              3   X   #    U  H   n[        UR                  S S5      5      v   M"     g7f)cr   N)strget.0chars     Q/var/www/html/pdf-tiff/venv/lib/python3.13/site-packages/pymupdf/_table_refine.py	<genexpr>(_refine_rawdict_spans.<locals>.<genexpr>F   s%     XBW$s488C#455BWs   (*charsbbox)r   textr   r	   )get_textr   joinstripappendfloat)
pager
   rawblockline	directionr	   spanr   vs
             r   _refine_rawdict_spansr%   7   s     E
--	
"C2&99V!IIgr*DIHHW%E"-wwX$((7TVBWXXzz||3788FB3G H3Gaq3G H $(!&		 . + '$ L !Is   *Dc                 8   U R                  S5      S;  a  gU R                  S5      n[        U[        [        45      (       a  [	        U5      S:  a  g [        [        US   5      5      n[        [        US   5      5      nX2:  $ ! [        [        4 a     gf = f)	zTrue when a rawdict span's direction indicates non-horizontal text flow.

wmode != 0 is vertical; otherwise compare |dir_x| vs |dir_y| (a missing or
unparsable direction counts as horizontal).r	   Nr   Tr      Fr      )	r   
isinstancelisttuplelenabsr   	TypeError
ValueError)r#   r"   dxdys       r   _refine_is_vertical_or_rotatedr3   T   s    
 xx	)Ii$//3y>A3Ey|$%y|$% 7N z" s   .B BBc                   ^ [        U SS5      nUb  U$ U R                  S5       Vs/ s Hi  n[        US   5      R                  5       (       d  M&  [	        US   5      [	        US   5      [	        US   5      [	        US   5      [        US   5      4PMk     nn[        U 5       Vs/ s HO  n[        U5      (       d  M  [        UR                  S	5      =(       d    S
5      R                  5       (       d  MM  UPMQ     nnU(       Ga=  U Vs/ s H1  n[        R                  " UR                  S5      =(       d    / 5      PM3     nn/ nU HW  nUu  ppn[        R                  " X-   S-  X-   S-  5      m[        U4S jU 5       5      (       a  MF  UR                  U5        MY     [        XV5       H  u  pNUR                  (       a  M  UR                  [	        UR                  5      [	        UR                   5      [	        UR"                  5      [	        UR$                  5      [        US	   5      45        M     ['        US S9n [)        U SU5        U$ s  snf s  snf s  snf ! [*         a     U$ f = f)a%  Center-point word list for grid refinement, cached on the page object.

Extract page words once (page.get_text("words")), then substitute vertical/
rotated text: any horizontal word whose center falls inside a rotated span's
bbox is dropped, and each rotated span is appended as a single word._refine_words_cacheNwords   r   r)   r(      r   r   r         ?c              3   j   >#    U  H(  oR                   (       a  M  UR                  T5      v   M*     g 7fN)is_emptycontains)r   rectcenters     r   r   %_refine_page_words.<locals>.<genexpr>   s$     X]T--(4==((]s   33c                     U S   U S   4$ Nr)   r    items    r   <lambda>$_refine_page_words.<locals>.<lambda>   s    tAwQ.@    key)getattrr   r   r   r   r%   r3   r   pymupdfRectPointanyr   zipr<   x0y0x1y1sortedsetattr	Exception)r   cachewr6   r#   rotated_spansrotated_rectskeptwordwx0wy0wx1wy1_r>   r?   s                  @r   _refine_page_wordsrc   f   s#    D/6E w''Aqt9?? 	HqteAaDk51;adS1YG' 
  *$//D)$/ 	478H8NB4O4U4U4W 	/  
 JWX-$dhhv&6&<"=-XD$(!Cc]]CI#4syC6GHFX]XXXKK  m;JD}}KKtwwtwwtwwtwwQTUYZ`UaQbc < t!@A+U3 L=
 Y"  Ls6   %IAI&I>4I6I
8II" "
I0/I0c                 |    X-   S-  nX-   S-  n	XHs=:*  =(       a    U:*  Os  =(       a    XYs=:*  =(       a    U:*  $ s  $ )zWord-to-cell membership by CENTER point (inclusive), not clip-overlap.

A word straddling a column line is claimed by exactly one cell.r9   rC   )
r^   r_   r`   ra   rQ   rR   rS   rT   cxcys
             r   _refine_word_in_rectrg      s<     )s	B
)s	B>>r>,bnn"n,n,rH   c                 >   [        UR                  5      [        UR                  5      [        UR                  5      [        UR                  5      4u  p#pE[        U 5       VVVV	V
s/ s H!  u  pgpn
[        XgXX#XE5      (       d  M  XgXU
4PM#     sn
n	nnn$ s  sn
n	nnnf )zGPage words whose center lies in rect, as (x0, y0, x1, y1, text) tuples.)r   rQ   rR   rS   rT   rc   rg   )r   r>   rQ   rR   rS   rT   r^   r_   r`   ra   r   s              r   _refine_words_in_rectri      s~    477^U477^U477^U477^SNBB );4(@(@$Cc#BBC 	#3T"(@  s   'B	Bc                 ^    [        [        R                  " SU R                  5       5      5      $ )z8Loose value-like predicate: the text contains any digit.\d)boolresearchr   r   s    r   _refine_has_digitrp      s    		%.//rH   c                     U R                  5       n[        [        R                  " SU5      5      =(       a    [	        S U 5       5      (       + $ )z=Strict value-like predicate: contains a digit and no letters.rk   c              3   @   #    U  H  oR                  5       v   M     g 7fr;   )isalphar   s     r   r   -_refine_has_digit_no_alpha.<locals>.<genexpr>   s     7\S[4S[s   )r   rl   rm   rn   rO   )r   strippeds     r   _refine_has_digit_no_alpharv      s7    zz|H		%*+\C7\S[7\4\0\\rH   ffffff?)	thresholdc                    U c  g U S S u  p#n[        U5      U:  =(       a#    [        U5      U:  =(       a    [        U5      U:  $ ! [          a     gf = f)NTr8   )rW   r   )fillrx   rgbs        r   _refine_is_whiter~      s_    |r(a 8iQE!Hy$8QU1X	=QQ  s   A 
AAc                    Ub)  [         R                  " U5      nUR                  (       d  U$ U  VVs/ s HO  nU  HE  nUc  M  [         R                  " U5      R                  (       a  M/  [         R                  " U5      PMG     MQ     nnnU(       d  g [         R                  " US   5      nUSS   H  nUR                  U5        M     U$ s  snnf Nr   r)   )rL   rM   r<   include_rect)cells
table_bboxr>   rowcellrectsrE   s          r   _refine_table_rectr      s    ||J'}}K CD 	 %,LL$6$?$? 	T 	 
  <<a!Dab	$ Ks   C#C*Cc                   / n[        U R                  R                  5      nU R                  5        H  n[	        UR                  S5      5      (       a  M$  UR                  S/ 5       H  nUS   S:w  a  M  [        R                  " US   5      nUR                  5         UR                  SU-  :  a  ML  UR                  U:  d  UR                  U:  a  Mn  Xq-  R                  (       a  M  UR                  U5        M     M     U$ )Nrz   itemsr   rm   r)   rw   )r   r>   widthget_drawingsr~   r   rL   rM   	normalizeheightr<   r   )r   
table_rectmin_dimout
page_widthdrawingrE   r>   s           r   _refine_raw_shaded_rectsr      s    
Ctyy'J$$&GKK/00KK,DAw$<<Q(DNNzzTJ..zzG#t{{W'<!++JJt - ' JrH   g?)contain_fracc                   ^^^^ / n[        U 5       HY  u  mmTR                  5       mTS::  a  M  [        UUUU4S j[        U 5       5       5      nU(       a  MH  UR                  T5        M[     U$ )Nr   c              3      >#    U  HI  u  pUT:g  =(       a6    TU-  R                  5       TT-  :  =(       a    UR                  5       TS -  :  v   MK     g7f)g?N)get_area)r   other_indexotherarear   indexr>   s      r   r   0_refine_cell_background_rects.<locals>.<genexpr>   s`      
 ';" 5  /'')\D-@@/ 4$;./ ';s   AA)	enumerater   rO   r   )	raw_rectsr   keepnestedr   r   r>   s    `  @@@r   _refine_cell_background_rectsr      sh    D +t}}19 
 '0	&:	
 
 vKK , KrH   c                "   / n/ n[        U 5       HN  nU(       a3  XCS   -
  U:  a(  UR                  [        U5      [        U5      -  5        / nUR                  U5        MP     U(       a&  UR                  [        U5      [        U5      -  5        U$ )N)rU   r   sumr-   )values	toleranceclusterscurrentvalues        r   _refine_clusterr      sx    HGur{*Y6OOCL3w<78Gu	  
 Gs7|34OrH   c                 X   [        5       n[        5       nU R                  5        GHl  nUR                  S5      S;   nUR                  S/ 5       GH=  nUS   nUS:X  Ga  US   US   p[        UR                  U	R                  -
  5      S:  a  [        UR
                  U	R
                  -
  5      S	:  az  UR                  S
-
  UR                  U	R                  -   S-  s=:  a  UR                  S
-   :  a9  O  O6UR                  [        UR                  U	R                  -   S-  S5      5        O[        UR
                  U	R
                  -
  5      S:  a  [        UR                  U	R                  -
  5      S	:  ay  UR                  S
-
  UR
                  U	R
                  -   S-  s=:  a  UR                  S
-   :  a8  O  O5UR                  [        UR
                  U	R
                  -   S-  S5      5        GM  US:w  a  GM  [        R                  " US   5      n
U
R                  5         X-  R                  (       a  GM  U
R                   S
:  aH  U
R"                  S	:  a8  UR                  [        U
R                  U
R                  -   S-  S5      5        GMG  U
R"                  S
:  aH  U
R                   S	:  a8  UR                  [        U
R                  U
R                  -   S-  S5      5        GM  U(       d  GM  UR                  [        U
R                  S5      5        UR                  [        U
R                  S5      5        UR                  [        U
R                  S5      5        UR                  [        U
R                  S5      5        GM@     GMo     [%        U5      [%        U5      4$ )Nr   )sfsr   r   lr)   r(         ?g      $@       @r9   rm   )setr   r   r.   yxrR   rT   addroundrQ   rS   rL   rM   r   r<   r   r   rU   )r   r   xsysr   strokedrE   kindp1p2r>   s              r   _refine_border_linesr      s   	B	B$$&++f%4KK,D7Ds{a$q'Brtt$s*BDD244K(4/"+rttbdd{c.AWJMMTWDWWFF5"$$+!4a89rtt$s*BDD244K(4/"+rttbdd{c.AWJMMTWDWWFF5"$$+!4a89t|<<Q(DNN!++{{S TZZ$%6udgg/36:;c!dkkD&8udgg/36:;uTWWa()uTWWa()uTWWa()uTWWa()? - 'D ":vbz!!rH   c                r   ^^ U  V^s/ s H"  m[        UU4S jU 5       5      (       a  M   TPM$     sn$ s  snf )Nc              3   F   >#    U  H  n[        TU-
  5      T:*  v   M     g 7fr;   )r.   )r   borderedger   s     r   r   $_refine_drop_near.<locals>.<genexpr>(  s"     -d\cRXc$-.@I.M\cs   !rO   )edgesbordersr   r   s     ``r   _refine_drop_nearr   '  s)    "eUT#-d\c-d*dDUeees   44c                    U  Hc  u  pgpn
[        U
5      R                  5       (       d  M'  Xh-   S-  nXy-   S-  nX-   Us=:  a
  X5-
  :  d  MH  O  ML  X%-   Us=:  a  XE-
  :  d  M_     g  Me     g)Nr9   TF)r   r   )r6   rQ   rR   rS   rT   marginr^   r_   r`   ra   r   re   rf   s                r   _refine_has_textr   +  sq    $) #D4y  i3i3;)bk))bkB.L.L /M %* rH   c          
      r  ^^^ [        UUU4S jU 5       5      nU(       a  T/UQTPnS n	[        [        U5      S-
  5       H   n
[        XX   XXU
S-      TS9(       a  M  U
n	  O   U	c   U$ U	[        U5      :  a  UR	                  U	5        O!U	S-
  S:  a  UR	                  U	S-
  5        O U$ U(       a  M  U$ )Nc              3   Z   >#    U  H   nTT-   Us=:  a  TT-
  :  d  M  O  M  Uv   M"     g 7fr;   rC   )r   r   r   rR   rT   s     r   r   3_refine_content_filter_row_edges.<locals>.<genexpr>7  s*     SJD"v+2RrF{2R42R4Js   ++	+r)   r   r   )rU   ranger-   r   pop)r6   rR   rT   
candidatesrQ   rS   r   r   boundsempty_indexr   s    ``   `    r    _refine_content_filter_row_edgesr   6  s    SJSSE
!u!b!3v;?+E#Ev}bQRBS\bcc# ,  L U#IIk"1_!IIkAo&L % LrH   r   g      @g      @)cluster_tolerancer   r   border_tolerancec                   [        X5      nUb  U(       d  U$ [        XUS9n[        U5      n	U	(       d  U$ [        U	 V
Vs/ s H6  n
[	        U
R
                  5      [	        U
R                  5      4  H  oPM     M8     snn
US9n[        X5      u  p[        XUS9nU(       d  U$ [        U 5      n/ nSnU GH|  nU Vs/ s H  nUc  M  [        R                  " U5      PM      nnU(       d  UR                  U5        MI  [        S U 5       5      n[        S U 5       5      n[        S U 5       5      n[        S U 5       5      n[        UUUUUUUS	9nU(       d  UR                  U5        M  U/UQUPn[!        [#        U5      S
-
  5       H  n/ nU Hs  nUc  UR                  S5        M  [        R                  " U5      n
UR                  [	        U
R$                  5      UU   [	        U
R&                  5      UUS
-      /5        Mu     UR                  U5        M     U[#        U5      S-
  -  nGM     US::  a  U$ U$ s  snn
f s  snf )a/  Split rows the line grid merged but a cell background rectangle separates.

Cell background-shading rectangles give row boundaries the border lines do
not; candidate y-edges from those rectangles (minus edges that coincide with
real borders, minus edges that would cut an empty band) subdivide each row.N)r   )r   r   c              3   L   #    U  H  n[        UR                  5      v   M     g 7fr;   )r   rR   r   r   s     r   r   %_split_shaded_rows.<locals>.<genexpr>s       2TT%..T   "$c              3   L   #    U  H  n[        UR                  5      v   M     g 7fr;   )r   rT   r   s     r   r   r   t  r   r   c              3   L   #    U  H  n[        UR                  5      v   M     g 7fr;   )r   rQ   r   s     r   r   r   u  r   r   c              3   L   #    U  H  n[        UR                  5      v   M     g 7fr;   )r   rS   r   s     r   r   r   v  r   r   r   r)   r(   )r   r   r   r   r   rR   rT   r   r   rc   rL   rM   r   minmaxr   r   r-   rQ   rS   )r   r   r   r   r   r   r   r   r   background_rectsr>   r   y_edges
_border_xs	border_ysr6   	new_cells
added_rowsr   r   livery0ry1rx0rx1cutsr   
band_indexbands                                r   _split_shaded_rowsr   J  s1    $E6J(7KI4Y?*X*$dggdgg7Wt7W*X#G 1BJ>NOGt$EIJ/2Gstd"T"sGS!2T222T222T222T22/sC#s[abS!""c"Fa0JD<KK%"<<-DKKtww
);U477^VT^abTbMcd  T" 1 	c&kAo%
3 6 QQ 	Y Hs    =I 
=IIc                     [        U 5      $ r;   )rv   ro   s    r   _refine_value_liker     s    %d++rH   c                     [        U 5      $ r;   )rp   ro   s    r   _refine_span_value_liker     s    T""rH   c          	      T   [         R                  " U5      n/ n[        X5       H  u  pgpn
[        U
5      R	                  5       nU(       d  M)  [        U5      [        U5      -   S-  n[        U5      [        U	5      -   S-  nUR                  Us=::  a  UR                  ::  d  M  O  M  UR                  Us=::  a  UR                  ::  d  M  O  M  UR                  [        U5      [        U5      X45        M     [        U5      S:  a  gUR                  S S9  / nUS   /nUSS  H<  nUS   US	   S   -
  U:  a  UR                  U5        U/nM+  UR                  U5        M>     UR                  U5        U H  nUR                  S
 S9  [        [        U5      S-
  5       H~  nUUS-      S   UU   S   -
  U::  a  M  SR                  S USUS-     5       5      nSR                  S UUS-   S  5       5      n[        U5      (       d  Mk  [        U5      (       d  M}      g   M     g)zTrue if a cell holds two value-like groups separated by a wide gap on one
text line -- the signal that a single grid column packs several columns.r   r(   Fc                     U S   $ )Nr(   rC   rD   s    r   rF   *_refine_cell_span_signal.<locals>.<lambda>  s    QrH   rI   r   r)   Nr   c                     U S   $ r'   rC   rD   s    r   rF   r     s    47rH    c              3   *   #    U  H	  oS    v   M     g7fr8   NrC   r   r]   s     r   r   +_refine_cell_span_signal.<locals>.<genexpr>  s     B0AG0A   c              3   *   #    U  H	  oS    v   M     g7fr   rC   r   s     r   r   r     s     C1B!W1Br   T)rL   rM   ri   r   r   r   rQ   rS   rR   rT   r   r-   sortr   r   r   )r   r   gapline_tolerancer>   r6   rQ   rR   rS   rT   r   ru   re   rf   r   r   r]   r!   r   leftrights                        r   _refine_cell_span_signalr    s    <<DE 5d At9??$Bi%)#s*Bi%)#s*77b#DGG##2(@(@(@LL%)U2Y=> !B 5zA~	JJ'J(EQxjGab	7WR[^#n4LL!fGNN4   
LL		*	+3t9q=)EEAIq!DKN2c988B[uqy0ABBDHHCeaik1BCCE&t,,1H1O1O *  rH   g      (@g      @g333333?r(   r   r   support_ratiomin_supportc          	      &  ^ [        U5      n[        U[        XF-  5      5      m0 nU HD  n[        U5       H2  u  pU
c  M
  [	        X
X#S9(       d  M  UR                  U	S5      S-   Xy'   M4     MF     [        U4S jUR                  5        5       5      $ )zReturn the column indices that fire the under-segmentation gate: columns
where enough rows carry the two-value-group signal (a support threshold).)r   r   r   r)   c              3   <   >#    U  H  u  pUT:  d  M  Uv   M     g 7fr;   rC   )r   colcountsupport_thresholds      r   r   ._refine_detect_span_columns.<locals>.<genexpr>  s     W(8*#EEV<V##(8s   	)r-   r   intr   r  r   rU   r   )r   r   r   r   r  r  rowscol_hitsr   	col_indexr   r  s              @r   _refine_detect_span_columnsr    s    
 u:DK]-A)BCH(~OI|'[[&.ll9a&@1&D#	  .  W(8WWWrH   c           	         / nU GH4  n/ nU[        U5      :  Ga  XB   b  [        R                  " XB   5      n[        X5       H  u  pxpn[	        U5      R                  5       (       d  M'  [        U5      [        U	5      -   S-  n[        U5      [        U
5      -   S-  nUR                  Us=::  a  UR                  ::  d  M~  O  M  UR                  Us=::  a  UR                  ::  d  M  O  M  UR                  [        U5      [        U	5      [	        U5      45        M     UR                  [        U5      5        GM7     U$ )Nr   )r-   rL   rM   ri   r   r   r   rQ   rS   rR   rT   r   rU   )r   r   r	  r  r   r6   r>   rQ   rR   rS   rT   r   re   rf   s                 r   _refine_column_wordsr    s    DS>ch2<<)D(=d(I$4y((Bi%)+s2Bi%)+s277b+DGG++20H0H0HLL%)U2YD	!BC )J 	F5M"  KrH   bridge_tolerancec                  ^ [        U 5       VVVVVs/ s H  u  p#U  H	  u  pEodXR4PM     M     nnnnnn[        U5      S:  a  / $ [        S U 5       5      n[        S U 5       5      n	X-
  S:  a  / $ / n
S nUnX::  ar  U VVVs1 s H  u  pEo$Us=:  a  U:  d  M  O  M  UiM     nnnn[        U5      U:  a  Ub  U
R	                  US   US   45        S nOUc  X/OUS   U/nUS-  nX::  a  Mr  Ub  U
R	                  US   US   45        / nU
 GH  u  nnXS-   ::  d	  UU	S-
  :  a  M  UU-   S-  m[        U4S jU  5       5      nU  Vs/ s H!  n[        U4S	 jU 5       5      (       a  M  UPM#     nnS=nnS=nnS=nn/ n/ nU H  nU VVVs/ s H  u  pEnUT::  d  M  UPM     nnnnU VVVs/ s H  u  pEnUT:  d  M  UPM     nnnnU(       a8  US-  nUS-  nU[        S
R                  U5      5      -  nUR                  U5        U(       d  M  US-  nUS-  nU[        S
R                  U5      5      -  nUR                  U5        M     US:  d  US:  a  GMO  US:  aB  [        S
R                  U5      5      (       a  [        S
R                  U5      5      (       d  GM  O#U(       a	  UU-  S:  d  U(       a  UU-  S:  a  GM  UR	                  T5        GM     [        U5      $ s  snnnnnf s  snnnf s  snf s  snnnf s  snnnf )Nr(   c              3   ,   #    U  H
  u  n  o!v   M     g 7fr;   rC   )r   rQ   rb   s      r   r   !_refine_cut_xs.<locals>.<genexpr>  s     *	HB1R	s   c              3   *   #    U  H	  u  pov   M     g 7fr;   rC   )r   rb   rS   s      r   r   r    s     *	HA1R	r   r)   r   r   r   c              3   `   >#    U  H#  n[        U4S  jU 5       5      (       d  M  Sv   M%     g7f)c              3   R   >#    U  H  u  po1Ts=:  =(       a    U:  Os  v   M     g 7fr;   rC   r   rQ   rS   rb   cut_xs       r   r   +_refine_cut_xs.<locals>.<genexpr>.<genexpr>  $     =e_dR[RTZ[5oo2oo_d   $'r)   Nr   )r   r6   r  s     r   r   r    s!     fJ5#=e_d=e:eQQJs   .	.c              3   R   >#    U  H  u  po1Ts=:  =(       a    U:  Os  v   M     g 7fr;   rC   r  s       r   r   r    r  r   r   r9   )r   r-   r   r   r   r   rO   r   r   extendrU   )
rows_wordsr  	row_indexr6   rQ   rS   rb   	intervalslohichannelsr   r   crossing_rowsr   	left_edge
right_edgecrossing_count	body_rows	left_rows
right_rowsleft_okright_okleft_nright_nleft_valuesright_valuesr   r  r  r  s                                 @r   _refine_cut_xsr6    s$   =Fz=Rl=R)9fkWbXZ`ab$fk$=RIl
9~		*	*	*B	*	*	*B	w{	HG
A
'<EUI'8ryaRTIU} 00"WQZ 89 'qfgaj!_G	S ' WQZ01D!)	:Q*Q"6Z'3.fJff )3f
u#=e_d=e:eU
	f!""	J(E-2BU\RTbEkDUDB.3CelbdrU{TeECQ	!-chhtn==""4(ua
1.sxx??##E*  q=JNA%chh{&;<<DVWZW_W_`lWmDnDn Eo6)C/WGASVYAYEE "*H $<{ m V& g CCs;   K,K5K5 K5K<.K<L"L2LLr  allowed_colsc          
         [        S U 5       SS9nUc  S O
[        U5      n0 n[        U5       H.  nUb  Xu;  a  M  [        [	        XU5      US9nU(       d  M*  XU'   M0     U(       d  U$ / n	U GH   n
/ n[        U
5       H  u  p|UR                  U5      nU(       d  UR                  U5        M0  Uc#  UR                  S /[        U5      S-   -  5        MV  [        R                  " U5      u  pnnU/U Vs/ s H  nUUs=:  a  U:  d  M  O  M  UPM     sn-   U/-   n[        [        U5      S-
  5       H!  nUR                  UU   UUUS-      U/5        M#     M     U	R                  U5        GM     U	$ s  snf )Nc              3   8   #    U  H  n[        U5      v   M     g 7fr;   )r-   )r   r   s     r   r   (_refine_split_columns.<locals>.<genexpr>"  s     +UcSUs   r   )defaultr  r)   )r   r   r   r6  r  r   r   r   r"  r-   rL   rM   )r   r   r  r8  ncolsactive_colscol_cutsr	  r   r   r   new_rowr   rQ   rR   rS   rT   r  r   r   s                       r   _refine_split_columnsrA  !  sl   +U+Q7E&.$C4EKHU|"s'=24DWgh4 SM  I"3IC<<$Dt$vTQ78!(d!3BTHuURUUHHB4O"3r7Q;/ENNBuIr2eai="#EF 0 ( 	!   Is   5E5

E5
E5
)r   r   r  r  r  c          	      X    [        U UUUUUS9nU(       d  U$ [        XU[        U5      S9$ )zSplit under-segmented columns, but only those that fire the value-group
gate -- so ordinary single-value columns are never disturbed.r  r7  )r  rA  r   )r   r   r   r   r  r  r  
fired_colss           r   _split_undersegmented_columnsrD  B  sC     -%#J  &6S_ rH   c                 z    U  VVs/ s H&  o  H  o"c  M  [         R                  " U5      PM     M(     snn$ s  snnf r;   )rL   rM   )r   r   r   s      r   _refine_rects_from_cellsrF  ^  s/    */T%3CDGLLC%TTTs   77c                     U (       d  g [         R                  " U S   5      nU SS   H  nUR                  U5        M     U$ r   )rL   rM   r   )r   r>   rE   s      r   _refine_union_rectrH  b  s=    <<a!Dab	$ KrH   c                 B    [        U S / S9n[        S U 5       5      nU$ )Nc                 <    [        S U  5       5      [        U 5      4$ )Nc              3   .   #    U  H  oc  M  Sv   M     g 7fNr)   rC   r   s     r   r   ?_refine_best_column_bounds.<locals>.<lambda>.<locals>.<genexpr>n  s     C34QQ3s   	)r   r-   )r   s    r   rF   ,_refine_best_column_bounds.<locals>.<lambda>n  s    C3CCSXNrH   )rJ   r<  c              3      #    U  Hf  nUc  M  [         R                  " U5      nUR                  UR                  :  d  M:  [	        UR                  5      [	        UR                  5      4v   Mh     g 7fr;   )rL   rM   rS   rQ   r   )r   r   r>   s      r   r   -_refine_best_column_bounds.<locals>.<genexpr>q  sV      D 	) \\$'D77TWW	 	)twwtww(s   A0.A01A0)r   rU   )r   best_rowr   s      r   _refine_best_column_boundsrR  k  s5    NH
   F MrH   c                    U (       d  / $ [        U S S9n U S   //nU S   S   U S   S   -   S-  nU SS  HF  nUS   US   -   S-  nXB-
  [        :  a  UR                  U/5        OUS   R                  U5        UnMH     / nU Hy  nUR                  [        S	 U 5       5      [        S
 U 5       5      [	        S U 5       5      [	        S U 5       5      SR                  S [        US S9 5       5      S.5        M{     U$ )zuGroup center-point words into body "lines" by a fixed center-y gap,
returning each line's union bbox and joined text.c                 (    U S   U S   -   S-  U S   4$ )Nr)   r8   r   r   rC   rD   s    r   rF   ,_refine_cluster_words_by_y.<locals>.<lambda>  s    T!WtAw->#,EtAw+OrH   rI   r   r)   r8   r   Nr   c              3   *   #    U  H	  oS    v   M     g7f)r   NrC   r   s     r   r   -_refine_cluster_words_by_y.<locals>.<genexpr>       6gdq'gr   c              3   *   #    U  H	  oS    v   M     g7f)r)   NrC   r   s     r   r   rW    rX  r   c              3   *   #    U  H	  oS    v   M     g7f)r(   NrC   r   s     r   r   rW    rX  r   c              3   *   #    U  H	  oS    v   M     g7fr   rC   r   s     r   r   rW    rX  r   r   c              3   *   #    U  H	  oS    v   M     g7f)r7   NrC   r   s     r   r   rW    s      _5^Ta5^r   c                     U S   $ r'   rC   rD   s    r   rF   rU    s    VZ[\V]rH   )rQ   rR   rS   rT   r   )rU   _REFINE_LINE_GAPr   r   r   r   )r6   r   last_centerr]   r?   r   clusters          r   _refine_cluster_words_by_yra  {  s    	5OPEq
|H8A;q!,3Kab	q'DG#s*"22OOTF#RL%  E6g666g666g666g66 _VGI]5^ __	
  LrH   c                    [        5       n[        U S   5      n[        U S   5      nX4-   S-  n[        U5       HH  u  nu  px[        XH5      [	        X75      -
  n	U	S:  d  Xus=::  a  U::  d  M3  O  M7  UR                  U5        MJ     U$ )NrQ   rS   r   r   )r   r   r   r   r   r   )
r!   
col_boundscolsline_x0line_x1r?   r   rQ   rS   overlaps
             r   _refine_line_columnsrh    s    5DDJGDJG3&F$Z0xg"S%55Q;",",,HHUO 1 KrH   c           	         / n[        UR                  5      [        UR                  5      [        UR                  5      [        UR                  5      4u  p4pV[        U 5       H  u  pxpnXy-   S-  nX-   S-  nX<s=::  a  U::  d  M#  O  M'  XMs=::  a  U::  d  M5  O  M9  [        U5      R                  5       nU(       d  M[  UR                  [        U5      [        U5      [        U	5      [        U
5      U45        M     U$ )Nr9   )	r   rQ   rR   rS   rT   rc   r   r   r   )r   r>   r6   r   r   r   r   rQ   rR   rS   rT   r   re   rf   s                 r   _refine_page_words_in_rectrj    s    EtwwtwwtwwtwwWCc 24 8g_g_ 0S 0 0t9??$DteBirE"IuRy$OP !9 LrH   c                   U (       d  / $ [        U 5      n[        US   S   5      [        US   S   5      //nUSS   H  u  pEUS   nUS   [        U5      -
  n[        US   US   -
  [        U5      [        U5      -
  5      nUS:  a@  Xx-  U:  a8  [        US   [        U5      5      US'   [        US   [        U5      5      US'   M  UR	                  [        U5      [        U5      /5        M     U VVs/ s H  u  pEXE4PM
     snn$ s  snnf )Nr   r)   r   )rU   r   r   r   r   )	r   fracr   mergedrR   rT   r   rg  
min_heights	            r   _refine_merge_overlapping_linesro    s   	5MEU1Xa[!5q!#567F)*!*uRy(gaj0%)eBi2GH
>g2d:WQZr3GAJWQZr3GAJMM59eBi01  $**6RH6***s   6D	g333333?r)   clean_thresholdheader_row_countc          
         [        U5      nU(       a(  [        S[        [        U5      [	        U5      5      5      OSn[        S[	        U5      U-
  5      n[	        U5      U::  a  g[	        U5      S:  a  g[        [        XS 5      5      nUb  UR                  (       a  g[        X5      n[        U5      nU(       d  g[        S[	        U5      S-   S-  5      n	/ n
U H9  n[        [        X5      5      n[	        U5      U	:  d  M(  U
R                  U5        M;     [	        U
5      [	        U5      -  nX:  a  g[	        U
5      U::  a  gUR                  UR                  UR                  UR                   /U VVs/ s H  u  p[#        US5      [#        US5      /PM     snnU
 Vs/ s H  n[%        US   5      [%        US   5      /PM!     snUUS.$ s  snnf s  snf )ai  Decide whether the body rows are over-merged. Returns the geometry needed
to re-cut them (body bbox, column bounds, per-record y-bounds) or None.

Fires only when the body text clusters into clean multi-column "record" lines
(clean_ratio >= clean_threshold) and there are more records than existing body
rows -- i.e. several records collapsed into one grid row.r)   r   Nr(   rR   rT   )	body_bboxrc  record_line_boundsexisting_body_rowsrr  )rR  r   r   r  r-   rH  rF  r<   rj  ra  rU   rh  r   rQ   rR   rS   rT   r   r   )r   r   rq  rr  rc  rv  	body_rectr6   r   min_colsrecord_linesr!   rd  clean_ratiorQ   rS   s                   r   _refine_detect_row_overmerger{    s    ,E2JINs1c#&6"7UDETUQE
-= =>
5z%%
:"#;EBS<T#UVII..&t7E&u-E1s:*q01HL*4<=t9 % 
 l#c%j0K$
<..  llILL),,	MBLM*b!eBl3*MR^_R^$d4j 15d3DER^_0, M_s   9$G$&Ggffffff?rq  merge_overlap_fracrr  c          
      ,   [        XX$S9nUc  U$ [        R                  " US   5      nUS    VVs/ s H  u  px[        U5      [        U5      4PM     n	nnUS    V
Vs/ s H  u  p[        U
5      [        U5      4PM     nn
n[	        XS9n[        U5      [        US   5      ::  a  U$ U V
Vs/ s H  u  pX-   S-  PM     nn
n[        UR                  5      /n[        XS	S 5       H  u  nnUR                  UU-   S-  5        M     UR                  [        UR                  5      5        US[        US
   5       n/ n[        [        U5      5       H  nUU   n
UUS	-      n/ nU	 H}  u  px[        R                  " XzX5      nUR                  [        UR                  5      [        UR                  5      [        UR                  5      [        UR                  5      /5        M     UR                  U5        M     / UQUQ$ s  snnf s  snn
f s  snn
f )a,  Re-cut over-merged body rows into one grid row per detected record.

Keeps the header rows intact, then rebuilds the body as evenly-bounded rows
(cut at the midpoints between consecutive record centers) across the detected
column bounds. header_row_count is the number of leading header rows to keep.rp  Nrt  rc  ru  )rl  rv  r   r)   rr  )r{  rL   rM   r   ro  r-   r  rR   rP   r   rT   r   rQ   rS   )r   r   rq  r}  rr  metart  rQ   rS   rc  rR   rT   raw_line_boundsline_boundscentersr   prevnxtheader_rowsnew_bodyr$  r@  r>   s                          r   _split_overmerged_rowsr    s    (_D |T+./I7;L7IJ7IVR59eBi(7IJJ<@AU<VW<V&"b	59-<VOW1/[K
;3t$89::-89[623[G99<< !E!"+.	cdSjC'( /	LLy||$%7#d#5678KH3{+,	99q=! FB<</DNNE$''NE$''NE$''NERVRYRYN[\ ! 	  - %[$8$$/ KW
 :s   "H"H
.Hr   c                4    [        XU5      n[        X5      nU$ )aF  Structural half of refine_grid: split shaded rows, then under-segmented
columns. Exposed separately so a caller that must know the header/body
boundary of the post-structure grid can insert that computation between the
two phases. table_bbox, when given, bounds the shaded-rectangle search;
otherwise the cells' union is used.)r   rD  )r   r   r   s      r   refine_grid_structurer    s     tJ7E)$6ELrH   )rr  rq  r}  c                    [        U UUUUS9$ )a  Row half of refine_grid: split body rows that collapsed several records
into one grid row. header_row_count is the number of leading header rows to
keep intact; callers that resolve a header region pass it in, and the default
of 1 is a conservative single-header assumption.r|  )r  )r   r   rr  rq  r}  s        r   refine_grid_rowsr  &  s     
 "'-) rH   )r   rr  c                .    [        XUS9n[        XUS9nU$ )ao  Refine a detected table's cell grid and return the refined grid.

``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 grid is refined in three
stages -- split rows that cell background shading separates, split columns
that jam several values into one cell, and split body rows that merged
several records -- using the page's words and vector graphics. The result is
a new grid in the same format (rows may be added and rows may widen).

``table_bbox`` optionally bounds the table (else the cells' union is used).
``header_row_count`` is the number of leading header rows to preserve when
re-cutting body rows (default 1). This is the all-in-one convenience wrapper;
a caller needing the header boundary of the intermediate grid can instead
call :func:`refine_grid_structure` then :func:`refine_grid_rows`.
r  )rr  )r  r  )r   r   r   rr  s       r   refine_gridr  4  s"      "$*EET;KLELrH   c           	         U (       d  / $ [        U  Vs1 s H  oS   iM	     sn5      n[        U5       VVs0 s H  u  p4XC_M	     nnn/ n[        U S S9n[        R                  " US S9 Hl  u  pS/[	        U5      -  n
U	 HA  n[        US   5      [        US   5      [        US   5      [        US   5      /XUS      '   MC     UR                  U
5        Mn     U$ s  snf s  snnf )	zConvert a Table's flat cell list into the row-major grid refine_grid wants.

Mirrors Table.rows: columns are the sorted distinct left edges, rows are the
cells grouped by top edge, each padded with None where a column has no cell.r   c                     U S   U S   4$ rB   rC   r   s    r   rF   '_refine_cells_to_grid.<locals>.<lambda>S  s    1Q41,rH   rI   c                     U S   $ rL  rC   r  s    r   rF   r  T  s    !A$rH   Nr)   r(   r8   )rU   r   	itertoolsgroupbyr-   r   r   )r   r   r   ir   r  gridordered_y	row_cellsr   r   s               r   _refine_cells_to_gridr  I  s    
 		u%u!1u%	&B"+B-0-$!-I0DU 67G"**7Gfs2wD',T!W~uT!W~uTRSW~W\]abc]dWe&fC$q'"# C	 H
 K &0s
   CC!c                     U  VVs/ s HH  nU  H>  nUc  M  [        US   5      [        US   5      [        US   5      [        US   5      4PM@     MJ     snn$ s  snnf )zIFlatten a refined grid back to a Table's flat (x0, y0, x1, y1) cell list.r   r)   r(   r8   )r   )r  r   r   s      r   _refine_grid_to_cellsr  \  sf     CD 	ItAwtAwtAwtAwH 	I  s
   A>Ar;   ),__doc__r  rm   rL   r^  r%   r3   rc   rg   ri   rp   rv   r~   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r6  rA  rD  rF  rH  rR  ra  rh  rj  ro  r{  r  r  r  r  r  r  rC   rH   r   <module>r     sf  B  	   :$'T-0
] )- R&* >A "
%"Pf. A
 AJ,#$P SQRX"" 45 >B <=4 J 	8U <	
+" BFXY +^ %)TTU&%T 6:  78bf  ,0! *&rH   