
    ,h=             !          d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
 ddlmZmZmZmZmZmZmZmZ g ag ada eej0                        Z G d d	e      Zg d
Zg dZ ed      ZdZdZ dZ!dZ"dZ#dZ$dZ%dZ& e
dddd      Z'ddddddddZ(de)fdZ* G d d      Z+ G d d      Z, G d  d!      Z-d"e)de)fd#Z. ej^                  e,j`                        jb                  je                         Z3 ej^                  e-      jb                  je                         Z4d"e)de+fd$Z5d"e)de6fd%Z7e#fd&e)de6fd'Z8did"e)de)fd(Z9d) Z:de)fd*Z;de)fd+Z<de)fd,Z=	 	 	 djde)fd-Z>dkde)fd.Z?de@fd/ZAde)fd0ZBd1e6fd2ZCd3e6de)fd4ZDeefd5ZEd6e6fd7ZFe fd8e6fd9ZGd: ZHde@fd;ZIde@fd<ZJd= ZKd> ZLe"fd?eMfd@ZNdA ZOe!fd?eMfdBZPdlde@fdCZQdD ZRdE ZSde)fdFZT G dG dH      ZU G dI dJeU      ZV G dK dL      ZW G dM dN      ZXe G dO dP             ZY G dQ dR      ZZ	 dmdSZ[dndTZ\dU Z]dV Z^ddWdWddedde ddde!e"ddddddddfdXe6dYe6dZe)d[e)d\ed]ed^ed_ed`edaedbedceddedeedfedgef dhZ_y)oa  
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.

---------------------------------------------------------------------
Portions of this code have been ported from pdfplumber, see
https://pypi.org/project/pdfplumber/.

The ported code is under the following MIT license:

---------------------------------------------------------------------
The MIT License (MIT)

Copyright (c) 2015, Jeremy Singer-Vine

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------------------
Also see here: https://github.com/jsvine/pdfplumber/blob/stable/LICENSE.txt
---------------------------------------------------------------------

The porting mainly pertains to files "table.py" and relevant parts of
"utils/text.py" within pdfplumber's repository on Github.
With respect to "text.py", we have removed functions or features that are not
used by table processing. Examples are:

* the text search function
* simple text extraction
* text extraction by lines

Original pdfplumber code does neither detect, nor identify table headers.
This PyMuPDF port adds respective code to the 'Table' class as method '_get_header'.
This is implemented as new class TableHeader with the properties:
* bbox: A tuple for the header's bbox
* cells: A tuple for each bbox of a column header
* names: A list of strings with column header text
* external: A bool indicating whether the header is outside the table cells.

    N)Sequence)	dataclass)
itemgetter   )RectMatrixTEXTFLAGS_TEXTTOOLS
EMPTY_RECTsRGB_to_pdfPointmessagec                       e Zd Zy)
UnsetFloatN__name__
__module____qualname__     O/var/www/html/Resume-Scraper/venv/lib/python3.12/site-packages/pymupdf/table.pyr   r   j       r   r   )snap_tolerancesnap_x_tolerancesnap_y_tolerancejoin_tolerancejoin_x_tolerancejoin_y_toleranceedge_min_lengthmin_words_verticalmin_words_horizontalintersection_toleranceintersection_x_toleranceintersection_y_tolerance)lineslines_stricttextexplicit   g      @   x0topx1bottomffffifflfiflst)u   ﬀu   ﬃu   ﬄu   ﬁu   ﬂu   ﬆu   ﬅreturnc                     t        | t              r| S t        | t              rt        |       S t        | d      r| j	                  d      }|S t        |       S )Nto_dictrecords)
isinstancelistr   hasattrr7   )
collectionress     r   to_listr>      sQ    *d#	J	)J	Y	'  +
Jr   c            	       :    e Zd ZdZd	d
dZ	 	 	 ddedededefdZy)TextMapz
    A TextMap maps each unicode character in the text to an individual `char`
    object (or, in the case of layout-implied whitespace, `None`).
    Nr5   c                 d    || _         dj                  t        t        d      |            | _        y )N r   )tuplesjoinmapr   	as_stringselfrC   s     r   __init__zTextMap.__init__   s$    Z]F!;<r   
main_groupreturn_groupsreturn_charsc                     | j                   |j                  |      |j                  |       }|D cg c]
  \  }}|	| }}}t        |      \  }	}
}}|j	                  |      |	|
||d}|r|j                         |d<   |r||d<   |S c c}}w )N)r'   r+   r,   r-   r.   groupschars)rC   startendobjects_to_bboxgrouprN   )rH   mrJ   rK   rL   subsetr'   crO   r+   r,   r-   r.   results                 r   match_to_dictzTextMap.match_to_dict   s     QWWZ01553DE$*<yaam<<-e4CV GGJ'
  xxzF8#F7O# =s
   
B
B
Nr5   N)r   TT)	r   r   r   __doc__rI   intbooldictrX   r   r   r   r@   r@      sD    
= "!  	
  
