ó
    /j-j,G  ã                   óv   • S r SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSK	J
r
  S	rS
rSrSr " S S\5      rg)z&Implementation of hybrid table parser.é   )Úbboxes_overlap)Úboundaries_to_split_lines)Útext_in_bboxé   )Ú
BaseParser)ÚLattice)ÚNetworkg      à?gš™™™™™á?g      ø?é   c                   óš   ^ • \ rS rSrSr            SU 4S jjrU 4S jrS rS rS\	4S jr
\SS	 j5       rS
 rS rS rS rSrU =r$ )ÚHybridé"   u‘  Defines a hybrid parser, leveraging both network and lattice parsers.

Parameters
----------
table_regions : list, optional (default: None)
    List of page regions that may contain tables of the form x1,y1,x2,y2
    where (x1, y1) -> left-top and (x2, y2) -> right-bottom
    in PDF coordinate space.
table_areas : list, optional (default: None)
    List of table area strings of the form x1,y1,x2,y2
    where (x1, y1) -> left-top and (x2, y2) -> right-bottom
    in PDF coordinate space.
columns : list, optional (default: None)
    List of column x-coordinates strings where the coordinates
    are comma-separated.
split_text : bool, optional (default: False)
    Split text that spans across multiple cells.
flag_size : bool, optional (default: False)
    Flag text based on font size. Useful to detect
    super/subscripts. Adds <s></s> around flagged text.
strip_text : str or sequence of str, optional (default: '')
    Characters or substrings to strip from each cell. A ``str``
    strips per-character; a list/tuple of ``str`` strips whole
    substrings (#484).
edge_tol : int, optional (default: 50)
    Tolerance parameter for extending textedges vertically.
row_tol : int, optional (default: 2)
    Tolerance parameter used to combine text vertically,
    to generate rows.
column_tol : int, optional (default: 0)
    Tolerance parameter used to combine text horizontally,
    to generate columns.
engine : str, optional (default: 'combined')
    Line-detection engine for hybrid's **lattice half** (the network
    half is text-based and unaffected):

    - ``'combined'`` (default): OpenCV on the rendered page **plus** the
      PDF's native vector ruled lines unioned in â€” recovers
      faintly-rendered rules. Matches the ``flavor='lattice'`` default.
    - ``'raster'``: detect ruled lines with OpenCV only (pre-#763).
    - ``'vector'``: detect ruled lines **straight from the PDF's vector
      graphics, skipping rasterisation and OpenCV entirely** â€” the
      render-free hybrid (network text-edge alignment merged with vector
      ruled lines) for partial-ruled / borderless tables at roughly an
      order of magnitude less time than the raster path. (#39)

c                 óš   >• [         TU ]  SUUUUUUUS9  X0l        [        UUUUUUUUU	U
US9U l        [        UUUUUUUU	U
UUS9U l        g )NÚhybrid)Útable_regionsÚtable_areasÚ	flag_sizeÚ
split_textÚ
strip_textÚreplace_textÚdebug)r   r   Úcolumnsr   r   r   r   Úedge_tolÚrow_tolÚ
column_tolr   )r   r   r   r   r   r   r   r   r   r   Úengine)ÚsuperÚ__init__r   r	   Únetwork_parserr   Úlattice_parser)Úselfr   r   r   r   r   r   r   r   r   r   r   r   ÚkwargsÚ	__class__s                 €ÚS/home/mande/repo/quber/.venv/lib/python3.13/site-packages/camelot/parsers/hybrid.pyr   ÚHybrid.__init__S   s–   ø€ ô  	‰ÑØØ'Ø#ØØ!Ø!Ø%Øð 	ñ 		
ð ŒÜ%Ø'Ø#ØØØ!Ø!Ø%ØØØ!Øñ
ˆÔô &Ø'Ø#ØØ!Ø!Ø%ØØØ!Øð ñ
ˆÕó    c
                 óÀ   >• [         T
U ]  UUUUUUUUU	5	        U R                  R                  UUUUUUUUU	5	        U R                  R                  UUUUUUUUU	5	        g)zÿCall this method to prepare the page parsing .

Parameters
----------
filename : [type]
    [description]
layout : [type]
    [description]
dimensions : [type]
    [description]
page_idx : [type]
    [description]
layout_kwargs : [type]
    [description]
N)r   Úprepare_page_parser   r   )r    ÚfilenameÚlayoutÚ
dimensionsÚpage_idxÚimagesÚhorizontal_textÚvertical_textÚrotationÚlayout_kwargsr"   s             €r#   r'   ÚHybrid.prepare_page_parseŒ   s�   ø€ ô6 	‰Ñ"ØØØØØØØØØô
	
ð 	×Ñ×.Ñ.ØØØØØØØØØô
	
