
    j5j                    t   d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
mZmZmZ ddlmZ ddlmZ ddlmZmZ dd	lmZmZ dd
lmZmZ ddlmZmZ  ej<                  e      Z ddZ!ddZ"	 	 	 	 	 	 	 	 	 	 	 	 ddZ#e G d d             Z$e G d d             Z% G d d      Z& G d d      Z' G d d      Z(y)zfpdf2-based PDF renderer for OCR text layers.

This module provides PDF rendering using fpdf2 for creating searchable
OCR text layers.
    )annotationsN)	dataclass)atancosdegreesradianssinsqrt)Path)FPDF)PDFResourceTypeTextMode)Matrix	Rectangle)FontManagerMultiFontManager)OcrClass
OcrElementc                V    | D ]$  }t        j                  |      }|dv r y|dk(  s$ y y)zCheck if text is right-to-left based on Unicode bidi properties.

    Looks for the first character with a strong directional type
    (R, AL, or L) to determine the text's base direction.
    )RALTLF)unicodedatabidirectional)textcharbidis      U/var/www/html/qr/venv/lib/python3.12/site-packages/ocrmypdf/fpdf_renderer/renderer.py_is_rtl_textr      s=      ((.;3;     c                p    t        ||||      }| j                  |      }|j                  |j                  fS )zTransform a point (x, y) by a matrix.

    Args:
        matrix: pikepdf Matrix to apply
        x: X coordinate
        y: Y coordinate

    Returns:
        Tuple of (transformed_x, transformed_y)
    )r   	transformllxlly)matrixxyrecttransformeds        r   transform_pointr*   +   s7     Q1a D""4(KOO[__--r    c                    t        ||||      }| j                  |      }|j                  |j                  |j                  |j
                  fS )a-  Transform a bounding box by a matrix.

    Args:
        matrix: pikepdf Matrix to apply
        left: Left edge of box
        top: Top edge of box
        right: Right edge of box
        bottom: Bottom edge of box

    Returns:
        Tuple of (llx, lly, width, height) of the transformed box
    )r   r"   r#   r$   widthheight)r%   lefttoprightbottomr(   r)   s          r   transform_boxr2   <   sN     T3v.D""4(K	 r    c                  D    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)WordRenderDataz1Rendering parameters for a single word on a line.strr   float