r   r@   c                   X    e Zd ZdZddZdddddeeddedddfded	e	d
e	dededede
fdZy)WordMapz&
    A WordMap maps words->chars.
    r5   Nc                     || _         y rY   )rC   rG   s     r   rI   zWordMap.__init__   s	    r   Fr   Tlayoutlayout_width_charslayout_height_charsuse_text_flow	presortedexpand_ligaturesc                    g }t        | j                        st        |      S |rt        ni }|rS|r|r"t	        d      t        t        ||z              }|r|r"t	        d      t        t        ||z              }dg|z  }ng }d}|s|r| j                  nt        | j                  d       }|d   d   }|d   |d   z
  }t        t        |d	 |
            D ]0  \  }}|r|d   d   d   ||	z   z
  |z  nd}t        t        |dkD        t        |      |z
        }t        |      D ].  }t        |      r|d
   d   dk(  r||z  }|j                  d       0 ||z  }d}|s|r|nt        |d       }|D ]  \  }}|r|d   |z
  |z  nd}t        t        d|      t        |      |z
        }|dg|z  z  }||z  }|D ]9  }|j                  |d   |d         } | D ]  }!|j                  |!|f       |dz  } ;  |s%|dg||z
  z  z  }3 |r@||dz   z
  }"t        |"      D ]  }|dkD  r||z  }|j                  d        |d
   dk(  r|dd
 }t        |      S )a  
        Given a list of (word, chars) tuples (i.e., a WordMap), return a list of
        (char-text, char) tuples (i.e., a TextMap) that can be used to mimic the
        structural layout of the text on the page(s), using the following approach:

        - Sort the words by (doctop, x0) if not already sorted.

        - Calculate the initial doctop for the starting page.

        - Cluster the words by doctop (taking `y_tolerance` into account), and
          iterate through them.

        - For each cluster, calculate the distance between that doctop and the
          initial doctop, in points, minus `y_shift`. Divide that distance by
          `y_density` to calculate the minimum number of newlines that should come
          before this cluster. Append that number of newlines *minus* the number of
          newlines already appended, with a minimum of one.

        - Then for each cluster, iterate through each word in it. Divide each
          word's x0, minus `x_shift`, by `x_density` to calculate the minimum
          number of characters that should come before this cluster.  Append that
          number of spaces *minus* the number of characters and spaces already
          appended, with a minimum of one. Then append the word's text.

        - At the termination of each line, add more spaces if necessary to
          mimic `layout_width`.

        - Finally, add newlines to the end if necessary to mimic to
          `layout_height`.

        Note: This approach currently works best for horizontal, left-to-right
        text, but will display all words regardless of orientation. There is room
        for improvement in better supporting right-to-left text, as well as
        vertical text.
        z;`layout_width` and `layout_width_chars` cannot both be set.z=`layout_height` and `layout_height_chars` cannot both be set.) Nr   c                 $    t        | d   d         S Nr   doctopfloatxs    r   <lambda>z$WordMap.to_textmap.<locals>.<lambda>$  s    51h3H r   keyrl   r,   c                 $    t        | d   d         S rk   rm   ro   s    r   rq   z$WordMap.to_textmap.<locals>.<lambda>,  s    uQqT(^/D r   
)rv   Nc                 $    t        | d   d         S )Nr   r+   rm   ro   s    r   rq   z$WordMap.to_textmap.<locals>.<lambda>G  s    eAaDJ.? r   r+   r   r'   N)lenrC   r@   	LIGATURES
ValueErrorr\   roundsorted	enumeratecluster_objectsmaxrangeappendminget)#rH   rb   layout_widthlayout_heightrc   rd   	x_density	y_densityx_shifty_shifty_tolerancere   rf   rg   _textmap
expansions
blank_linenum_newlineswords_sorted_doctop
first_worddoctop_startiwsy_distnum_newlines_prependline_lenline_words_sorted_x0wordrO   x_distnum_spaces_prependrV   lettersletternum_newlines_appends#                                      r   
to_textmapzWordMap.to_textmap   s   f 4;;8$$"2Y
!$U  &)|i/G)H%I"" $W  '*%	0I*J&K#%);;JJ M KK)HI 	 )+A.
!(+j.??#%Dk
 .	LEAr  Aq(#|g'=>)K 
 $'AE
f,	$  /0 .8}Q4(?
*H-.
 00LH  B$?@ !  4 
&e?E$t*w.);1%(Q)95=8;S%T"[M,>>>.. &A(nnQvY&	BG") & 4 A&&
& [M-?(-JKK].	Lb "59I"J./ .q5
*H-. ||+#CR=x  r   rZ   )r   r   r   r[   rI   DEFAULT_X_DENSITYDEFAULT_Y_DENSITYDEFAULT_Y_TOLERANCEr]   r\   r@   r   r   r   r   r`   r`      s    
 "##$##'#!%V!V!
  V! !V! V! V! V! 
V!r   r`   c            	       r    e Zd Zeedddddddf	defdZdefdZdefd	Z	d
 Z
d Zd ZdefdZdedefdZy)WordExtractorFTNkeep_blank_charsc
                     || _         || _        || _        || _        || _        || _        |g n|| _        |du rt        j                  n|xs d| _	        |	rt        | _        y i | _        y )NTrB   )x_tolerancer   r   re   horizontal_ltrvertical_ttbextra_attrsstringpunctuationsplit_at_punctuationry   r   )
rH   r   r   r   re   r   r   r   r   rg   s
             r   rI   zWordExtractor.__init__j  sy     '& 0*,(!,!42+
 $t+ &," 	! (8)Rr   ordered_charsc           
          t        |      \  }}}}|d   d   |d   d   z
  }|d   d   }|r j                  rn j                  rdnd}|d   d   }	d}
|s|	d   dk  rt        |      }d}
|	d   dk  r|	d	   dk  rd
}
n
|	d   dkD  rd}
dj	                   fd|D              |||||z   ||||
d	} j
                  D ]  }|d   |   ||<    |S )Nr   rl   r,   uprightr   ru   matrix  r)      Z   rB   c              3   b   K   | ]&  }j                   j                  |d    |d           ( ywr'   N)r   r   ).0rV   rH   s     r   	<genexpr>z,WordExtractor.merge_chars.<locals>.<genexpr>  s/      >?##AfIqy9s   ,/)	r'   r+   r-   r,   rl   r.   r   	directionrotation)rR   r   r   reversedrD   r   )rH   r   r+   r,   r-   r.   
doctop_adjr   r   r   r   r   rs   s   `            r   merge_charszWordExtractor.merge_chars  s)   -m<CV"1%h/-2B52II
"9-18$--d>O>OAVX	q!(+6!9q=$]3MH!9q=VAY]HAY]H GG CP  J&" 
 ## 	.C%a(-DI	. r   r5   c                    |d   rQ| j                   }| j                  }|d   }|d   }| j                  r|d   }|d   }|d   }	nc|d    }|d    }|d    }	nP| j                  }| j                   }|d   }|d   }| j                  r|d   }|d   }|d   }	n|d    }|d    }|d    }	t	        |	|k  xs |	||z   kD  xs |||z   kD        S )a  This method takes several factors into account to determine if
        `curr_char` represents the beginning of a new word:

        - Whether the text is "upright" (i.e., non-rotated)
        - Whether the user has specified that horizontal text runs
          left-to-right (default) or right-to-left, as represented by
          self.horizontal_ltr
        - Whether the user has specified that vertical text the text runs
          top-to-bottom (default) or bottom-to-top, as represented by
          self.vertical_ttb
        - The x0, top, x1, and bottom attributes of prev_char and
          curr_char
        - The self.x_tolerance and self.y_tolerance settings. Note: In
          this case, x/y refer to those directions for non-rotated text.
          For vertical text, they are flipped. A more accurate terminology
          might be "*intra*line character distance tolerance" and
          "*inter*line character distance tolerance"

        An important note: The *intra*line distance is measured from the
        *end* of the previous character to the *beginning* of the current
        character, while the *inter*line distance is measured from the
        *top* of the previous character to the *top* of the next
        character. The reasons for this are partly repository-historical,
        and partly logical, as successive text lines' bounding boxes often
        overlap slightly (and we don't want that overlap to be interpreted
        as the two lines being the same line).

        The upright-ness of the character determines the attributes to
        compare, while horizontal_ltr/vertical_ttb determine the direction
        of the comparison.
        r   r,   r+   r-   r.   )r   r   r   r   r]   )
rH   	prev_char	curr_charrp   yaycyaxbxcxs
             r   char_begins_new_wordz"WordExtractor.char_begins_new_word  s-   N Y  A  A5!B5!B""t_t_t_o%o%o%   A  A4B4B  u%x(u%))&&))"W R!V R!V
 	
r   c              #   ~  K   g dfd	}|D ]  }|d   }| j                   s!|j                         r |d       E d {    5|| j                  v r! ||      E d {     |d       E d {    dr&| j                  d   |      r ||      E d {    j	                  |        r y y 7 w7 Z7 L7 &w)Nc              3   *   K   r | g y | gy wrY   r   )new_charcurrent_words    r   start_next_wordz:WordExtractor.iter_chars_to_words.<locals>.start_next_word  s"      ""!)!12LzLs   r'   ru   rY   )r   isspacer   r   r   )rH   r   r   charr'   r   s        @r   iter_chars_to_wordsz!WordExtractor.iter_chars_to_words  s     	B " 	*D<D((T\\^*4000222*4000*4000$";";L<Ld"S*4000 ##D)	*"   1 10 1sE   :B=B5B=B7B=,B9-'B=B;!B=7B=9B=;B=c              #   r  K   dt         fd}t        t        |      |d      D ]  }|d   d   }|rdnd}t        |t        |      | j                        }|D ]W  }|rdnd}t        |t        |            }	|r| j                  s!n| j                  st        |	      E d {    N|	E d {    Y  y 7 7 w)Nr5   c                      t        | d          S Nr   )r\   ro   s    r   upright_keyz2WordExtractor.iter_sort_chars.<locals>.upright_key  s    )%%%r   r   r   rl   r+   rr   )	r\   r~   r:   r   r   r|   r   r   r   )
rH   rO   r   upright_clusterr   cluster_keysubclustersscsort_keyto_yields
             r   iter_sort_charszWordExtractor.iter_sort_chars  s     	&c 	&  /tE{KK 	(O%a(3G&-(4K *K!8$:J:JK " 	(#*4!"*X*>? 07++D<M<M'111'''	(	(  2's$   BB7B3
B7)B5*
B75B7c              #     K   | j                   r|n| j                  |      }t        dg| j                   }t	        j
                  ||      }|D ]0  \  }}| j                  |      D ]  }| j                  |      |f  2 y wr   )re   r   r   r   	itertoolsgroupbyr   r   )rH   rO   r   grouping_keygrouped_charskeyvals
char_group
word_charss           r   iter_extract_tuplesz!WordExtractor.iter_extract_tuples.  s     !%!3!39M9Me9T!)?d.>.>?!))-F#0 	AGZ"66zB A
''
3Z@@A	As   BBc                 H    t        t        | j                  |                  S rY   )r`   r:   r   )rH   rO   s     r   extract_wordmapzWordExtractor.extract_wordmap8  s    tD44U;<==r   rO   c                 H    t        d | j                  |      D              }|S )Nc              3   &   K   | ]	  \  }}|  y wrY   r   )r   r   r   s      r   r   z.WordExtractor.extract_words.<locals>.<genexpr><  s     R.dJTR   )r:   r   )rH   rO   wordss      r   extract_wordszWordExtractor.extract_words;  s"    R$2J2J52QRRr   )r   r   r   DEFAULT_X_TOLERANCEr   r]   rI   r:   r   r   r   r   r   r`   r   r   r   r   r   r   r   i  s~     ('!&"@ 	@:# #JI
 
	I
V>(0A> >4 D r   r   rO   c                 6    t        di |j                  |       S )Nr   )r   r   )rO   kwargss     r   r   r   @  s    "6"0077r   c           
         |j                  ddi       t        di t        D ci c]  }||v s|||    c}}|j                  |       } |j                  di t
        D ci c]  }||v s|||    c}}|S c c}w c c}w )Nrf   Tr   )updater   WORD_EXTRACTOR_KWARGSr   r   TEXTMAP_KWARGS)rO   r   k	extractorwordmaptextmaps         r   chars_to_textmapr   H  s    
MM;%& !6
FA!v+1fQi<
FI ''.G g   !/
?A1;1fQi<
?G N G @s   	A:A:!	A?+A?c           
         t        |       } t        |       dk(  ry|j                  d      rt        | fi |j                  S |j                  dt
              }t        di t        D ci c]  }||v s|||    c}}|j                  |       }|r	|d   d   }nd}|dk(  r7|j                  d        d	j                  |D cg c]  }|d
   	 c}      }|S |dk(  r7|j                  d        d	j                  |D cg c]  }|d
   	 c}      }|S t        |t        d      |      }dj                  d |D              }|dk(  r/dj                  t        |      D 	cg c]  }	|	dk7  r|	nd	 c}	      }|S c c}w c c}w c c}w c c}	w )Nr   rB   rb   r   r   r   c                     | d   | d    fS Nr-   r,   r   ws    r   rq   zextract_text.<locals>.<lambda>i  s    ag%y%9 r   rr   ri   r'   r   c                     | d    | d   fS r   r   r   s    r   rq   zextract_text.<locals>.<lambda>l  s    qwh%%9 r   rl   rv   c              3   L   K   | ]  }d j                  d |D                yw)ri   c              3   &   K   | ]	  }|d      ywr   r   )r   r   s     r   r   z)extract_text.<locals>.<genexpr>.<genexpr>p  s     &EtF|&Er   N)rD   )r   lines     r   r   zextract_text.<locals>.<genexpr>p  s     X$chh&E&EEXs   "$r   r   )r>   rx   r   r   rF   r   r   r   r   sortrD   r~   r   r   )