ð 	×Ñ×.Ñ.ØØØØØØØØØõ
	
r%   c                 óB   • U R                   U   nUR                  X5      $ )N)Útable_bbox_parsesÚ_generate_columns_and_rows)r    ÚbboxÚ	table_idxÚparsers       r#   r4   Ú!Hybrid._generate_columns_and_rowsÉ   s#   € Ø×'Ñ'¨Ñ-ˆØ×0Ñ0°ÓAÐAr%   c                 ón  • U R                   U   nUR                  " XX440 UD6nUR                  R                  UR                  S:H  R	                  SS9)    Ul        UR                  R                  S S 2UR                  S:H  R	                  SS9) 4   Ul        UR                  R
                  Ul        U$ )NÚ r   )Úaxisé    )r3   Ú_generate_tableÚdfÚlocÚallÚshape)r    r6   r5   ÚcolsÚrowsr!   r7   Útables           r#   r=   ÚHybrid._generate_tableÍ   sž   € Ø×'Ñ'¨Ñ-ˆØ×&Ò& y¸ÑMÀfÑMˆð —8‘8—<‘< %§(¡(¨b¡.×!5Ñ!5¸1Ð!5Ð!=Ð =Ñ>ˆŒà—8‘8—<‘<¢ U§X¡X°¡^×$8Ñ$8¸aÐ$8Ð$@Ð#@Ð @ÑAˆŒØ—h‘h—n‘nˆŒØˆr%   Úreturnc                 ó’   • UR                   R                  =(       d+    UR                  S   S:H  =(       d    UR                  S   S:H  $ )u#  Drop tables left empty after the empty-row/col purge.