x_baselinefont_familyword_tzboolis_rtlN)__name__
__module____qualname____doc____annotations__ r    r   r4   r4   U   s     ;
INLr    r4   c                  <    e Zd ZU dZdZded<   dZded<   dZded<   y)DebugRenderOptionszOptions for debug visualization during rendering.

    When enabled, draws colored lines/shapes to visualize OCR structure.
    Fr:   render_baselinerender_line_bboxrender_word_bboxN)r<   r=   r>   r?   rD   r@   rE   rF   rA   r    r   rC   rC   `   s)    
 "OT!"d""d"r    rC   c                  L    e Zd ZdZddZed	d       Zed	d       Zd
dZddZ	y)CoordinateTransformzManages coordinate transformations for fpdf2 rendering.

    Handles conversion from OCR pixel coordinates (top-left origin) to
    PDF points. fpdf2 uses top-left origin like hOCR, so no Y-flip needed.
    c                .    || _         || _        || _        y)z Initialize coordinate transform.Ndpipage_width_pxpage_height_px)selfrK   rL   rM   s       r   __init__zCoordinateTransform.__init__s   s    *,r    c                :    | j                   dz  | j                  z  S )zPage width in PDF points.      R@)rL   rK   rN   s    r   page_width_ptz!CoordinateTransform.page_width_pty   s     !!D(48833r    c                :    | j                   dz  | j                  z  S )zPage height in PDF points.rQ   )rM   rK   rR   s    r   page_height_ptz"CoordinateTransform.page_height_pt~   s     ""T)DHH44r    c                &    |dz  | j                   z  S )zConvert pixels to PDF points.rQ   )rK   )rN   values     r   px_to_ptzCoordinateTransform.px_to_pt   s    t|dhh&&r    c                    | j                  |j                        | j                  |j                        | j                  |j                        | j                  |j                        fS )z*Convert BoundingBox from pixels to points.)rX   r.   r/   r0   r1   )rN   bboxs     r   
bbox_to_ptzCoordinateTransform.bbox_to_pt   sN     MM$))$MM$((#MM$**%MM$++&	
 	
r    N)rK   r6   rL   r6   rM   r6   )returnr6   )rW   r6   r\   r6   )r\   !tuple[float, float, float, float])
r<   r=   r>   r?   rO   propertyrS   rU   rX   r[   rA   r    r   rH   rH   l   s>    - 4 4 5 5'
r    rH   c                  (   e Zd ZdZ	 	 	 d	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZddZddZ		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z
	 	 	 	 	 	 	 	 	 	 	 	 dd
Z	 d	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZy)Fpdf2PdfRendererzRenders OcrElement trees to PDF using fpdf2.

    This class provides the core rendering logic for converting OCR output
    into PDF text layers using fpdf2's text drawing capabilities.
    Nc                   |j                   t        j                  k7  rt        d      |j                  t        d      || _        || _        || _        || _        || _	        |xs
 t               | _        t        ||j                  j                  |j                  j                        | _        i | _        d| _        y)a>  Initialize renderer.

        Args:
            page: Root OcrElement (must be ocr_page)
            dpi: Source image DPI
            multi_font_manager: MultiFontManager instance
            invisible_text: If True, render text as invisible (text mode 3)
            image: Optional path to image to overlay on top of the text layer,
                creating a sandwich PDF (text underneath, image on top)
            debug_render_options: Options for debug visualization

        Raises:
            ValueError: If page is not an ocr_page or lacks a bounding box
        zRoot element must be ocr_pageNzPage must have bounding boxrJ   F)	ocr_classr   PAGE
ValueErrorrZ   pagerK   multi_font_managerinvisible_textimagerC   debug_optionsrH   r,   r-   coord_transform_registered_fonts _logged_aspect_ratio_suppression)rN   re   rK   rf   rg   rh   debug_render_optionss          r   rO   zFpdf2PdfRenderer.__init__   s    . >>X]]*<==99:;;	"4,
1I5G5I  3))//99++ 
 2405-r    c                   t        d| j                  j                  | j                  j                  f      }|j	                  d       |j                  d       d|_        | j                  rt        j                  |_
        nt        j                  |_
        | j                  |       |j                  t        |             y)z^Render page to PDF file.

        Args:
            output_path: Output PDF file path
        pt)unitformatFautoTr   N)r   rj   rS   rU   set_auto_page_breakset_text_shapingc_marginrg   r   	INVISIBLE	text_modeFILLrender_to_pdfoutputr5   )rN   output_pathpdfs      r   renderzFpdf2PdfRenderer.render   s     $$22$$33
 	U+ 	T"  $..CM$MMCM 	3 	

3{#$r    c                   |j                  | j                  j                  | j                  j                  f       | j                  j
                  D ]  }| j                  ||        | j                  j
                  s-| j                  j                  D ]  }| j                  ||        | j                  R|j                  t        | j                        dd| j                  j                  | j                  j                         yy)zRender page content to an existing FPDF instance.

        This method adds a page and renders all content. Used by both
        single-page rendering and multi-page rendering.

        Args:
            pdf: FPDF instance to render into
        )rq   Nr   )r&   r'   wh)add_pagerj   rS   rU   re   
paragraphs_render_paragraphlines_render_linerh   r5   rN   r}   paralines       r   rz   zFpdf2PdfRenderer.render_to_pdf   s     	$$22$$33 	 	
 II(( 	.D""3-	. yy##		 -!!#t,- ::!IIDJJ&&44&&55   "r    c                    t        |j                        }|| j                  vr8|j                  j                  }|j	                  ||       || j                  |<   | j                  |   S )zRegister font with fpdf2 if not already registered.

        Args:
            pdf: FPDF instance
            font_manager: FontManager containing the font

        Returns:
            Font family name to use with pdf.set_font()
        )familyfname)r5   	font_pathrk   stemadd_font)rN   r}   font_managerfont_path_strfamily_names        r   _register_fontzFpdf2PdfRenderer._register_font  sd     L223 6 66&0055KLL=LA4?D""=1%%m44r    c                    |j                   D ]1  }|j                  t        j                  v s | j	                  ||       3 y)zzRender a paragraph element.

        Args:
            pdf: FPDF instance
            para: Paragraph OCR element
        N)childrenrb   r   
LINE_TYPESr   r   s       r   r   z"Fpdf2PdfRenderer._render_paragraph"  s9     MM 	-D~~!4!44!!#t,	-r    c           
     L   |j                   y|j                   j                  dk  r?t        j                  d|j                   t	        |d      r|j
                         yd       y| j                  j                  |j                   j                        }| j                  j                  |j                   j                        }| j                  j                  |j                   j                        }| j                  j                  |j                   j                        }| j                  j                  r| j                  |||||       |j                  xs d}d}d}	d}
|j                   W|j                   j"                  }| j                  j                  |j                   j$                        }	t'        |      dk  rd}d	}
|dk(  r&t'        |      d
kD  rt)        t+        |            }d}d}
t-               j/                  ||      j1                  |       }|j3                         }t5        |||||      \  }}}}|
sU| j6                  j8                  d   }|j;                         \  }}}||z  }||z  }t'        |       |z  |t'        |      z   z  }	|dk7  rt)        t+        |            nd}|j/                  d|      j/                  d|	      j1                  |      }||	z   }|d
k  r|dz  }| |z   }| j                  j<                  r^t?        |dd      }t?        ||d      }|jA                  ddd       |jC                  d       |jE                  |d   |d   |d   |d          |jF                  }|j3                         }|jH                  D cg c]/  }|jJ                  tL        jN                  k(  s!|j
                  s.|1 }}| jQ                  |||||||      syg } |D ]  }!|!|!j
                  r|!j                   | j                  j                  |!j                   j                        }"| j                  j                  |!j                   j                        }#| j                  j                  |!j                   j                        }$| j                  j                  |!j                   j                        }%|$|"z
  }&| j                  jR                  r| jU                  ||"|#|$|%       t5        ||"|#|$|%      \  }'}}}| j6                  jW                  |!j
                  |      }(| jY                  ||(      })|j[                  |)|       | j\                  xr t_        |!j
                        }*|*r6|j`                  }+d|_0        |jc                  |!j
                        },|+|_0        n|jc                  |!j
                        },|,dkD  r|&dkD  r	|&|,z  dz  }-nd}-| je                  tg        |!j
                  |'|)|-|*               | sy| ji                  || |||       yc c}w )a'  Render a line element with baseline support.

        Strategy (following pikepdf reference implementation):
        1. Create a baseline_matrix that transforms from hOCR coordinates to
           a coordinate system aligned with the text baseline
        2. For each word, transform its hOCR bbox using baseline_matrix.inverse()
           to get its position in the baseline coordinate system
        3. Render words along the baseline with horizontal scaling

        Args:
            pdf: FPDF instance
            line: Line OCR element
        Nr   z:line box is invalid so we cannot render it: box=%s text=%sr            Fg{Gzt?T      ?zNotoSans-Regularg?         ?   sized   g      Y@)r   r7   r8   r9   r;   )5rZ   r-   logerrorhasattrr   rj   rX   r.   r/   r0   r1   ri   rE   _render_debug_line_bbox	textanglebaselineslope	interceptabsr   r   r   