rO   r   r   r   r   r   r   r   r%   rV   s
             r   extract_textr   V  s   ENE
5zQzz(00:::jj0CD! 
%:Ja6kq&)|J
	 ''.Qx
+HHr>JJ9J:HH7Aai78E  _JJ9J:HH7Aai78E  $E:h+?MEIIXRWXXE3HUO TqqDy!c"9 TU) K 8 8
 !Us   '	E81E8E=?FF
line_charsc                     d}d }t        | t        d            D ]!  }||d   ||z   kD  r|dz  }|d   }||d   z  }# |S )NrB   r+   rr   ri   r-   r'   )r|   r   )r  	tolerancecolllast_x1r   s        r   collate_liner  w  sf     DGzz$'78 d4jGi4G&HCKDt*V	
 Kr   c                     t        dddd      t        dd      dt        ffd} ||       }t        || j                  	      S )
u   
    Removes duplicate chars — those sharing the same text, fontname, size,
    and positioning (within `tolerance`) as other characters in the set.
    fontnamesizer   r'   rl   r+   rO   c              3     K   t        |       }t        j                  |      D ]V  \  }}t        t	        |      t        d            D ]/  }t        |t        d            D ]  }t        |      d     1 X y w)Nrr   rl   r+   r   )r|   r   r   r~   r:   r   )	rO   sorted_charsgrp	grp_chars	y_cluster	x_clusterrs   pos_keyr  s	         r   yield_unique_charsz(dedupe_chars.<locals>.yield_unique_chars  s     e-'//#F 	<NC,YH!5y <	 "1z$/" <I !8;;<<	<s   A?Brr   )r   r:   r|   index)rO   r  r  dedupedrs   r  s    `  @@r   dedupe_charsr    sJ    
 ZF
;C4(G	<$ 	< !'G'u{{++r   c                 @    t        |       }| d   | d   k(  rdnd|d<   |S )Nr,   r.   hvorientation)r^   )r   edges     r   line_to_edger    s+    :D"&u+h"?#cDKr   c           	      ^   t        d      D cg c]  }t        |        c}\  }}}}|j                  dd| d   | d   dd       |j                  dd| d   | d   | d	   z   | d
   | d	   z   dd       |j                  dd| d   dd       |j                  dd| d   dd       ||||gS c c}w )N   	rect_edger   y1r,   r  )object_typeheighty0r.   r  r!  r   rl   )r  r   r  r,   rl   r  r+   r  )r  widthr-   r  r-   )r  r"  r+   r  )r   r^   r   )rectrp   r,   r.   leftrights         r   rect_to_edgesr&    s    49!H=qT
=CuJJ&t*5k	
 MM&t*;h/8ntH~5	
	 	KK&t*		
 
LL&t*		
 u%%I  >s   B*c                    t        | d   | d   dd        }|D cg c]  \  }}dt        |d   |d         t        |d   |d         t        |d   |d         t        |d   |d         | d   | d   z
  z   t        |d   |d         t        |d   |d   z
        t        |d   |d   z
        |d   |d   k(  rdn|d   |d   k(  rdnd d		 c}}S c c}}w )
Nptsr   
curve_edger   rl   r,   r  r  )	r  r+   r-   r,   rl   r.   r"  r   r  )zipr   r   abs)curvepoint_pairsp0p1s       r   curve_to_edgesr0    s   eElE%L$45K " B (beRU#beRU#r!ube$"Q%A'5?U5\+IJ"Q%A'AA'"Q%"Q%-("$Q%2a5.3ber!unsRV
	
  s   B0Cc                 f    | d   }d|v r| gS |dk(  rt        |       gS t        t        d|   |       S )Nr  _edger   )r#  r,  )r  r&  r0  )objts     r   obj_to_edgesr5    sD    MA!|u	
fS!""%?B3GGr   c                 l    dvrt        d      dt        ffd}t        t        ||             S )N)r  r  NOrientation must be 'v' or 'h'r5   c                     | d   dk(  rdnd}| d   k(  nd}d u xs | d   k(  }t        |xr |xr | |   k\        S )Nr  r  r   r"  r  Tr]   )edim
et_correctorient_correct	edge_type
min_lengthr  s       r   testzfilter_edges.<locals>.test  sd    M*c1hw6?6KQ}%2QU
$,O-0@K0OJL>Lqv7KMMr   )rz   r]   r:   filter)edgesr  r>  r?  r@  s    ``` r   filter_edgesrC    s<     **9::N4 N tU#$$r   c                    |dk(  rt        |       D cg c]  }|g c}S t        |       dk  rt        |       D cg c]  }|g c}S g }t        t        |             } | d   g}| d   }| dd  D ]2  }|||z   k  r|j                  |       n|j                  |       |g}|}4 |j                  |       |S c c}w c c}w )Nr      r   )r|   rx   r:   r   )xsr  rp   rN   current_grouplasts         r   cluster_listrI    s    A~#BZ(((
2w{#BZ(((F	fRj	BUGMa5DV 	!"  #MM-(CM MM- M )(s
   
B6
B;c           
          t        t        t        |             |      }t        |      D cg c]  \  }}|D cg c]  }||f c} }}}}t	        t        j                  |       S c c}w c c}}}w rY   )rI  r:   setr}   r^   r   chain)valuesr  clustersr   value_clustervalnested_tupless          r   make_cluster_dictrR    sr    DV-y9H ENhDW 0@=]+c#q+M  	/00 	,s   A.A)A.)A.c           
      F  
 t              st              t        |       }t        ||      
t        d      t        d      }}t	        
fd| D        |      }t        j                  ||      }|D 	cg c]  \  }}	t        t        ||	             c}	}S c c}	}w )Nr   r   c              3   P   K   | ]  }|j                   |            f  y wrY   )r   )r   rp   cluster_dictkey_fns     r   r   z"cluster_objects.<locals>.<genexpr>  s%     J!a!1!1&)!<=Js   #&rr   )callabler   rE   rR  r|   r   r   r:   )rF  rV  r  rM  get_0get_1cluster_tuplesgroupedr   r  rU  s    `        @r   r~   r~     s    FF#_F$VY7La=*Q-5EJrJPUVNE:G,34DAqDUA444s   :Baxisc                 4   |dv sJ |dk(  rd| d   |z   fd| d   |z   fg}|dk(  rAd| d   |z   fd| d   |z   fg}d| v r|d| d   |z   fgz  }d	| v r|d	| d	   |z
  fd
| d
   |z
  fgz  }| j                  t        | j                               t              z         S )N)r  r  r  r+   r-   r  r,   r.   rl   r!  r  )	__class__tupleitems)r3  r\  value	new_itemss       r   move_objectrc  &  s    :s{3t9u$%3t9u$%
	 s{CJ&'s8}u,-
	 s?8S]U%:;<<I3;s4y5()s4y5() I ==syy{+eI.>>??r   attrc                    ddddd|   }t        |       }t        |t        |      |      }|D cg c],  }t        t	        t        |      |            t        |      z  . }}t        ||      D 	cg c]&  \  }}|D 	cg c]  }	t        |	|||	|   z
         c}	( }
}}}	t        t        j                  |
       S c c}w c c}	w c c}	}}w )Nr  r  r+   r-   r,   r.   )
r:   r~   r   sumrE   rx   r*  rc  r   rL  )objsrd  r  r\  	list_objsrN  clusteravgsavgr3  snapped_clusterss              r   snap_objectsrn  <  s    S<TBDT
Iy*T*:IFHNVW7CJt$g./#g,>WDW  $/ GS =DDSS$c$i	0D  	!1233 XDs   1B=3C?CCCc                     g g d}| D ]  }||d      j                  |        t        |d   d|      }t        |d   d|      }||z   S )zs
    Given a list of edges, snap any within `tolerance` pixels of one another
    to their positional average.
    r  r  r  r  r+   r  r,   )r   rn  )rB  r   r   by_orientationr:  	snapped_v	snapped_hs          r   