The render-free ``engine='vector'`` half reads ruled lines straight
from the PDF's vector graphics, which include decorative page borders
and form rules. Those can raise a "grid" with no text inside; once
:meth:`_generate_table` strips its all-empty rows and columns nothing
is left, and an empty table would otherwise leak out as a spurious
detection. (The rendered raster/combined halves don't hit this â€” the
OpenCV pipeline doesn't pick those rules up â€” so their output is
unchanged.)
r<   r   )r>   ÚemptyrA   )r    rD   s     r#   Ú_reject_tableÚHybrid._reject_tableÙ   s6   € ð �x‰x�~‰~×K §¡¨Q¡°1Ñ!4×K¸¿¹ÀA¹È!Ñ8KÐKr%   c                 ó|  • [        U 5      S-
  n[        U5      S-
  nSn US:  a   U $ X   nUS:  a!  X`S   S   /nU R                  SU5        US-
  nOhX   nUS   Xb-   :  a  XhS'   Ub  XeS'   US-
  nOHUS   Xb-
  :”  a  US-
  nUnUS:  a	  XhS'   US-
  nO&XhS   /nU R                  US-   U5        XhS'   UnUS-
  nMž  )zÕAugment existing boundaries using provided hard splits.

Boundaries:   |---|    |-| |---------|  #noqa RST305
Splits:     |       |     |       |  #noqa RST305
Augmented:  |-------|-----|-------|--|  #noqa RST305
r   Nr<   )ÚlenÚinsert)	Ú
boundariesÚsplitsÚ	toleranceÚidx_boundariesÚ
idx_splitsÚprevious_boundaryÚsplitÚnew_boundaryÚboundarys	            r#   Ú_augment_boundaries_with_splitsÚ&Hybrid._augment_boundaries_with_splitsç   s*  € ô ˜Z›¨1Ñ,ˆÜ˜“[ 1‘_ˆ
Ø ÐØØ˜A‹~àðF ÐðE Ñ&ˆEà Ó!à %°!¡}°QÑ'7Ð8�Ø×!Ñ! ! \Ô2Ø'¨!™^‘
à%Ñ5�Ø˜A‘; Ñ!2Ó2ð #(˜Q‘Kð )Ñ4Ø/4¨!Ñ,Ø!+¨a¡‘JØ˜a‘[ 5Ñ#4Ó4à%3°aÑ%7�NØ(0Ð%Ø%¨Ó)ð ', ™Ø%/°!¡^˜
øð %*°A©;Ð#7�LØ×%Ñ% n°qÑ&8¸,ÔGØ"'˜Q‘KØ(4Ð%Ø!+¨a¡�JñK r%   c                 óÒ  ^• [        XR                  U R                  -   5       Vs/ s H*  nUR                  5       R	                  5       (       d  M(  UPM,     nnU(       d  gU4S jnU R
                  R                  X@R
                  R                  S9nSnU HA  nU Vs1 s H
  o5" U5      iM     n	nU	R                  S5        [        U	5      S:¼  d  M<  US-  nMC     U$ s  snf s  snf )uí  Count text rows inside ``lattice_bbox`` that populate >=2 columns.

Used to tell a complete ruled grid from a *partially*-ruled fragment.
A horizontal rule lattice missed leaves text in **several** columns at
the same y; a multi-line cell only adds extra text in **one** column.
So clustering the bbox's textlines by y and counting the clusters that
span at least two of lattice's columns approximates the table's true
row count â€” robust to multi-line cells (which inflate a naive y-count).
r<   c                 ó¶   >• U R                   U R                  -   S-  n[        [        T5      S-
  5       H!  nTU   Us=::  a  TUS-      ::  d  M   Us  $   M#     g )Ng       @r   )Úx0Úx1ÚrangerL   )ÚtextlineÚxcÚiÚcol_anchorss      €r#   Ú	column_ofÚ4Hybrid._count_column_aligned_rows.<locals>.column_of.  s\   ø€ Ø—+‘+ §¡Ñ+¨sÑ2ˆBÜœ3˜{Ó+¨aÑ/Ö0�Ø˜q‘> RÕ=¨;°q¸1±uÑ+=Ö=Ø’Hò >ñ 1ð r%   )r   Nr   r   )
r   r-   r.   Úget_textÚstripr   Ú_group_rowsr   ÚdiscardrL   )
r    Úlattice_bboxra   ÚtÚtlsrb   rC   ÚalignedÚrowrB   s
     `       r#   Ú_count_column_aligned_rowsÚ!Hybrid._count_column_aligned_rows  sâ   ø€ ô "Ø×2Ñ2°T×5GÑ5GÑGôó
ò�ð �z‰z‹|×!Ñ!×#÷	 ñð 	ð 
ö Øõ	ð ×"Ñ"×.Ñ.¨s×<OÑ<O×<WÑ<WÐ.ÐXˆØˆÛˆCÙ*-Ó.ª# Q�I˜a–L©#ˆDÐ.Ø�L‰L˜ÔÜ�4‹y˜A�~Ø˜1‘’ñ	 ð
 ˆùò1
ùò( /s   ¦'CÁCÂC$c                 ó  • U R                   R                  U   nUS   nUS   n[        U5      S::  d  [        U5      S::  a  gUR                  S/ 5      nU Vs1 s H!  n[	        US   S5      [	        US   S5      4iM#     nn[        U5      [        U5      -  nU(       a  [        U5      U-  OSn	U	[
        :  a  g[        U5      S-
  n
U R                  X5      nU[        :¼  a  [        U-  U
s=::  a  [        U-  ::  d   g  gg	s  snf )
uÁ  Whether lattice already resolved a full ruled grid for this bbox.

The combine ``_augment_boundaries_with_splits`` *unions* network's
text-derived column splits onto lattice's. On a partial / borderless
table that recovers columns lattice couldn't see â€” the niche hybrid
is for. But on an **already-complete** ruled grid the union only adds
spurious splits and, because the merged bbox is then parsed by the
*network* parser (text-grouped rows), it also throws away lattice's
exact ruled rows â€” the over-split that sinks fully-ruled docs (#38).

So gate the augmentation. A grid counts as complete only when:

1. lattice found genuine ruled lines in **both** directions (interior
   column *and* row anchors, not just the two bbox edges);
2. its joints actually cover that grid (a real lattice of crossings,
   not a couple of stray rules); and
3. lattice's row lines account for the table's text rows â€” i.e. it is
   not a *partially*-ruled fragment whose unruled rows lattice would
   silently drop (the us-008 / us-033 failure mode). Network handles
   those better, so they stay on the augmented path.

Complete grids are routed to the lattice parser as-is; incomplete ones
keep the network-augmented path.
ra   Úrow_anchorsr   FÚjoints_normalizedr<   r   g        T)
r   r3   rL   ÚgetÚroundÚ_LATTICE_GRID_COVERAGErm   Ú_MIN_ALIGNED_ROWSÚ_LATTICE_ROW_MATCHÚ_LATTICE_ROW_CEIL)r    rh   Úparsera   rp   rq   ÚjÚunique_jointsÚgrid_pointsÚcoverageÚlattice_rowsÚaligned_rowss               r#   Ú_lattice_grid_is_completeÚ Hybrid._lattice_grid_is_complete>  s  € ð2 ×#Ñ#×5Ñ5°lÑCˆØ˜MÑ*ˆØ˜MÑ*ˆô ˆ{Ó˜qÓ ¤C¨Ó$4¸Ó$9ØØ!ŸI™IÐ&9¸2Ó>ÐÙCTÓUÒCT¸aœ%  !¡ a›.¬%°°!±°a«.Ó9ÑCTˆÐUÜ˜+Ó&¬¨[Ó)9Ñ9ˆÞ7B”3�}Ó%¨Ò3ÈˆØÔ,Ó,Øô ˜;Ó'¨!Ñ+ˆØ×6Ñ6°|ÓQˆØÔ,Ó,Ü Ñ-Øõ0ä  <Ñ/ô0ð ð	0ð Øùò! Vs   Á(Dc                 ór  • U R                   R                  U   nUS   nU R                  R                  U   nUS   nUc  U R                   U R                  U'   gU R                  U5      (       a  U R                   U R                  U'   gU R	                  XdU R                   R
                  5      nUS   S   [        US   US   5      US   S   [        US   US   5      4n[        U5      US'   U R                  R                  U	 XPR                  R                  U'   U R                  U R                  U'   g)	zAIdentify splits that were only detected by lattice or by network.ra   Úcols_boundariesNr<   r   éÿÿÿÿr
   Úcols_anchors)	r   r3   r   r   rW   Ú	joint_tolÚminÚmaxr   )r    rh   Únetwork_bboxÚlattice_parseÚlattice_colsÚnetwork_bbox_dataÚnetwork_cols_boundariesÚaugmented_bboxs           r#   Ú_merge_bbox_analysisÚHybrid._merge_bbox_analysisq  sK  € à×+Ñ+×=Ñ=¸lÑKˆØ$ ]Ñ3ˆà ×/Ñ/×AÑAÀ,ÑOÐØ"3Ð4EÑ"FÐð #Ñ*Ø37×3FÑ3FˆD×"Ñ" <Ò0Ø×+Ñ+¨L×9Ñ9ð 48×3FÑ3FˆD×"Ñ" <Ò0à&*×&JÑ&JØ'°t×7JÑ7J×7TÑ7Tó'Ð#ð (¨Ñ*¨1Ñ-Ü�L ‘O \°!¡_Ó5Ø'¨Ñ+¨AÑ.Ü�L ‘O \°!¡_Ó5ð	ˆNô 1JØ'ó1Ð˜nÑ-ð ×#Ñ#×5Ñ5°lÐCØDU×Ñ×1Ñ1°.ÑAØ59×5HÑ5HˆD×"Ñ" >Ò2r%   c                 ó*  • 0 U l         U R                  R                  5         [        U R                  R                   S S9nU R                  R                  5         [        U R                  R                   S S9nU Hr  nSn[        [        U5      S-
  SS5       H.  nX%   n[        X65      (       d  M  U R                  X65        X%	 SnM0     U(       a  MY  U R                  U R                   U'   Mt     U H  nU R                  U R                   U'   M     g )Nc                 ó   • U S   U S   * 4$ ©Nr<   r   © ©r5   s    r#   Ú<lambda>Ú-Hybrid._generate_table_bbox.<locals>.<lambda>˜  ó   € ÀTÈ!ÁWÈtÐTUÉwÈhÑDWr%   )Úkeyc                 ó   • U S   U S   * 4$ r’   r“   r”   s    r#   r•   r–   œ  r—   r%   Fr   rƒ   T)	r3   r   Ú_generate_table_bboxÚsortedr   r]   rL   r   rŽ   )r    Ú_lattice_bboxesÚ_network_bboxesrh   ÚmergedÚidxrˆ   s          r#   rš   ÚHybrid._generate_table_bbox“  s  € Ø!#ˆÔà×Ñ×0Ñ0Ô2Ü Ø×Ñ×1Ñ1Ñ7Wñ
ˆð 	×Ñ×0Ñ0Ô2Ü Ø×Ñ×1Ñ1Ñ7Wñ
ˆó
 ,ˆLØˆFäœS Ó1°AÑ5°r¸2Ö>�Ø.Ñ3�Ü% l×AÑAÙØ×)Ñ)¨,ÔEà#Ð(Ø’ñ ?÷ �6Ø7;×7JÑ7J�×&Ñ& |Ó4ñ ,ó ,ˆLØ37×3FÑ3FˆD×"Ñ" <Ó0ò ,r%   )r   r   r   r3   )NNNFFr:   NNr   r<   FÚcombined)r<   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   r'   r4   r=   ÚboolrI   ÚstaticmethodrW   rm   r   rŽ   rš   Ú__static_attributes__Ú__classcell__)r"   s   @r#   r   r   "   sˆ   ø† ñ.ðd ØØØØØØØØØØØ÷7
õr;
òzBò
ðL dô Lð ó0ó ð0òd"òH1òf I÷DGð Gr%   r   N)r¦   Úutilsr   r   r   Úbaser   Úlatticer   Únetworkr	   rt   rv   rw   ru   r   r“   r%   r#   Ú<module>r¯      sH   ðÙ ,å "Ý -Ý  Ý Ý Ý ð Ð ð Ð ØÐ ð Ð ôNGˆZõ NGr%   