translatedrotatedinverser2   rf   fontsget_font_metricsrD   r*   set_draw_colorset_line_widthr   languager   rb   r   WORD_check_aspect_ratio_plausiblerF   _render_debug_word_bboxselect_font_for_wordr   set_fontrg   r   text_shapingget_string_widthappendr4   _emit_line_bt_block).rN   r}   r   line_left_ptline_top_ptline_right_ptline_bottom_ptr   r   intercept_pthas_meaningful_baselineline_size_aabb_matrixinv_line_matrix_line_size_widthline_size_heightdefault_font_managerascentdescentunits_per_emascent_normdescent_normslope_angle_degbaseline_matrix	font_sizetotal_rotation_degbaseline_startbaseline_endline_languageinv_baseline_matrixr   wordsword_render_datawordword_left_ptword_top_ptword_right_ptword_bottom_ptword_width_ptbox_llxr   r8   word_is_rtlsaved_shapingnatural_widthr9   s.                                                 r   r   zFpdf2PdfRenderer._render_line-  s    99 99q IIL		$T62		
  9;
  ++44TYY^^D**33DIIMMB,,55diiooF--66tyy7G7GH
 ..((\;~
 NN)c	
 "'==$MM''E//889P9PQL5zE!&*#
 E
S 0U,I E&+# HZk2WiZ  	 07792?\;~3
/1o/
 ' $(#:#:#@#@AS#T ,@,Q,Q,S)FG\ </K"\1L \"""#\!224  383,'$u+.C ",,# Z<(W_% 	 %|3	s?(3.I (Z/9 --,_aCN*?OQOLsAs+t$HHq!>!#4l1o|TU
  .557 }}*