snap_edgesrt  H  sh     B'N 3q'(//23 ^C0$DI^C0%EIy  r   rs   c                    |dv sJ | |   }||z
  }||fg}|dk(  r$|| d   k  sJ |j                  d| d   |z
  f       n|dk(  r$|| d   k\  sJ |j                  d|| d   z
  f       n|dk(  rZ|| d   k  sJ |j                  d| d   |z   f       |j                  d| d   |z
  f       d	| v r_|j                  d	| d	   |z
  f       nE|dk(  r@|| d   k\  sJ |j                  d| d   |z   f       d
| v r|j                  d
| d
   |z
  f       | j                  t        | j                               t        |      z         S )Nrf  r+   r-   r"  r,   r.   rl   r   r  r!  )r   r^  r_  r`  )r3  rs   ra  	old_valuediffrb  s         r   resize_objectrx  Z  s   ///CI9D	eI d{D	!!'3t9u#456	D	!!'53t9#456	H%%(CMD$89:(CMD$89:3;dCI$456	E
""(CMD$89:3;dCI$456==syy{+eI.>>??r   r  c                 .   |dk(  rd\  }}n|dk(  rd\  }}nt        d      t        t        | t        |                  }|d   g}|dd	 D ]F  }|d
   }||   ||   |z   k  r ||   ||   kD  s"t	        ||||         |d
<   6|j                  |       H |S )z
    Given a list of edges along the same infinite line, join those that
    are within `tolerance` pixels of one another.
    r  )r+   r-   r  )r,   r.   r7  rr   r   r   Nru   )rz   r:   r|   r   rx  r   )	rB  r  r  min_propmax_propsorted_edgesjoinedr:  rH  s	            r   join_edge_groupr~  u  s    
 c'(		,(9::u*X*>?@L1oF!" bzX;4>I56{T(^+*41X;Gr
 MM! Mr   c                     d }|dkD  s|dkD  rt        | ||      } t        | |      }t        j                  ||      }fd|D        }t	        t        j
                  |       } | S )z|
    Using the `snap_edges` and `join_edge_group` methods above,
    merge a list of edges into a more "seamless" list.
    c                 .    | d   dk(  rd| d   fS d| d   fS )Nr  r  r,   r  r+   r   )r  s    r   	get_groupzmerge_edges.<locals>.get_group  s-    #%e%%d$$r   r   rr   c              3   X   K   | ]!  \  }}t        ||d    |d    dk(  rn       # yw)r   r  N)r~  )r   r   r`  r   r   s      r   r   zmerge_edges.<locals>.<genexpr>  s=       Au 	1Q4adck*?O	
s   '*)rt  r|   r   r   r:   rL  )	rB  r   r   r   r   r  _sortededge_groupsedge_gens	      ``    r   merge_edgesr    ss    % !/!35"24DEU	*G##G;K $	H (+,ELr   c                 (    | d   | d   | d   | d   dS )zd
    Return the rectangle (i.e a dict with keys "x0", "top", "x1",
    "bottom") for an object.
    r   r   rE  r)   )r+   r,   r-   r.   r   )bboxs    r   bbox_to_rectr    s#    
 q'$q'aDGLLr   c                 *    t        t        |             S )z
    Given an iterable of objects, return the smallest rectangle (i.e. a
    dict with "x0", "top", "x1", and "bottom" keys) that contains them
    all.
    )r  rR   objectss    r   objects_to_rectr    s     011r   c                 p    t        |  \  }}}}t        |      t        |      t        |      t        |      fS )zk
    Given an iterable of bounding boxes, return the smallest bounding box
    that contains them all.
    )r*  r   r   )bboxesr+   r,   r-   r.   s        r   merge_bboxesr    s5    
 v,BRGSXs2wF44r   c                 4    t        t        t        |             S )zd
    Given an iterable of objects, return the smallest bounding box that
    contains them all.
    )r  rE   bbox_getterr  s    r   rR   rR     s    
 K122r   word_thresholdc           
      |   t        | t        d      d      }t        fd|      }t        t	        t
        |            }t        |      dk(  rg S t        t	        t        d      |            }t        t	        t        d      |            }g }|D ])  }||||d   |d   ||z
  dd|||d	   |d	   ||z
  ddgz  }+ |S )
zi
    Find (imaginary) horizontal lines that connect the tops
    of at least `word_threshold` words.
    r,   r   c                      t        |       k\  S rY   rx   rp   r  s    r   rq   z"words_to_edges_h.<locals>.<lambda>      c!f&> r   r   r+   r-   r  )r+   r-   r,   r.   r"  r  r.   )	r~   r   rA  r:   rE   r  rx   r   r   )	r   r  by_toplarge_clustersrectsmin_x0max_x1rB  rs	    `       r   words_to_edges_hr    s    
 UJu$5q9F>GN_n56E
5zQ	Z%u-.FZ%u-.FE 
 xE(&" {H+&"
 	

0 Lr   c                     | \  }}}}|\  }}}}	t        ||      }
t        ||      }t        ||	      }t        ||      }||
z
  }||z
  }|dk\  r|dk\  r||z   dkD  r|
|||fS y )Nr   )r   r   )aba_lefta_topa_righta_bottomb_leftb_topb_rightb_bottomo_lefto_righto_bottomo_topo_widtho_heights                   r   get_bbox_overlapr    s    '($FE7H'($FE7H F'7#G8X&HuEG%H1}A(W*<q*@w11r   c           
         t        | t        d      d      }t        | t        d      d      }d }t        | |d      }||z   |z   }t        |d       }t        fd|      }t	        t        t        |            }	g }
|	D ]*  t        fd|
D              }|r|
j                         , t        |
      d	k(  rg S t        t        |
      }t	        t        |t        d                  }t        t        t        d      |            }t        t        t        d
      |            }t        t        t        d      |            }|D cg c]  }|d   |d   ||||z
  dd c}||||||z
  ddgz   S c c}w )zy
    Find (imaginary) vertical lines that connect the left, right, or
    center of at least `word_threshold` words.
    r+   r   r-   c                 0    t        | d   | d   z         dz  S )Nr+   r-   rE  rm   )r   s    r   
get_centerz$words_to_edges_v.<locals>.get_center  s    T$Z$t*,-11r   c                     t        |        S rY   r  ro   s    r   rq   z"words_to_edges_v.<locals>.<lambda>  s    c!fW r   rr   c                      t        |       k\  S rY   r  r  s    r   rq   z"words_to_edges_v.<locals>.<lambda>  r  r   c              3   6   K   | ]  }t        |        y wrY   )r  )r   rV   r  s     r   r   z#words_to_edges_v.<locals>.<genexpr>$  s     JA&tQ/Js   r   r,   r.   r  r+   r-   r,   r.   r   r  )r~   r   r|   rA  r:   rE   rR   anyr   rx   r  r   r   )r   r  by_x0by_x1r  	by_centerrN  sorted_clustersr  r  condensed_bboxesoverlapcondensed_rectssorted_rectsr  min_top
max_bottomr  r  s    `                @r   words_to_edges_vr    s    E:d#3Q7EE:d#3Q7E2  z15Iu}y(H X+<=O>PN #o~67F  *J9IJJ##D)*
 !	,(89OJt4DEFLZ%|45F#j'67GSH-|<=J 
  D'D'  7*	

   7*	
		  
s   E5c           	         i }dD cg c]  t        t        fd|              c}\  }}t        |t        dd            D ]  }t        |t        dd            D ]  }|d   |d   |z   k  s|d   |d   |z
  k\  s!|d   |d   |z
  k\  s0|d   |d   |z   k  s?|d   |d   f}	|	|vrg g d||	<   ||	   d   j	                  |       ||	   d	   j	                  |         |S c c}w )
zi
    Given a list of edges, return the points at which they intersect
    within `tolerance` pixels.
    rp  c                     | d   k(  S )Nr  r   )rp   os    r   rq   z(edges_to_intersections.<locals>.<lambda>O  s    a.!3 r   r+   r,   rr   r.   r-   r  r  )r:   rA  r|   r   r   )
rB  r   r   intersectionsr  v_edgesh_edgesr  r  vertexs
       `     r   edges_to_intersectionsr  H  s'   
 MFPABV3U;<GW GD%!89 5Zt%<= 	5A5ah45x[QuX%;<tW4;!67tW4;!67D'1U8,.242,>M&)f%c*11!4f%c*11!4	55 !s   C(c                     t        |       S )z0
    Return the bounding box for an object.
    )r  )r3  s    r   obj_to_bboxr  a  s     sr   c                      dt         f fdt        t         j                                     t	              dt
        f fdfdt        t	                    D        }t        t        d|            S )a8  
    Given a list of points (`intersections`), return all rectangular "cells"
    that those points describe.

    `intersections` should be a dictionary with (x0, top) tuples as keys,
    and a list of edge objects as values. The edge objects should correspond
    to the edges that touch the intersection.
    r5   c                    d }| d   |d   k(  r5 ||    d         j                   ||   d               }t        |      ry| d   |d   k(  r5 ||    d         j                   ||   d               }t        |      ryy)Nc                 4    t        t        t        |             S rY   )rK  rE   r  )rB  s    r   edges_to_setzCintersections_to_cells.<locals>.edge_connects.<locals>.edges_to_sets  s    s;.//r   r   r  Tr   r  F)intersectionrx   )r/  p2r  commonr  s       r   edge_connectsz-intersections_to_cells.<locals>.edge_connectsr  s    	0 a5BqE>!-"3C"89FF]2.s34F 6{a5BqE>!-"3C"89FF]2.s34F 6{r   r   c                 f   |dz
  k(  ry | |   }| |dz   d  }|D cg c]  }|d   |d   k(  s| }}|D cg c]  }|d   |d   k(  s| }}|D ]U  } 
||      s|D ]D  } 
||      s|d   |d   f}	|	v s 
|	|      s& 
|	|      s0|d   |d   |	d   |	d   fc c S  W y c c}w c c}w )Nr   r   r   )pointsr   ptrestrp   belowr%  below_ptright_ptbottom_rightr  r  n_pointss             r   find_smallest_cellz2intersections_to_cells.<locals>.find_smallest_cell  s   1AYa!eg 2qAaDBqEM22 2qAaDBqEM22 	LH X.! L$R2 (Xa[9 "]2%lH=%lH=qE2a5,q/<?KKL		L  % 32s   B)B)B.B.c              3   0   K   | ]  } |        y wrY   r   )r   r   r  r  s     r   r   z)intersections_to_cells.<locals>.<genexpr>  s     J!"61-Js   N)r]   r:   r|   keysrx   r\   r   rA  )r  cell_genr  r  r  r  s   ` @@@@r   intersections_to_cellsr  h  sf     & &++-./F6{Hc 4 KuS[7IJHtX&''r   c                 H   dt         fd}t        |      }t               g }g }t        |      rt        |      }t        |      D ]  } ||      }t        |      dk(  r1t        |      z  |j	                  |       |j                  |       Jt        fd|D              }	|	dkD  sdt        |      z  |j	                  |       |j                  |        t        |      |k(  r:|j	                  t        |             j                          |j                          t        |      rt        |      r|j	                  t        |             t        t        |      dz
  dd      D ]  }
t               }t               }t               }||
   D ]/  }||z  }|j                  |d          |j                  |d          1 t        |      dk  s9t        |      dk  s+t        j                  | j                  |t                    s||
=  t        |d	 
      }|S )z
    Given a list of bounding boxes (`cells`), return a list of tables that
    hold those cells most simply (and contiguously).
    r5   c                 ,    | \  }}}}||f||f||f||ffS rY   r   )r  r+   r,   r-   r.   s        r   bbox_to_cornersz(cells_to_tables.<locals>.bbox_to_corners  s/    "CVS	B<"cRLAAr   r   c              3   &   K   | ]  }|v  
 y wrY   r   )r   rV   current_cornerss     r   r   z"cells_to_tables.<locals>.<genexpr>  s     "NA1#7"Ns   r   ru   rE  textpagec                 &    t        d | D              S )Nc              3   0   K   | ]  }|d    |d   f  yw)r   r   Nr   )r   rV   s     r   r   z4cells_to_tables.<locals>.<lambda>.<locals>.<genexpr>  s     .G!ad|.Gs   )r   )r4  s    r   rq   z!cells_to_tables.<locals>.<lambda>  s    3.GQ.G+G r   rr   )r_  r:   rK  rx   r   removerg  clearr   r   addwhite_spaces
issupersetget_textboxTEXTPAGEr|   )pagecellsr  remaining_cellscurrent_cellstablesinitial_cell_countcellcell_cornerscorner_countr   r  x1_valsx0_valsrV   r  r  s                   @r   cells_to_tablesr     s   B B 5kO
 eOMF
o
 /) 	1D*40L=!Q&3|#44$$T*&&t,  #"N"NN  !##s<'88O!((.#**40#	1( }!33MM$}-.!!#!5 o
> =d=)* 3v;?B+ L%% 	AFAKK!KK!	
 L17|a&&  % !  q	%, V!GHGNr   c                       e Zd Zd Zy)	CellGroupc                 X   || _         t        t        t        d      t	        d |                  t        t        t        d      t	        d |                  t        t        t        d      t	        d |                  t        t        t        d      t	        d |                  f| _        y Nr   r   rE  r)   )r  r   rE   r   rA  r   r  )rH   r  s     r   rI   zCellGroup.__init__  sz    
JqM6$#678JqM6$#678JqM6$#678JqM6$#678	
	r   N)r   r   r   rI   r   r   r   r  r    s    
r   r  c                       e Zd Zy)TableRowNr   r   r   r   r  r    r   r   r  c                       e Zd ZdZd Zy)TableHeaderz9PyMuPDF extension containing the identified table header.c                 <    || _         || _        || _        || _        y rY   )r  r  namesexternal)rH   r  r  r
  aboves        r   rI   zTableHeader.__init__
  s    	

r   N)r   r   r   r[   rI   r   r   r   r  r    s
    Cr   r  c                       e Zd Zd Zed        Zedefd       Zedefd       Z	edefd       Z
defdZddZd	 Zdd
Zy)Tablec                 J    || _         || _        | j                         | _        y rY   )r  r  _get_headerheader)rH   r  r  s      r   rI   zTable.__init__  s     	
&&(r   c           
         | j                   }t        t        t        d      |            t        t        t        d      |            t	        t        t        d      |            t	        t        t        d      |            fS r  )r  r   rE   r   r   )rH   rV   s     r   r  z
Table.bbox  sa    JJJqM1%&JqM1%&JqM1%&JqM1%&	
 	
r   r5   c                    t        | j                  t        dd            }t        t        t	        t        t        d      | j                                          }g }t        j                  |t        d            D ]Q  \  }}|D ci c]  }|d   |
 }}t        |D cg c]  }|j                  |       c}      }	|j                  |	       S |S c c}w c c}w )Nr   r   rr   )r|   r  r   r:   rK  rE   r   r   r  r   r   )
rH   r  rF  rowsr   	row_cellsr  xdictrp   rows
             r   r  z
Table.rows!  s    Aq)9:&SA

;<=>%--gz!}E 	LAy/89tT!Wd]9E9"5QEIIaL56CKK	  :5s   CC
c                 ,    t        | j                        S rY   )rx   r  )rH   s    r   	row_countzTable.row_count,  s    499~r   c                 x    t        | j                  D cg c]  }t        |j                         c}      S c c}w rY   )r   r  rx   r  )rH   r  s     r   	col_countzTable.col_count0  s'    $))4QCL4554s   7c           	         t         }g }dt        fd}| j                  D ]  }g }|D cg c]  } |||j                        s| }}|j                  D ]{  }	|	d }
nc|D cg c]  } |||	      s| }}t        |      r=|	d   |d<   |	d   |d<   d|v r|	d   |	d   z
  |d	<   |	d
   |	d   z
  |d<   t        |fi |}
nd}
|j                  |
       } |j                  |        |S c c}w c c}w )Nr5   c                     | d   | d   z   dz  }| d   | d   z   dz  }|\  }}}}t        ||k\  xr ||k  xr ||k\  xr ||k        S )Nr,   r.   rE  r+   r-   r9  )r   r  v_midh_midr+   r,   r-   r.   s           r   char_in_bboxz#Table.extract.<locals>.char_in_bbox8  sm    %[4>1Q6E$Z$t*,1E"&BR"V52:VESLVuv~ r   r   r   r   r   rb   rE  r   r)   r   rB   )CHARSr]   r  r  r  rx   r   r   )rH   r   rO   	table_arrr   r  arrr   	row_charsr  	cell_text
cell_charss               r   extractzTable.extract4  s2   		 	 99 	"CC*/P$<chh3OPIP		 &< $I *3"!%l46N"J " :,0Gy),0Gy)#v-59!WtAw5FF>26:1gQ6GF?3$0$Fv$F	$&	

9%#&$ S!-	"0 - Q"s   C&C&C+.C+c                    d}t        | j                  j                        D ]U  \  }}||dk(  rd|dz    }|j                  dd      }|r%t	        j
                  |j                  dd	            }||dz   z  }W |dz  }|ddj                  d
 t        | j                        D              z   dz   z  }| j                  j                  rdnd}| j                         |d D ]f  }d}t        |      D ]J  \  }}|dn|j                  dd      }|r%t	        j
                  |j                  dd	            }||dz   z  }L |dz  }||z  }h |dz   S )zOutput table content as a string in Github-markdown format.

        If clean is true, markdown syntax is removed from cell content.|NrB   Colr   rv   ri   -z&#45;c              3       K   | ]  }d   yw)z---Nr   )r   r   s     r   r   z$Table.to_markdown.<locals>.<genexpr>j  s      F1 Fs   z|
r   )r}   r  r
  replacehtmlescaperD   r   r  r  r'  )	rH   cleanoutputr   namejr  r   r  s	            r   to_markdownzTable.to_markdownZ  sg     !!2!23 	!GAt|trzQqSE{<<c*D{{4<<W#=>dSj F	! 	$# Fdnn0E FFFNN %%A1 <<>!"% 		CD$S> #4!\rt||D#/F;;t||C'ABDs
"# DLDdNF		 }r   c                 V   	 ddl }i }| j                         }| j                  }| j                  j
                  }t        |      }t        |      D ]  }||   }	|	rd| ||<    |t        t        |            k7  r(t        |      D ]  }||   }	|	d| k7  s| d|	 ||<    |j                  s|dd }t        |      D ]>  }||   }