x}}(DA*
 *
 11	?-}
 13 ;	D|499		0A//88HL..77		FK 0099$))//JM!11::499;K;KLN)L8M !!22,,{M>
  -# GQ1  22GG		=L --c<@KLL9L5 --I,tyy2IK # 0 0#'  # 4 4TYY ?#0  # 4 4TYY ?q ]Q%6(=8C?##NYY"'"% k;	z   	  	
k*
s   "X!8X!X!c           	        |dk  s
|dk  s|dk  ry||k\  rydj                  d |D              }|sy| j                  j                  ||      }	| j                  ||	      }
|j	                  |
t        |             |j                  |      }|dk  ryt        t        |            }|t        |      z  |t        |      z  z   }|t        |      z  |t        |      z  z   }|dk  ry||z  }||z  }||z  }|dk\  ryt        j                  d||||dd	        | j                  st        j                  d
       d| _        y)a  Check whether the line's aspect ratio is plausible for its text.

        Compares the aspect ratio of the OCR bounding box to the aspect ratio
        the text would have if rendered normally (accounting for baseline
        slope). A large mismatch indicates Tesseract misread rotated text
        without detecting the rotation.

        Returns:
            True if plausible (rendering should proceed), False to suppress.
        r   T c              3  T   K   | ]   }||j                   s|j                    " y w)N)r   ).0r   s     r   	<genexpr>zAFpdf2PdfRenderer._check_aspect_ratio_plausible.<locals>.<genexpr>4  s#      
Q]qvvAFF
s   (((r   皙?z[Suppressing text with improbable aspect ratio: actual=%.3f expected=%.3f ratio=%.4f text=%rNP   z8Suppressing OCR output text with improbable aspect ratioF)joinrf   r   r   r   roundr   r   r   r   r	   r   debugrl   info)rN   r}   r   r   r   r   r   r   	line_textr   r8   r   theta
expected_w
expected_hactual_aspectexpected_aspectratios                     r   r   z.Fpdf2PdfRenderer._check_aspect_ratio_plausible  sw   ( a#3q#8IN
 ..HH 
!
 
	  ..CC}
 ))#|<[uY'78,,Y7A O,-"SZ/)c%j2HH
"SZ/)c%j2HH
?'*::$z1/C< 			;crN	
 44HHJ 59D1r    c                   | j                   j                  }t        |      dkD  }t        |dd      \  }}	||	z
  }
g }|rt        |dd      \  }}||z
  }||z
  }||