g }t        t        |            D ]  }|j                  ||   |           |||
<   @ |j                  |      S # t        $ r t        d        w xY w)z/Return a pandas DataFrame version of the table.r   Nz!Package 'pandas' is not installedr*  r+  r   )pandasModuleNotFoundErrorr   r'  r  r
  rx   r   rK  r  r   	DataFrame)rH   r   pdpd_dictr'  hdrr
  hdr_lenr   r2  rs   ra  r3  s                r   	to_pandaszTable.to_pandas|  sR   	
 ,,.kk!!e*w 	%A8D 9a	% c#e*o%7^ -QxS9$"#AdV}E!H-
 ||abkGw 	!A(CE3w<( ,WQZ]+, GCL	! ||G$$= # 	78	s   D D(c                 4	  % | j                   %|}%fd}	 | j                  d   }|j                  }t        |j                        }t        ||| j                         d   d      }t        | j                        dk  r|S t        |      dk  r|S |dd D cg c]  }||d   nd }	} ||      }
|}d|_	        |j                  |_
        g }%j                  d|t              d	   D ]D  }|d
   D ]:  }|d   D ]0  }|d   dz  r|d   j                         s |j                  |       2 < F g }g }g }|j                  d d       t!        t        |            D ]  }||   }|d   d   }||d   d   z
  }|d   dz  }|dk(  r4|j                  |       |j                  |       |j                  |       \|d   }|d   }|d   }|r|s n||z
  |k  st#        ||z
  |d   d   z
        |k  r%|d   d   ||z
  |d   d   |f|d<   |||<   |r||d<   ||z
  d|z  kD  r n5|j                  |       |j                  |       |j                  |        |g k(  r|S |dd }|j                  |d   z
  |d   k\  r|S |
r|d   s|S |g k(  r|S t%               }|D cg c]  }|d   d   |d   k\  s| c}D ]
  }||d   z  } |j&                  s|}|j                  |_
        %j                  d|      D cg c]  }t        |dd        }}t)        t+        t-        |D cg c]  }|d   	 c}            d      }g }|D ]N  }|	D  cg c]'  } | #|D ]  }|d   |k(  r|d   | k  r|d   | kD  r| |f  ) }!} }|!g k(  r|j                  |       N n |g k(  r|S |}"|d   |"_	        |D cg c]&  }| |d   |"j                  |d   |"j                  fnd( }#}| j                  d   |"_        | j                  d   |"_        |#D cg c]E  }|?%j3                  |      j5                  dd      j5                  dd      j                         ndG }$}t        t7        |"      |#|$d      S # t
        $ r Y yw xY wc c}w c c}w c c}w c c}w c c}} w c c}w c c}w )aj  Identify the table header.

        *** PyMuPDF extension. ***

        Starting from the first line above the table upwards, check if it
        qualifies to be part of the table header.

        Criteria include:
        * A one-line table never has an extra header.
        * Column borders must not intersect any word. If this happens, all
          text of this line and above of it is ignored.
        * No excess inter-line distance: If a line further up has a distance
          of more than 1.5 times of its font size, it will be ignored and
          all lines above of it.
        * Must have same text properties.
        * Starting with the top table line, a bold text property cannot change
          back to non-bold.

        If not all criteria are met (or there is no text above the table),
        the first table row is assumed to be the header.
        c                     j                  dt        |       d   D ]"  }|d   D ]  }|d   D ]  }|d   dz  s   y  $ y	)
a  Check if row 0 has bold text anywhere.

            If this is true, then any non-bold text in lines above disqualify
            these lines as header.

            bbox is the (potentially repaired) row 0 bbox.

            Returns True or False
            r^   )flagsclipblocksr%   spansr@     TF)get_textr	   )r  r  lsr  s       r   top_row_is_boldz*Table._get_header.<locals>.top_row_is_bold  sa     ]]6d]KHU (7 (AwZ (W:?#'(((
 r   r   NFrE  ru   r^   rA  r@  rB  r%   rC  r@  r   r'   c                     | d   d   S )Nr  r)   r   rG  s    r   rq   z#Table._get_header.<locals>.<lambda>  s    61 r   T)rs   reverser  r)   rD  g      ?   r   rA  r  )rL  rv   ri   z  rB   )r  r  r  r   r  
IndexErrorr  r'  rx   r!  r  rE  r	   stripr   r   r   r+  r   is_emptyr|   r:   rK  r+   r-   r  r-  r_  )&rH   r   y_deltarH  r  r  r  header_top_rowrV   col_xtop_row_boldrA  rC  r  rF  rG  selectline_heights
line_boldsr   r  r  boldr!  h0bold0nclipr   
word_rectsr  	word_topsr,   rp   intersectinghdr_bbox	hdr_cells	hdr_namesr  s&                                        @r   r  zTable._get_header  s   , yy	"	))A,CIIE>D
 %T5$,,.2CUK tyy>A!! u:>!! 6;3BZ
01AMAaDt+
 
 't, u''vDGQ 	(AwZ (7 (AgJNqy/@Q	((	( 
 	

-t
<s5z"  	$AaA61BQvYq\!AW:?D Avb!##A&!!$' Bb!BrNET Bw'!S"r'QvYq\)A%Bg%MvYq\27AfIaL"E&	a%)JrNb38#MM""d#A 	$D R<!! 77VAY,q/1!! 
1!!B;!! "AailfRj&@!A 	AQvYE	~~D'' ,0==t=+LMad1Ra5kM
M4:$>aQqT$> ?@$O	  	C =#	 Q43;1Q4!8!q	 AL  r!c"	 R<!!5Rj 
 78mQqT8;;!hkk2M
	 
 iiliil 
  =   #++D#6>>tSIOOQ
	 
 5?Iy$GGg  		
f B N$>"

sB   0Q' Q6Q;/Q;7R !R
,R
++RA
R'	Q32Q3N)T)r)   )r   r   r   rI   propertyr  r:   r  r\   r  r  r'  r4  r=  r  r   r   r   r  r    s    )
 
 
 d   3   63 6 6$4 $L D"%HAHr   r  c                      e Zd ZU dZeed<   dZeed<   dZeed<   dZ	eed<   e
Zeed<   eZeed<   eZeed	<   eZeed
<   eZeed<   eZeed<   dZeed<   eZeed<   eZeed<   dZeed<   eZeed<   eZeed<   dZeed<   ddZedd       Z y)TableSettingsr%   vertical_strategyhorizontal_strategyNexplicit_vertical_linesexplicit_horizontal_linesr   r   r   r   r   r   r)   r   r    r!   r"   r#   r$   text_settingsc           	      &   t         D ]$  }t        | |      xs ddk  st        d| d       dD ]=  }t        | |dz         }|t        vst        | ddj	                  t               d       | j
                  i | _        d	D ]:  }|| j
                  vs| j
                  j                  d
d      | j
                  |<   < d
| j
                  v r| j
                  d
= dD ]/  \  }}t        | |      t        u st        | |t        | |             1 | S )a  Clean up user-provided table settings.

        Validates that the table settings provided consists of acceptable values and
        returns a cleaned up version. The cleaned up version fills out the missing
        values with the default values in the provided settings.

        TODO: Can be further used to validate that the values are of the correct
            type. For example, raising a value error when a non-boolean input is
            provided for the key ``keep_blank_chars``.

        :param table_settings: User-provided table settings.
        :returns: A cleaned up version of the user-provided table settings.
        :raises ValueError: When an unrecognised key is provided.
        r   zTable setting 'z' cannot be negative)
horizontalvertical	_strategyz_strategy must be one of{,})r   r   r  r)   ))r   r   )r   r   )r   r   )r   r   )r#   r"   )r$   r"   )	NON_NEGATIVE_SETTINGSgetattrrz   TABLE_STRATEGIESrD   rj  r   UNSETsetattr)rH   settingr  strategyrd  fallbacks         r   __post_init__zTableSettings.__post_init__x  sO     - 	RGg&+!q0 ?7);O!PQQ	R 6 	Kt[;%>?H// "m $"234B8 	 %!#D 3 	RD4---+/+=+=+A+A+q+Q""4(	R $,,,"";/
 		=ND( tT"e+dGD($;<		= r   c                     | |        S t        ||       r|S t        |t              r?i }i }|j                         D ]  \  }}|d d dk(  r	|||dd  <   |||<    ||d<    | di |S t        d|       )NrM  text_rj  zCannot resolve settings: r   )r9   r^   r`  rz   )clssettingscore_settingsrj  r   r  s         r   resolvezTableSettings.resolve  s    5L#&O$'MM ( )1Ra5G#+,M!AB%('(M!$	)
 .;M/*'''8
CDDr   )r5   re  rY   )!r   r   r   rf  str__annotations__rg  rh  r:   ri  DEFAULT_SNAP_TOLERANCEr   rn   rt  r   r   DEFAULT_JOIN_TOLERANCEr   r   r   r   DEFAULT_MIN_WORDS_VERTICALr    DEFAULT_MIN_WORDS_HORIZONTALr!   r"   r#   r$   rj  r^   ry  classmethodr  r   r   r   re  re  d  s    $s$&&$(T(&*t*2NE2#e##e#2NE2#e##e#OU ::">%>$%E%&+e+&+e+M43j E Er   re  c                   *    e Zd ZdZddZdefdZd Zy)TableFindera0  
    Given a PDF page, find plausible table structures.

    Largely borrowed from Anssi Nurminen's master's thesis:
    http://dspace.cc.tut.fi/dpub/bitstream/handle/123456789/21520/Nurminen.pdf?sequence=3

    ... and inspired by Tabula:
    https://github.com/tabulapdf/tabula-extractor/issues/16
    Nc                    || _         t        j                  |      | _        | j	                         | _        t        | j
                  | j                  j                  | j                  j                        | _	        t        | j                        | _        t        | j                   | j                        D cg c]  }t        | j                   |       c}| _        y c c}w rY   )r  re  r  r}  	get_edgesrB  r  r#   r$   r  r  r  r   r  r  )rH   r  r}  
cell_groups       r   rI   zTableFinder.__init__  s    	%--h7^^%
3JJMM22MM22

 ,D,>,>?
 .diiD
 $))Z(
 
s   7Cr5   c           
         | j                   }dD ]I  }t        ||dz         }|dk(  st        |d|z   dz         }t        |      dk  s9t        d| d| d	       |j                  }|j
                  }|d
k(  s|d
k(  rt        t        fi |j                  xs i }ng }g }|j                  xs g D ]  }	t        |	t              r+t        |	      D ]  }
|
d   dk(  s|j                  |
        >|j                  |	|	| j                  j                  d   | j                  j                  d   | j                  j                  d   | j                  j                  d   z
  dd        |dk(  rt!        t"        d      }n?|dk(  rt!        t"        dd      }n'|d
k(  rt%        ||j&                        }n
|dk(  rg }ng }||z   }g }|j(                  xs g D ]  }	t        |	t              r+t        |	      D ]  }
|
d   dk(  s|j                  |
        >|j                  | j                  j                  d   | j                  j                  d   | j                  j                  d   | j                  j                  d   z
  |	|	dd        |dk(  rt!        t"        d      }n?|dk(  rt!        t"        dd      }n'|d
k(  rt+        ||j,                        }n
|dk(  rg }ng }||z   }t/        |      t/        |      z   }t1        ||j2                  |j4                  |j6                  |j8                        }t!        ||j:                        S )N)rm  rl  rn  r(   	explicit__linesrE  zIf z"_strategy == 'explicit', explicit_zD_lines must be specified as a list/tuple of two or more floats/ints.r'   r  r  r   r)   r  r%   r&   r   )r>  )r  r  r   )r+   r-   r"  r,   r.   r  )r   r   r   r   )r?  )r}  rr  rx   rz   rf  rg  r   r!  rj  rh  r9   r^   r5  r   r  r#  rC  EDGESr  r    ri  r  r!   r:   r  r   r   r   r   r   )rH   r}  r  rw  r%   v_strath_stratr   
v_explicitdescr:  v_baser  
h_explicith_baser  rB  s                    r   r  zTableFinder.get_edges  sS   ==5 
	Kx{)BCH:%+*Ch*NOu:>$k] +$$/= 1'( 
	 ,,..f6 1!%JH,B,B,HbJEE
44: 	D$%%d+ -A'3."))!,- !!""#yy~~a0"&)).."3"&)).."3diinnQ6G"G'*		" g!%-F&!%?F%eH<W<WXF
"FFZ
66<" 	D$%%d+ -A'3."))!,- !!"iinnQ/"iinnQ/!%!2TYY^^A5F!F#"&'*		" g!%-F&!%?F%h&C&CF 
"FFZQ$q'!%66%66%66%66
 Eh.F.FGGr   c                     t        | j                        }||k\  rt        d      |dk  r||z  }|dk  r| j                  |   S )Nztable not on pager   )rx   r  rO  )rH   r   tcounts      r   __getitem__zTableFinder.__getitem__>  sJ    T[[!;011!eKA !e{{1~r   rY   )r   r   r   r[   rI   r:   r  r  r   r   r   r  r    s"    
aH4 aHFr   r  c                    | j                   dz   }| j                  j                  }| j                  }| j	                  |t
              a| j                  dt              d   }|| j                   z  }|D ]  }|d   D ]  }|d   }	t        |	d   d	      t        |	d   d	      f}	t        |	d   |	d    |	d   |	d   dd      }
|	d   dk(  rd
}nd}t        |d   d       D ]  }|d   }|d   }t        |d         }t        |d   d       D ]m  }t        |d         }||z  }t        |d         |z  }|j                  |
_        |j                   |
_        |d   }i d|r|j$                  |j&                  z
  n|j(                  |j*                  z
  d|j(                  d|j*                  |z   d|d|j(                  |j*                  z
  dt-        |
      ddd|d d!d"d#d$|d|r|n|j(                  |j*                  z
  d%|d&d!d'|d(|j*                  d)||j$                  |j&                  z
  |j&                  |j$                  |j*                  |j(                  d*}t.        j1                  |       p    y!)+z(Extract text as "rawdict" to fill CHARS.r   rI  rawdictr  rB  r%   dirr   r  TFrC  c                     | d   d   S Nr  r   r   rK  s    r   rq   zmake_chars.<locals>.<lambda>n  s    AfIaL r   rr   fontr	  colorrO   c                     | d   d   S r  r   )rV   s    r   rq   zmake_chars.<locals>.<lambda>r  s    &	! r   r  originrV   advr.   rl   r  r   r   ncs	DeviceRGBnon_stroking_colornon_stroking_patternNr  r   page_numberstroking_colorstroking_patternr'   r,   r   )r"  r+   r-   r!  r  )numberr#  r   transformation_matrixget_textpager	   r  rE  r{   r   r|   r   r   r   rp   r:  r   fr-   r+   r  r!  r_  r!  r   )r  rA  r  page_heightctmrB  doctop_baseblockr   ldirr   r   spanr  fontsizer  r   r  bbox_ctmr  r'   	char_dicts                         r   
make_charsr  \  s    ++/K))""K

$
$C  d. AH]]9x]8BF+K ,,'N +	,D;D$q'1%uT!Wa'89DDGd1gXtAwQAFFAw!|tG}2HI #,<<#DM2"4=6LM ,DV-D#czH"4>2S8F%xxFH%xxFH9D!Gtww0477AR! $''! !$''K"7! #H	!
 !$''DGG"3! !%-! {! -e! /! &v! &{! G4779J! )%! +D! !  tww!!" "7#!$ "&477!2"gg"gg&kk&kk-!I0 LL+?,	#,+	,,,r   c                 8    !" |j                   !|j                  "|j                  }|j                  dk(  xs |j                  dk(   j
                  j                   j                  z   j                  dz     j
                  } j                  dv r|j                  \  }}t        dd||      }|t        |      }n|}!"fd !"fd} |       \  }	}
!"fd fd}|
D ]  }|d	   }|d
   r6|d   d   dk(  r+|d   d   dk(  r |j                  d|d   d   |d   d   f       |D ]h  }|d   dvr|d   dk(  r5|dd  \  }} |||||      }|s*t        j                  t        |             I|d   dk(  r_|d   j                         }|j                  |k  r|j                  |j                  k  r~t!        |j"                  |j$                  z         dz  }t'        ||j(                        }t'        ||j*                        } |||||      }|rt        j                  t        |             |j                  |k  r|j                  |j                  k  r~t!        |j*                  |j(                  z         dz  }t'        |j$                  |      }t'        |j"                  |      } |||||      }|rt        j                  t        |              |||j,                  |j.                  |      }|rt        j                  t        |              |||j.                  |j                  |      }|rt        j                  t        |              |||j                  |j0                  |      }|rt        j                  t        |              |||j0                  |j,                  |      }|st        j                  t        |             |d   \  }}}} |||||      }|rt        j                  t        |              |||||      }|rt        j                  t        |              |||||      }|rt        j                  t        |              |||||      }|sKt        j                  t        |             k  dd dd}|	D ]   } |||j,                  |j0                  |      }|rt        j                  t        |              |||j.                  |j                  |      }|rt        j                  t        |              |||j,                  |j.                  |      }|rt        j                  t        |              |||j0                  |j                  |      }|st        j                  t        |              |t3        |t4        t6        f      sJ g }|D ]G  \  }}t'        |      }t'        |      } |||||      }|s*t        j                  t        |             I y )Nr&   r   r   r   r   c                 "   |j                   z
  | j                   cxk  r|j                  z   k  s4n |j                   z
  | j                  cxk  r|j                  z   k  rgn nd|j                  z
  | j                  cxk  r|j                  z   k  s4n |j                  z
  | j                  cxk  r|j                  z   k  ry ny| j                   z
  |j                   cxk  r| j                  z   k  s4n | j                   z
  |j                  cxk  r| j                  z   k  rgn y| j                  z
  |j                  cxk  r| j                  z   k  s4n | j                  z
  |j                  cxk  r| j                  z   k  ry yyy)aM  Detect whether r1, r2 are neighbors.

        Defined as:
        The minimum distance between points of r1 and points of r2 is not
        larger than some delta.

        This check supports empty rect-likes and thus also lines.

        Note:
        This type of check is MUCH faster than native Rect containment checks.
        TF)r+   r-   r!  r  )r1r2snap_xsnap_ys     r   are_neighborsz!make_edges.<locals>.are_neighbors  s6    EEFNbee5ruuv~5uuv~8"%%&.8EEFNbee5ruuv~5uuv~8"%%&.8 9 EEFNbee5ruuv~5uuv~8"%%&.8 	 EEFNbee5ruuv~5uuv~8"%%&.8 9  r   c            	      b   g } 	j                         D ]B  }|d   dk(  r'r%|d   j                  
kD  r|d   j                  kD  r2| j                  |       D t	        t        | D cg c]  }|d   	 c}      d       }g }|r|d   }d}|rYd}t        t        |      d	z
  dd
      D ]8  } |||         s|||   j                  z  }|||   j                  z  }||= d}: |rYt        j                  	j                  |t                    s|j                  |       |d= |r|| fS c c}w )z:Detect and join rectangles of "connected" vector graphics.typer  r#  c                 2    | j                   | j                  fS rY   )r  r+   )r  s    r   rq   z4make_edges.<locals>.clean_graphics.<locals>.<lambda>  s    add| r   rr   r   TFr   ru   r  )get_drawingsr"  r   r   r|   rK  r   rx   tlbrr  r  r  r  )pathspprects	new_rectsprect0repeatr   r  r&   r  r  r  s          r   clean_graphicsz"make_edges.<locals>.clean_graphics  sU    ""$ 
	A &	S  fIOOf,fI$$v-LLO
	 61QvY67=ST	 AYFFs6{Q26 &A$VVAY7&),,.&),,."1I!%&   **4+;+;FX+;+VW  (q	! $ %3 7s   'D,c                     t        | j                  |j                  z
        k  s%t        | j                  |j                  z
        k  ryy)z'Check if line is roughly axis-parallel.TF)r+  rp   r   )r/  r  r  r  s     r   is_parallelzmake_edges.<locals>.is_parallel  s:    rttbdd{v%RTTBDD[)9V)Cr   c                 V    ||      si S t        |j                  |j                        }t        |j                  |j                        }t        |j                  |j                        }t        |j                  |j                        }||j                  kD  s-||j
                  k  s||j                  kD  s||j                  k  ri S ||j
                  k  r|j
                  }||j                  kD  r|j                  }||j                  k  r|j                  }||j                  kD  r|j                  }||z
  }||z
  }	||	cxk(  rdk(  ri S  i d|d|z
  d|d|z
  d|d|	d||f||fgd	| d   d
dddddd| d   r| d   n| d   ddddddddd|||z   d}
|
S )z;Given 2 points, make a line dictionary for table detection.r   r+   r!  r-   r  r"  r   r(  	linewidthstrokeTfillFevenoddr  r  r  Nr  r   r  r  r  )r,   r.   rl   )r   rp   r   r   r-   r+   r  r!  )r  r/  r  rA  r+   r-   r!  r  r"  r   	line_dictdoctop_basisr  r  r  s              r   	make_linezmake_edges.<locals>.make_line  s   2r"Irtt_rtt_rtt_rtt_ <2<2<2<I<B<B<B<BRbFaI  
"
+"
 "
 +"	

 U
 f
 RHr2h'
 7
 d
 E
 u
 AgJajAfI
 !$
 6
 ;
  !
" #D#
$ <')
	, r   r`  	closePathrF  ru   rE  )rF  requr  )r   r   r   )r  r  r"  )r   r   r   rf  rg  r#  r   r  r   r  r   r   r  r  	normalizer"  r+  r-   r+   r   r!  r  r  bltrr9   r_  r:   )#r  rA  tset	add_linesr?  prectr   r  r  r  r  r  r  r`  r   r/  r  r  r#  rp   r   ulurlllrpathr  r  r  r  r&   r  r  r  r  s#   `                          @@@@@@@@r   
make_edgesr    sR   ""F""F%%J.0 	6##~5  ))""K;;,L++/KIIE}}	!xx1Q1a Dz@*  * X #$MFE4l  N:'
 [>eAhqkS0U2Yq\S5HLL#uRy|U1Xa[9: G	:At,,ts{12B%aR6	LLi!891 t~~' JJ*,dkk1IDGGdgg-.2Aq$''*Bq$''*B )!RT :I \)%<= KK:-$++

2JDGGdgg-.2Atww*Btww*B )!RT :I \)%<=%a$''4@	LLi!89%a$''4@	LLi!89%a$''4@	LLi!89%a$''4@	LLi!89 "#1BB%aR6	LLi!89%aR6	LLi!89%aR6	LLi!89%aR6	LLi!89OG	:N:` q9D 2dDGGTWWd;	LLi01dDGGTWWd;	LLi01dDGGTWWd;	LLi01dDGGTWWd;	LLi012" )eT]33	 2B2Y2YdBD1	LLi012r   c           	         | j                   }| j                  }| j                   }|dk(  rBt        dddd|j                  |j                  z
  |j
                  z
  |j                  z
  d      }nq|dk(  rBt        ddddd|j                  |j                  z
  |j                  z
  |j
                  z
        }n*t        ddddd|j
                  z  d|j                  z        }|| j                  z  }dt        |      z  }t        j                  | |d      }|dv r4|\  }}	}
}|	|_        ||_        ||_        |
|_        | j                  |       | j                  d       | j                  }| j                  }||   } | |||fS )zNullify page rotation.

    To correctly detect tables, page rotation must be zero.
    This function performs the necessary adjustments and returns information
    for reverting this changes.
    r   r   r   r   s   %g %g %g %g %g %g cm r  )mediaboxr   r   r  r-   r+   r!  derotation_matrixr_  r
   _insert_contentsset_mediaboxset_rotationparentr  )r  r  rotmbmat0matcmdxrefr+   r!  r-   r  docpnos                 r   page_rotation_set0r    sd    }}H
--C	B
byaAq"%%"%%-"%%"7"%%"?C	aAq!RUURUU]RUU%:RUU%BCaAq"ruu*b255j9 ''
'C
"U3Z
/C!!$Q/D iBB"a ++C
++Cs8DsH$$r   c                     | j                   }|j                  |d       | j                  |       | j                  |       | j                  }||   } | S )zPReset page rotation to original values.

    To be used before we return tables.    )r  update_streamr  r  r  )r  r  r  r  r  r  s         r   page_rotation_resetr    sP     ++CdD!hc
++Cs8DKr   r%   rf  rg  vertical_lineshorizontal_linesr   r   r   r   r   r   r   r    r!   r"   r#   r$   c                 t   g a g at        t        j                               }t        j                  d       | j
                  dk7  rt        |       \  } }}}nd\  }}}|t        }|t        }|
t        }
|t        }|t        }|t        }||}|}i d|d|d|d|d|d	|d
|d|	d|
d|d|d|d|d|d|d|d|||d}t        j                  |      }|| _
        t        | |       t        | |||       t        | |      }t        j                  |       |t        | |||      } |S )NTr   NNNrf  rg  rh  ri  r   r   r   r   r   r   r   r    r!   r"   r#   r$   text_tolerance)text_x_tolerancetext_y_tolerance)r}  rN  )rA  r  r  )r!  r  r]   r
   set_small_glyph_heightsr   r  rt  re  r  table_settingsr  r  r  r  )r  rA  rf  rg  r  r  r   r   r   r   r   r   r   r    r!   r"   r#   r$   r   r  r  rw  r  	old_smallold_xrefold_rotold_mediaboxr}  r  r  s                                 r   find_tablesr	    s   4 EEU2245I	!!$'}}0B40H-h*:''<    '#( '#( $&.2 	"> 	$%5	
 	. 	, 	, 	. 	, 	, 	? 	0 	 4 	!"8 	#$<  	#$<!" 	.#$ -,'H*   ( 3DDt$4di -F	!!),"47LIMr   )r   )NNr   )r   )r   r   rY   r  )`r[   inspectr   r   r.  collections.abcr   dataclassesr   operatorr   rB   r   r   r	   r
   r   r   r   r   r  r!  r  rK  
whitespacer  rn   r   rq  rs  rt  r  r  r  r  r   r   r   r   r  ry   r:   r>   r@   r`   r   r   	signaturer   
parametersr  r   r   r   r  r   r  r  r  r&  r0  r5  rC  rI  r^   rR  r~   rc  rn  rt  rx  r~  r  r  r  r  rR   r\   r  r  r  r  r  r  r   r  r  r  r  re  r  r  r  r  r  r	  r   r   r   <module>r     sb  HT     $ ! 
	 	 	 	
6$$%	 	   A 1         udH5 		 4 	 # #L^! ^!BT Tn8 8D 8 #""7#5#56AAFFH)))-8CCHHJ D w  3 F " 	, ,d ,.%&4 %&PT $H H 	%
 
%$T (1D 15d 5 @3 @,	4S 	4 	4 '&!$@C @6 8N  6BM$ M2 253 3O &C &R 3M :C :z4 2;(|OD Od
 
	y 	 PH PHf
 YE YE YExC CL*5,zI2X)%X
 
$&!2""2"" :">$%&*&*/T T 	T
 T T T T T T T T T T  T  "!T" $#T$ $%Tr   