z
  }t	        ||z  ||z  z         }|dkD  r||z  }||z  }nd}d}|j                  d       |j                  |dd|dd| dd|dd|d	d|
d	d
       |j                  d       | j                  rdnd}|j                  | d       |d   j                  }|r|j                  |d	d       n.t        ||d      \  }}||z
  }|j                  |d	d|d	d       d}|}t        |      D ]'  \  }}|t        |      dz
  k(  }|j                  |k7  r|j                  |j                  |       |j                  j                  t        j                  |j                   j"                  |j$                         |j                  d|j                   j"                   d|j&                  d	d       |j                  }|dkD  ru|r%|j                  |z
  }|j                  |d	d       nNt        ||d      \  }} t        ||j                  d      \  }!}"|!|z
  }#|"| z
   }$|j                  |#d	d|$d	d       |sy||dz      }%|%j                  |j                  z
  }&|&dkD  rF| j)                  |j*                        r| j)                  |%j*                        s|j*                  dz   }'n|j*                  }'n|j*                  }'|j                  |j,                  d	d       |j                  | j/                  ||'|j0                               |j                  }* |j                  d       |r|j                  d       |j3                  dj5                  |             d|_        y)aU  Emit a single BT block for the entire line using raw PDF operators.

        Writes all words in a single BT..ET block with relative Td positioning
        and per-word Tz. Each non-last word gets a trailing space appended, with
        Tz calculated so the rendered width of "word " spans from the current
        word's start to the next word's start. This works around a poppler bug
        where Tz is not carried across BT/ET boundaries, which affects all
        poppler-based viewers and tools (Evince, pdftotext, etc.).

        Args:
            pdf: FPDF instance
            word_render_data: List of WordRenderData, one per word on this line
            baseline_matrix: Transform from baseline coords to page coords
            font_size: Font size in points
            total_rotation_deg: Total rotation angle (textangle + slope)
        g{Gz?r   r   r   r   qz.6fr   z.2fz cmBT   z Trz 0 Tdz TdNr   r   z/Fz Tfz TzETQ
)rj   rU   r   r*   r
   r   rg   r7   	enumeratelenr8   r   _resource_catalogaddr   FONTcurrent_fontire   font_size_pt_is_cjk_onlyr   r9   _encode_shaped_textr;   _outr   font_stretching)(rN   r}   r   r   r   r   page_heighthas_rotationbx0by0_fpdfby0_pdfopsbx1by1_fpdfby1_pdfdxdylengthcos_asin_atrfirst_x_baselinepage_xpage_y_fpdf
page_y_pdfprev_font_familyprev_x_baseliner   r   is_lastdx_baselinepx_prev	py_prev_fpx_curr	py_curr_fdx_pdfdy_pdf	next_wordadvancetext_to_renders(                                           r   r   z$Fpdf2PdfRenderer._emit_line_bt_blockd  s   0 **99 -.5'A>X(+OS!DMC!H,GsB7"B"r'BG+,FzVV
 JJsOJJ+QuSkE6#,ac{!s)1WSM. 	

4 %%Q1

bT: ,A.99JJ*3/u56 #2!11#FK %{2JJJ&Qz#&6c:;'+* !12 <	.GAt3/0144G #33T--I>%%))#((#*:*:*<*<chh 

))++,Ac.>.>s-C3G $(#3#3  1u"&//O"CKJJ+c!2%89 *9'!*&GY *9'!*&GY %w.F(945FJJ&Qvcl#>? ,QU3	#..@ aK))$))4 --inn= &*YY_N%)YYN!% JJ$,,s+3/0JJ((ndkkJ #ooOy<	.~ 	

4JJsO3  "r    c                l   |j                   }|r|j                  |ddd         S |j                  rs|j                  j                  d      rX|j	                  ||j
                  |j                        }|r/dj                  d |D              }|rd|j                  |       dS |j                  |      S )u  Encode text using HarfBuzz text shaping for complex script support.

        Unlike font.encode_text() which maps unicode characters one-by-one to
        glyph IDs, this uses HarfBuzz to handle BiDi reordering, Arabic joining
        forms, Devanagari conjuncts, and other complex script shaping. Falls
        back to encode_text() when text shaping is not enabled.

        For RTL words with invisible text, we use encode_text() instead of
        shape_text(). fpdf2's shape_text() produces RTL ligature glyphs
        (e.g. lam-alef) with multi-character CMap entries whose character
        order gets reversed by the bidi algorithm during text extraction,
        producing garbled output (e.g. "سالح" instead of "سلاح").
        encode_text() maps characters 1:1 in logical order, giving correct
        extraction. Since the text is invisible (Tr=3), the lack of proper
        joining forms and ligature shaping is harmless.
        Nuse_shaping_enginer   c              3  B   K   | ]  }|d    t        |d            yw)mapped_charN)chr)r   tis     r   r   z7Fpdf2PdfRenderer._encode_shaped_text.<locals>.<genexpr>  s-      !-(4 =)*!s   (z) Tj)r   encode_textr   get
shape_textr   r   escape_text)rN   r}   r   r;   fontshapedmappeds          r   r  z$Fpdf2PdfRenderer._encode_shaped_text  s    &  ##D2J// 0 0 4 45I J__T3+;+;S=M=MNF !$! 
 t//78==%%r    c                (   |D ]  }t        |      }d|cxk  rdk  rn d|cxk  rdk  r)n d|cxk  rdk  r7n d|cxk  rdk  rEn d	|cxk  rd
k  rSn d|cxk  rdk  ran d|cxk  rdk  ron d|cxk  rdk  r}n d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rdk  rn d|cxk  rd k  rn d!|cxk  rd"k  rn d#|cxk  rd$k  r
 y%  y% y&)'a#  Check if text contains only CJK characters.

        CJK scripts don't use spaces between words, so we should not insert
        spaces between adjacent CJK words.

        Args:
            text: Text to check

        Returns:
            True if text contains only CJK characters
        i N  i  i 4  iM  i   iߦ i  i? i@ i i  i i i i   iO i@0  i0  i0  i0  i1  i1  i   i  i   i  i01  i1  i`  i  i  i  i 0  i?0  i   i  FT)ord)rN   r   r   cps       r   r  zFpdf2PdfRenderer._is_cjk_only&  s     	DTB "&&R)6)b+G+b+G+b+G+b+G+b+G+b+G+R)6)R)6)R)6)R)6)R)6)R)6)R)6)R)6)R)6)R)6) */	0 r    c                    |j                  ddd       |j                  d       |j                  ||||z
  ||z
         y)z%Draw a blue box around the line bbox.r   r   g      ?Nr   r   r(   rN   r}   r.   r/   r0   r1   s         r   r   z(Fpdf2PdfRenderer._render_debug_line_bboxL  s>     	1a%3sEDL&3,7r    c                   |j                  ddd       |j                  d       t        |      dkD  r5|j                  |||      5  |j	                  ||||z   |       ddd       y|j	                  ||||z   |       y# 1 sw Y   yxY w)z'Draw a magenta line along the baseline.r   r   r   r   )r&   r'   N)r   r   r   rotationr   )rN   r}   r&   r'   r,   rotation_degs         r   _render_debug_baselinez'Fpdf2PdfRenderer._render_debug_baselineY  s     	33'4 |s"la15 -Aq5y!,- - HHQ1u9a(- -s   B  B	c                    |j                  ddd       |j                  d       |j                  ||||z
  ||z
         y)z&Draw a green box around the word bbox.r   r   g333333?Nr8  r9  s         r   r   z(Fpdf2PdfRenderer._render_debug_word_bboxk  s>     	1c1%3sEDL&3,7r    )TNN)re   r   rK   r6   rf   r   rg   r:   rh   zPath | Nonerm   DebugRenderOptions | Noner|   r   r\   None)r}   r   r\   rA  )r}   r   r   r   r\   r5   )r}   r   r   r   r\   rA  )r}   r   r   r   r\   rA  )r}   r   r   zlist[OcrElement | None]r   r6   r   r6   r   r6   r   r6   r   z
str | Noner\   r:   )r}   r   r   zlist[WordRenderData]r   r   r   r6   r   r6   r\   rA  )F)r}   r   r   r5   r;   r:   r\   r5   r   r5   r\   r:   )r}   r   r.   r6   r/   r6   r0   r6   r1   r6   r\   rA  )r}   r   r&   r6   r'   r6   r,   r6   r<  r6   r\   rA  )r<   r=   r>   r?   rO   r~   rz   r   r   r   r   r   r  r  r   r=  r   rA   r    r   r`   r`      s     $!:>-6-6 -6 -	-6
 -6 -6 8-6^!%F"H5(	-h
TKK 'K 	K
 K K  K "K 
KZW"W" /W"  	W"
 W" "W" 
W"t 49'&'&"'&,0'&	'&R$L88 8 	8
 8 8 
8)) ) 	)
 ) ) 
)$88 8 	8
 8 8 
8r    r`   c                  4    e Zd ZdZ	 	 d	 	 	 	 	 	 	 ddZddZy)Fpdf2MultiPageRendererzRenders multiple OcrElement pages into a single PDF.

    This class handles multi-page documents by delegating to Fpdf2PdfRenderer
    for each page while sharing a single FPDF instance and font registration.
    Nc                T    || _         || _        || _        |xs
 t               | _        y)a=  Initialize multi-page renderer.

        Args:
            pages_data: List of (pageno, ocr_tree, dpi) tuples
            multi_font_manager: Shared multi-font manager for all pages
            invisible_text: Whether to render invisible text
            debug_render_options: Options for debug visualization
        N)
pages_datarf   rg   rC   ri   )rN   rF  rf   rg   rm   s        r   rO   zFpdf2MultiPageRenderer.__init__  s,     %"4,1I5G5Ir    c                   | j                   st        d      t        d      }|j                  d       |j	                  d       d|_        | j                  rt        j                  |_	        nt        j                  |_	        i }| j                   D ]Y  \  }}}|j                  t        ||| j                  | j                  | j                  	      }||_        |j!                  |       [ |j#                  t%        |             y)
zrRender all pages to a single multi-page PDF.

        Args:
            output_path: Output PDF file path
        zNo pages to renderro   )rp   Frr   Tr   N)re   rK   rf   rg   rm   )rF  rd   r   rt   ru   rv   rg   r   rw   rx   ry   rZ   r`   rf   ri   rk   rz   r{   r5   )rN   r|   r}   shared_registered_fonts_pagenore   rK   page_renderers           r   r~   zFpdf2MultiPageRenderer.render  s     122 oU+T"  $..CM$MMCM 35 #'// 	-GT3yy  -#'#:#:#22%)%7%7M /FM+ '',#	-( 	

3{#$r    )TN)rF  z#list[tuple[int, OcrElement, float]]rf   r   rg   r:   rm   r?  r@  )r<   r=   r>   r?   rO   r~   rA   r    r   rD  rD  y  sD      $:>J7J -J 	J
 8J(0%r    rD  rB  )r%   r   r&   r6   r'   r6   r\   ztuple[float, float])r%   r   r.   r6   r/   r6   r0   r6   r1   r6   r\   r]   ))r?   
__future__r   loggingr   dataclassesr   mathr   r   r   r   r	   r
   pathlibr   fpdfr   
fpdf.enumsr   r   pikepdfr   r   ocrmypdf.fontr   r   ocrmypdf.models.ocr_elementr   r   	getLoggerr<   r   r   r*   r2   r4   rC   rH   r`   rD  rA   r    r   <module>rV     s    #   ! 7 7   0 % 7 <g!."&+49CH&2    # # #"
 "
Je8 e8PK% K%r    