ó
    �®žjK" ã                  ó>  • % S SK Jr  S SKrS SKrS SKrS SKrS SK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JrJrJrJr  S S	KJr  S SKrS S
KJrJrJrJrJrJrJ r J!r!J"r"J#r#J$r$J%r%J&r&J'r'J(r(J)r)J*r*J+r+J,r,  S SK-J.r.  S SK/J0r1J2r2  S SK3J4r4J5r5J6r6J7r7  S SK8J9r9  S SK:J;r;  S SK<J=r=J>r>  S SK?J@r@JArAJBrB  S SKCJDrD  S SKEJFrFJGrG  S SKHJIrI  S SKJJKrK  \R˜                  " \M5      rNSrOS\PS'   SrQS\PS'    S SKRJSrSJTrT  S SKUJVrVJWrW  S SKXJYrY  S SKZJ0r0  S SK[J\r\J]r]J^r^  S S K_J`r`Jara  S S!KbJcrc  S S"KdJere  S S#KfJgrg  S S$KhJiri  S%rOS&rl\.RÚ                  " SSS%SS'9rnS(\PS)'   S*ro\$Rà                  \$Rà                  \$Râ                  \$Râ                  \$Rä                  \$Rä                  \$Rä                  \$Ræ                  \$Rè                  \$Rè                  S+.
ruS,\PS-'   S9S. jrv\9 " S/ S05      5       rw " S1 S25      rx " S3 S4\45      ry " S5 S6\45      rz " S7 S8\=\>5      r{g! \j a  rk\krQ SrkCkNàSrkCkff = f):é    )ÚannotationsN)Údeepcopy)Údatetime)ÚBytesIO)ÚPath)Úmkdtemp)Ú	AnnotatedÚAnyÚCallableÚFinalÚcast)ÚZipFile)ÚBoundingBoxÚContentLayerÚCoordOriginÚDocItemÚDocItemLabelÚDoclingDocumentÚDocumentOriginÚ	GroupItemÚ
GroupLabelÚImageRefÚPictureClassificationLabelÚPictureClassificationMetaFieldÚPictureClassificationPredictionÚPictureMetaÚProvenanceItemÚSizeÚ	TableCellÚ	TableDataÚTabularChartMetaField)Úetree)ÚImageÚUnidentifiedImageError)Ú	BaseModelÚFieldÚNonNegativeIntÚPositiveInt)Ú	dataclass)Úoverride)ÚDeclarativeDocumentBackendÚPaginatedDocumentBackend)Úconvert_to_modern_formatÚcrop_whitespaceÚget_docx_to_pdf_converter)ÚMsExcelBackendOptions)ÚFormatToMimeTypeÚInputFormat)ÚInputDocument)ÚDocumentLoadErrorFÚboolÚ_OPENPYXL_AVAILABLEzImportError | NoneÚ_OPENPYXL_IMPORT_ERROR)ÚWorkbookÚload_workbook)ÚCellÚ
MergedCell)Ú
Chartsheet)r#   )ÚOneCellAnchorÚSpreadsheetDrawingÚTwoCellAnchor)Úget_dependentsÚget_rels_path)ÚPatternFill)Úrange_boundaries)Ú	Worksheet)ÚIMAGE_NSTzuThe 'openpyxl' package is required to process Excel files. Install it with `pip install 'docling-slim[format-xlsx]'`.)Úresolve_entitiesÚload_dtdÚ
no_networkÚdtd_validationr   Ú_SAFE_XML_PARSERzÞLibreOffice is required to render Excel charts as images (render_chart_images=True). Install LibreOffice and make sure `soffice` is on PATH. Charts still keep their classification and reconstructed tabular data without it.)
ÚbarChartÚ
bar3DChartÚ	lineChartÚline3DChartÚpieChartÚ
pie3DChartÚdoughnutChartÚscatterChartÚ	areaChartÚarea3DChartz,Final[dict[str, PictureClassificationLabel]]Ú _CHART_TAGNAME_TO_CLASSIFICATIONc                ó&   • [        S U  5       5      $ )zLReturn True if any ZIP member name is absolute or contains a path traversal.c              3  óZ   #   • U  H!  oR                  S 5      =(       d    SU;   v •  M#     g7f)Ú/z..N)Ú
startswith)Ú.0Úms     Ú\/home/mande/repo/quber/.venv/lib/python3.13/site-packages/docling/backend/msexcel_backend.pyÚ	<genexpr>Ú(_has_unsafe_zip_paths.<locals>.<genexpr>}   s#   é € Ð@²x°!�|‰|˜CÓ ×- D¨A¡IÔ-²xùs   ‚)+)Úany)Únamelists    r\   Ú_has_unsafe_zip_pathsra   {   s   € äÑ@±xÓ@Ó@Ð@ó    c                  óV   • \ rS rSr% SrS\S'   S\S'   S\S'   S	\S
'   SS jrSS jrSrg)Ú
DataRegioné€   zDRepresents the bounding rectangle of non-empty cells in a worksheet.zPAnnotated[PositiveInt, Field(description='Smallest row index (1-based index).')]Úmin_rowzOAnnotated[PositiveInt, Field(description='Largest row index (1-based index).')]Úmax_rowzSAnnotated[PositiveInt, Field(description='Smallest column index (1-based index).')]Úmin_colzRAnnotated[PositiveInt, Field(description='Largest column index (1-based index).')]Úmax_colc                ó:   • U R                   U R                  -
  S-   $ )z%Number of columns in the data region.é   )ri   rh   ©Úselfs    r\   ÚwidthÚDataRegion.width‘   ó   € à�|‰|˜dŸl™lÑ*¨QÑ.Ð.rb   c                ó:   • U R                   U R                  -
  S-   $ )z"Number of rows in the data region.rk   )rg   rf   rl   s    r\   ÚheightÚDataRegion.height•   rp   rb   © N)Úreturnr(   )	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__rn   rr   Ú__static_attributes__rt   rb   r\   rd   rd   €   s7   ‡ áNðó ðó ðó ðó ô/÷/rb   rd   c                  óJ   • \ rS rSrSrS	S jrS
S jr\S
S j5       rSS jr	Sr
g)Ú_MergedCellIndexéš   zDIndex merged-cell anchors without expanding their coordinate ranges.c                ó^  • 0 U l         S nS nSnSnUR                  R                   Hå  nUR                  S-
  UR                  S-
  4nU R                   R                  UUR                  UR                  -
  S-   UR                  UR                  -
  S-   45        Uc  UR                  O[        X&R                  5      nUc  UR                  O[        X6R                  5      n[        XFR                  5      n[        XVR                  5      nMç     Ub  Ub  [        X$X55      U l        g S U l        g )Nr   rk   )Ú_anchor_spansÚmerged_cellsÚrangesrf   rh   Ú
setdefaultrg   ri   ÚminÚmaxrd   Úbounds)rm   Úsheetrf   rh   rg   ri   Úmerged_rangeÚanchors           r\   Ú__init__Ú_MergedCellIndex.__init__�   s:  € ØEGˆÔà"ˆØ"ˆØˆØˆà!×.Ñ.×5Ô5ˆLØ"×*Ñ*¨QÑ.°×0DÑ0DÀqÑ0HÐIˆFØ×Ñ×)Ñ)Øà ×(Ñ(¨<×+?Ñ+?Ñ?À!ÑCØ ×(Ñ(¨<×+?Ñ+?Ñ?À!ÑCðôð ‘?ð ×$Ò$ä˜×"6Ñ"6Ó7ð ð ‘?ð ×$Ò$ä˜×"6Ñ"6Ó7ð ô
 ˜'×#7Ñ#7Ó8ˆGÜ˜'×#7Ñ#7Ó8ŠGñ) 6ð0 Ñ" wÑ':ô �w¨Ó:ð 	�ð ð 	�rb   c                ó†   • [        U[        5      =(       d+    UR                  S-
  UR                  S-
  4U R                  ;   $ )z?Return whether a cell is an anchor or shadow of a merged range.rk   )Ú
isinstancer;   ÚrowÚcolumnr�   )rm   Úcells     r\   ÚcontainsÚ_MergedCellIndex.containsÁ   sC   € ô �tœZÓ(÷ "à—‘˜1‘Ø—‘˜a‘ðð ×!Ñ!ñ	"ð	
rb   c                ó"   • [        U [        5      $ )z=Return whether a cell is a non-anchor part of a merged range.)rŽ   r;   )r‘   s    r\   Ú	is_shadowÚ_MergedCellIndex.is_shadowÌ   s   € ô ˜$¤
Ó+Ð+rb   c                ó<   • U R                   R                  X4S5      $ )z?Return the row and column span for a 0-based anchor coordinate.)rk   rk   )r�   Úget)rm   r�   Úcols      r\   Úspan_atÚ_MergedCellIndex.span_atÑ   s   € à×!Ñ!×%Ñ% s j°&Ó9Ð9rb   )r�   r‡   N)rˆ   rD   ru   ÚNone)r‘   zCell | MergedCellru   r5   )r�   Úintr™   r�   ru   ztuple[int, int])rv   rw   rx   ry   rz   r‹   r’   Ústaticmethodr•   rš   r|   rt   rb   r\   r~   r~   š   s)   † ÙNô"
ôH	
ð ó,ó ð,÷:rb   r~   c                  óL   • \ rS rSr% SrS\S'   S\S'   S\S'   S\S'   S\S	'   S
rg)Ú	ExcelCelléÖ   a  Represents an Excel cell.

Attributes:
    row: The row number of the cell.
    col: The column number of the cell.
    text: The text content of the cell.
    row_span: The number of rows the cell spans.
    col_span: The number of columns the cell spans.
r�   r�   r™   ÚstrÚtextÚrow_spanÚcol_spanrt   N©rv   rw   rx   ry   rz   r{   r|   rt   rb   r\   r    r    Ö   s"   ‡ ñð 
ƒHØ	ƒHØ
ƒIØƒMØ†Mrb   r    c                  óB   • \ rS rSr% SrS\S'   S\S'   S\S'   S\S	'   S
rg)Ú
ExcelTableéè   aE  Represents an Excel table on a worksheet.

Attributes:
    anchor: The column and row indices of the upper-left cell of the table
    (0-based index).
    num_rows: The number of rows in the table.
    num_cols: The number of columns in the table.
    data: The data in the table, represented as a list of ExcelCell objects.
z%tuple[NonNegativeInt, NonNegativeInt]rŠ   r�   Únum_rowsÚnum_colszlist[ExcelCell]Údatart   Nr¦   rt   rb   r\   r¨   r¨   è   s   ‡ ñð 2Ó1ØƒMØƒMØ
Örb   r¨   c                  ó  ^ • \ rS rSr% SrSrS\S'   SrS\S'   \ S*       S+U 4S jjj5       r	    S,S	 jr
\S-S
 j5       r\\S-S j5       5       r\S.S j5       r\\S/S j5       5       r\S0S j5       rS1S jr        S2S jr      S3S jr        S4S jr    S5S jr      S6S jr    S7S jr              S8S jr\S9S j5       rS:S jrS;S jr        S4S jr            S<S jr        S4S jr        S2S jr \S=S j5       r!S>S jr"\S?S  j5       r#S@S! jr$SAS" jr%SBS# jr&\SCS$ j5       r'SDS% jr(\      SES& j5       r)          SFS' jr*\SGS( j5       r+S)r,U =r-$ )HÚMsExcelDocumentBackendéù   a‡  Backend for parsing Excel workbooks (XLSX and XLS files).

The backend converts an Excel workbook into a DoclingDocument object.
Each worksheet is converted into a separate page.
The following elements are parsed:
- Cell contents, parsed as tables. If two groups of cells are disconnected
  between each other, they will be parsed as two different tables.
- Images, parsed as PictureItem objects.
- Cell comments (notes), both old-style and Excel 365+ threaded comments.

The DoclingDocument tables and pictures have their provenance information, including
the position in their original Excel worksheet. The position is represented by a
bounding box object with the cell indices as units (0-based index). The size of this
bounding box is the number of columns and rows that the table or picture spans.

Legacy ``.xls`` files (binary Excel 97-2004 format) are first converted to
``.xlsx`` via LibreOffice before parsing.

Limitations:
    - Threaded comments (Excel 365+) are only extracted when the file is provided
      as a Path. When provided as a BytesIO stream, threaded comments cannot be
      extracted because the stream is consumed by openpyxl during initialization.
      Old-style cell comments (notes) are always extracted regardless of input type.
    - Legacy ``.xls`` files are always converted to a ``BytesIO`` stream before
      parsing, so threaded comments are not available for that format.
i    z
Final[int]Ú_MAX_DRAWING_BYTESi   Ú_MAX_IMAGE_BYTESc                óŒ  >• [         (       d  [        [        5      [        eUR                  [
        R                  :X  a  [        USS5      nUc
  [        5       n[        TU ])  XU5        UR                  R                  U l        SU l        SU l        SU l        SU l         ["        R$                  " 5          ["        R&                  " SS[(        SS9  ["        R&                  " SS	[(        SS9  [+        U R,                  [.        5      (       a  [1        U R,                  S
S9U l        OA[+        U R,                  [2        5      (       a"  [1        [5        U R,                  5      S
S9U l        SSS5        U R                   SLU l        g! , (       d  f       N"= f! [8         a%  nSU l        [;        SU R<                   35      UeSnAff = f)a  Initialize the MsExcelDocumentBackend object.

Parameters:
    in_doc: The input document object.
    path_or_stream: The path or stream to the Excel file.
    options: Backend options for Excel parsing.

Raises:
    RuntimeError: An error occurred parsing the file.
ÚxlsÚxlsxNFÚignorez>.* image format is not supported so the image is being droppedzopenpyxl\.reader\.drawings)ÚmessageÚcategoryÚmodulez6The image .* will be removed because it cannot be readT)ÚfilenameÚ	data_onlyz9MsExcelDocumentBackend could not load document with hash )r6   ÚImportErrorÚ_INSTALL_HINTr7   Úformatr2   ÚXLSr-   r0   Úsuperr‹   ÚlimitsÚ
page_rangeÚparentÚxlsx_to_pdf_converterÚxlsx_to_pdf_converter_initÚworkbookÚwarningsÚcatch_warningsÚfilterwarningsÚUserWarningrŽ   Úpath_or_streamr   r9   r   r¢   ÚvalidÚ	Exceptionr4   Údocument_hash)rm   Úin_docrÊ   ÚoptionsÚeÚ	__class__s        €r\   r‹   ÚMsExcelDocumentBackend.__init__  s…  ø€ ÷" #Ò"ÜœmÓ,Ô2HÐHØ�=‰=œKŸO™OÓ+Ü5°nÀeÈVÓTˆNØ‰?Ü+Ó-ˆGÜ‰Ñ˜°Ô9à Ÿ-™-×2Ñ2ˆŒð )-ˆŒð 7;ˆÔ"Ø05ˆÔ'àˆŒð	ô ×(Ò(Õ*Ü×'Ò'ØØ]Ü(Ø8ò	ô ×'Ò'ØØUÜ(Ø8ò	ô ˜d×1Ñ1´7×;Ñ;Ü$1Ø!%×!4Ñ!4Àñ%�D•Mô   × 3Ñ 3´T×:Ñ:Ü$1Ü!$ T×%8Ñ%8Ó!9ÀTñ%�D”M÷% +ð, Ÿ™¨dÐ2ˆD�J÷- +Õ*ûô. ó 	ØˆDŒJä#ØKÈD×L^ÑL^ÐK_Ð`óàðûð	ús1   Â!F Â6B1FÅ'F Æ
FÆF Æ
GÆ F>Æ>Gc           	     ó  ^• 0 n[        U R                  [        5      (       d  U$ SS0n [        U R                  [        5      (       a  U R                  R	                  S5        [        U R                  S5       n[        UR                  5       5      (       a   [        R                  S5        UsSSS5        $ 0 n UR                  S5      n[        R                  " U[        S9nUR                  S	US
9 Vs0 s HT  nUR                  S5      (       d  M  UR                  S5      (       d  M3  UR                  S5      UR                  S5      _MV     nn[%        U4S j['        U R(                  R*                  S5       5       S5      n
U
c  UsSSS5        $ SU
 S3n UR                  U5      n[        R                  " U[        S9nUR                  SUS
9 GHF  nUR                  S5      nUR-                  SUS
9nU(       d  M.  Uc  M3  UR.                  =(       d    SnUR                  UR                  S5      S5      nSnUR                  S5      =n(       a¼   UR1                  SS5      nSU;   aT  SU;   aN  UR3                  S5      nUS   R3                  S5      nUS   R5                  SS5      SS nUS    SU SUS    3nO9SU;   a3  UR3                  S5      nUS   R5                  SS5      SS nUS    SU 3n[6        R8                  " U5      nUUU4X/'   GMI     SSS5        U$ s  snf ! [          a#  n	[        R#                  SU	 35         Sn	A	GNSn	A	ff = f! [          a%  n	[        R#                  SU S U	 35         Sn	A	NuSn	A	ff = f! [          a%  n	[        R#                  S!U S"U	 35         Sn	A	N›Sn	A	ff = f! , (       d  f       U$ = f! [          a#  n	[        R#                  S#U	 35         Sn	A	U$ Sn	A	ff = f)$aQ  Parse threaded comments from Excel XML for a specific sheet.

Returns a dict mapping cell coordinates to (author, text, timestamp) tuples.
Only works when path_or_stream is a Path (not BytesIO).

Security Note:
    Uses secure XML parser configuration to prevent XXE attacks and validates
    ZIP file paths to prevent zip-slip attacks.
ÚtczGhttp://schemas.microsoft.com/office/spreadsheetml/2018/threadedcommentsr   Úrz#Skipping file with unsafe ZIP pathsNzxl/persons/person.xml©Úparserz.//tc:person)Ú
namespacesÚidÚdisplayNamezCould not parse person.xml: c              3  óP   >#   • U  H  u  pUR                   T:X  d  M  Uv •  M     g 7f©N)Útitle)rZ   ÚiÚwsÚ
sheet_names      €r\   r]   ÚBMsExcelDocumentBackend._parse_threaded_comments.<locals>.<genexpr>‹  s(   øé € ð â%K™E˜AØŸ8™8 zÑ1÷ ™Ú%Kùs   ƒ&�	&rk   z#xl/threadedComments/threadedCommentz.xmlz.//tc:threadedCommentÚrefztc:textÚ ÚpersonIdÚUnknownÚdTÚZz+00:00Ú.Ú+é   Ú0zCould not parse timestamp 'z': zCould not parse ú: z#Could not parse threaded comments: )rŽ   rÊ   r   r   Úseekr   ra   r`   Ú_logÚwarningÚreadr"   Ú
fromstringrJ   Úfindallr˜   rÌ   ÚdebugÚnextÚ	enumeraterÅ   Ú
worksheetsÚfindr£   ÚreplaceÚsplitÚljustr   Úfromisoformat)rm   rà   Úthreaded_commentsÚnsÚzip_fileÚ
person_mapÚ
person_xmlÚperson_treeÚpersonrÐ   Ú	sheet_numÚthreaded_fileÚthreaded_xmlÚthreaded_treeÚcommentÚcell_refÚ	text_elemr£   ÚauthorÚ	timestampÚtimestamp_strÚ
normalizedÚpartsÚfrac_and_tzÚfracs    `                       r\   Ú_parse_threaded_commentsÚ/MsExcelDocumentBackend._parse_threaded_comments^  s'  ø€ ð JLÐô ˜$×-Ñ-¬t×4Ñ4Ø$Ð$ð Ð[ð
ˆðN	BÜ˜$×-Ñ-¬w×7Ñ7Ø×#Ñ#×(Ñ(¨Ô+ä˜×,Ñ,¨cÔ2°hÜ(¨×):Ñ):Ó)<×=Ñ=Ü—L‘LÐ!FÔGØ,÷ 3Ñ2ð
 .0�
ð	CØ!)§¡Ð/FÓ!G�JÜ"'×"2Ò"2°:ÔFVÑ"W�Kð '2×&9Ñ&9¸.ÐUWÐ&9Ñ&Xó"â&X˜FØ!Ÿ:™: d×+ó Dà06·
±
¸=×0Ió D˜Ÿ
™
 4Ó(¨&¯*©*°]Ó*CÒCÙ&Xð ð "ô !ôä%.¨t¯}©}×/GÑ/GÈÔ%Kóð
 ó�	ð Ñ$Ø,÷5 3Ñ2ð8 #FÀiÀ[ÐPTÐ U�ð*HØ#+§=¡=°Ó#?�LÜ$)×$4Ò$4Ø$Ô-=ñ%�Mð $1×#8Ñ#8Ø/¸Bð $9ô $˜ð $+§;¡;¨uÓ#5˜Ø$+§L¡L°Àr LÐ$J˜	ç#˜8¨	Ó(=Ø#,§>¡>×#7°R˜DØ%/§^¡^°G·K±KÀ
Ó4KÈYÓ%W˜Fà(,˜IØ07·±¸DÓ0AÐA˜}ÕAð!&ð 2?×1FÑ1FÀsÈHÓ1U JØ'*¨jÓ'8¸SÀJÓ=NØ0:×0@Ñ0@ÀÓ0E¨Ø6;¸A±h·n±nÀSÓ6I¨Ø/:¸1©~×/CÑ/CÀAÀsÓ/KÈBÈQÐ/O¨à/4°Q©x¨j¸¸$¸¸qÀÈQÁÐ@PÐ,Qñ )3ð *-°
Ó):Ø0:×0@Ñ0@ÀÓ0E¨Ø/4°Q©x¯~©~¸aÀÓ/EÀbÀqÐ/I¨Ø8=¸a¹°zÀÀ4À&Ð5I¨
Ü08×0FÒ0FÀzÓ0R Ið <BÀ4ÈÐ:SÐ-Ô7ñC$÷G 3ðX !Ð ùòG"øô
 !ó CÜ—J‘JÐ!=¸a¸SÐA×BÒBûðCûôb (1ó !&Ü$(§J¡JØ*EÀmÀ_ÐTWÐXYÐWZÐ([÷%&ñ %&ûð!&ûô !ó HÜ—J‘JÐ!1°-°ÀÀ1À#ÐF×GÑGûðHú÷M 3Ô2ðX !Ð ûô ó 	BÜ�J‰JÐ<¸Q¸CÐ@×AÐAà Ð ûð	Búsó   ªAO Á:5OÂ/	O Â9OÂ<<L2Ã8L-ÄL-Ä.%L-ÅL2Å8OÆ	O ÆOÆA$NÈNÈANÉB;M"ÌNÌ#O Ì-L2Ì2
MÌ<MÍOÍMÍOÍ"
NÍ,NÎNÎNÎNÎ
OÎN>Î9OÎ>OÏOÏ
OÏO ÏO Ï
PÏ"P Ð Pc                ó^   • [         R                  SU R                   35        U R                  $ )Nzvalid: )rî   ró   rË   rl   s    r\   Úis_validÚMsExcelDocumentBackend.is_validÇ  s#   € ä�
‰
�W˜TŸZ™Z˜LÐ)Ô*Ø�z‰zÐrb   c                ó   • g)NTrt   ©Úclss    r\   Úsupports_paginationÚ*MsExcelDocumentBackend.supports_paginationÌ  s   € ð rb   c                óV  ^• U R                  5       (       a“  U R                  (       a‚  [        U R                  [        5      (       a  U R                  R
                  OS mTc  [        U R                  R                  5      $ [        U4S jU R                  R                   5       5      $ g)Nc              3  ó6   >#   • U  H  oT;   d  M
  S v •  M     g7f)rk   Nrt   )rZ   ÚnameÚsheet_names_filters     €r\   r]   Ú4MsExcelDocumentBackend.page_count.<locals>.<genexpr>Û  s   øé € ð Ú6�dÐBTÑ:T—‘Ò6ùs   ƒ	�	r   )	r  rÅ   rŽ   rÏ   r0   Úsheet_namesÚlenÚ
sheetnamesÚsum)rm   r  s    @r\   Ú
page_countÚ!MsExcelDocumentBackend.page_countÑ  s…   ø€ à�=‰=�?‰?˜tŸ}Ÿ}ô ˜dŸl™lÔ,A×BÑBð —‘×(Ò(àð ð
 "Ñ)Ü˜4Ÿ=™=×3Ñ3Ó4Ð4Üô Ø"Ÿm™m×6Ò6óó ð ð rb   c                óB   • [         R                  [         R                  1$ rÜ   )r2   ÚXLSXr¾   r  s    r\   Úsupported_formatsÚ(MsExcelDocumentBackend.supported_formatsá  s   € ô × Ñ ¤+§/¡/Ð2Ð2rb   c                ó^  • [        U R                  R                  =(       d    S[        U R                     S   U R
                  S9n[        U R                  R                  =(       d    SUS9nU R                  5       (       a  U R                  U5      nU$ [        SU R
                   S35      e)zíParse the Excel workbook into a DoclingDocument object.

Raises:
    RuntimeError: Unable to run the conversion since the backend object failed to
    initialize.

Returns:
    The DoclingDocument object representing the Excel workbook.
z	file.xlsxr   )r¹   ÚmimetypeÚbinary_hash)r  ÚoriginzCannot convert doc with z$ because the backend failed to init.)r   Úfiler  r1   Úinput_formatrÍ   r   Ústemr  Ú_convert_workbookÚRuntimeError)rm   r-  Údocs      r\   ÚconvertÚMsExcelDocumentBackend.convertæ  sš   € ô  Ø—Y‘Y—^‘^×2 {Ü% d×&7Ñ&7Ñ8¸Ñ;Ø×*Ñ*ñ
ˆô  4§9¡9§>¡>×#@°[ÈÑPˆà�=‰=�?‰?Ø×(Ñ(¨Ó-ˆCð ˆ
ô	 Ø*¨4×+=Ñ+=Ð*>Ð>bÐcóð rb   c                óê  • U R                   GbÏ  [        U R                  [        5      (       a  U R                  R                  OSnU R
                  u  p4Sn[        U R                   R                  5       GH  u  pgUb#  Xr;  a  [        R                  SU SU S35        M,  US-  nXS:  d  XT:”  a'  [        R                  SU SU SU SU S	U S
35        Mb  [        R                  SU SU SU 35        U R                   U   nUR                  U[        SSS9S9n	UR                  S[        R                  UU R!                  U5      S9U l        U R%                  XU5      nU R'                  X5      u  p«[        X«S9U	l        GM     UbQ  [+        U5      [+        U R                   R                  5      -
  nU(       a  [        R-                  S[/        U5      5        U$ [        R1                  S5        U$ )z°Parse the Excel workbook and attach its structure to a DoclingDocument.

Args:
    doc: A DoclingDocument object.

Returns:
    A DoclingDocument object with the parsed items.
Nr   zSkipping sheet rì   z (filtered out)rk   z (page z outside range Ú-Ú)zProcessing sheet z	 as page )rn   rr   )Úpage_noÚsize)rÂ   Úlabelr  Úcontent_layerz;sheet_names filter contains names not found in workbook: %szWorkbook is not initialized.)rÅ   rŽ   rÏ   r0   r   rÁ   rõ   r"  rî   ró   ÚinfoÚadd_pager   Ú	add_groupr   ÚSHEETÚ_get_sheet_content_layerrÂ   Ú_convert_sheetÚ_find_page_sizer:  Úsetrï   ÚsortedÚerror)rm   r3  r  Ú
start_pageÚend_pager9  Úidxr  rˆ   Úpagern   rr   Ú	unmatcheds                r\   r1  Ú(MsExcelDocumentBackend._convert_workbook  së  € ð �=‰=Ò$ô ˜dŸl™lÔ,A×BÑBð —‘×(Ò(àð ð $(§?¡?Ñ ˆJàˆGä& t§}¡}×'?Ñ'?×@‘	�Ø%Ñ1°dÓ6TÜ—J‘J °°°R¸°v¸_ÐMÔNÙð
 ˜1‘�ØÓ'¨7Ó+=Ü—J‘JØ)¨#¨¨b°°ð 7!Ø!( 	¨¸¸ÀAÀhÀZÈqðRôñ ä—	‘	Ð-¨c¨U°"°T°F¸)ÀGÀ9ÐMÔNàŸ™ dÑ+�à—|‘|¨G¼$ÀQÈqÑ:Q�|ÐR�à!Ÿm™mØÜ$×*Ñ*ØØ"&×"?Ñ"?ÀÓ"Fð	 ,ð �”ð ×)Ñ)¨#°gÓ>�Ø $× 4Ñ 4°SÓ B‘�Ü  uÑ<�—	ñ= Að@ "Ñ-ÜÐ 2Ó3´c¸$¿-¹-×:RÑ:RÓ6SÑS�	ÞÜ—L‘LØUÜ˜yÓ)ôð ˆ
ô �J‰JÐ5Ô6àˆ
rb   c                óô   • [        U[        5      (       a$  U R                  XU5      nU R                  XU5      n[        U[        [        45      (       a  U R                  XU5      nU R                  X5        U$ )a&  Parse an Excel worksheet and attach its structure to a DoclingDocument

Args:
    doc: The DoclingDocument to be updated.
    sheet: The Excel worksheet to be parsed.
    page_no: The dense (1-based) page number for this sheet in the output document.

Returns:
    The updated DoclingDocument.
)rŽ   rD   Ú_find_tables_in_sheetÚ_find_images_in_sheetr<   Ú_find_chart_in_sheetÚ _sort_sheet_children_by_position)rm   r3  rˆ   r9  s       r\   rB  Ú%MsExcelDocumentBackend._convert_sheetC  sl   € ô �eœY×'Ñ'Ø×,Ñ,¨S¸ÓAˆCØ×,Ñ,¨S¸ÓAˆCä�eœi¬Ð4×5Ñ5Ø×+Ñ+¨C¸Ó@ˆCØ×-Ñ-¨cÔ;àˆ
rb   c                ól   ^^• U R                   nUc  gSUU4S jjnUR                  R                  US9  g)a/  Sort the current sheet group's direct children by top-row position.

Tables are added before images during sheet conversion.  When an image
sits above a table on the sheet (smaller row index), it would otherwise
appear after the table in the exported document.  Sorting the sheet
group's children by their ``bbox.t`` corrects the visual order.

Children without provenance on the current page sort to the end.

Args:
    doc: The DoclingDocument whose current sheet group is sorted in place.
    page_no: The 1-based page number of the sheet being processed.
Nc                óÐ   >• U R                  T5      nUc  [        S5      $ [        US/ 5       H+  nUR                  T:X  d  M  UR                  R
                  s  $    [        S5      $ )NÚinfÚprov)ÚresolveÚfloatÚgetattrr9  ÚbboxÚt)râ   ÚitemrV  r3  r9  s      €€r\   Ú_top_rowÚIMsExcelDocumentBackend._sort_sheet_children_by_position.<locals>._top_rown  sY   ø€ Ø—;‘;˜sÓ#ˆDØ‰|Ü˜U“|Ð#Ü  f¨bÖ1�Ø—<‘< 7Õ*ØŸ9™9Ÿ;™;Ò&ñ 2ô ˜“<Ðrb   )Úkey)râ   r
   ru   rX  )rÂ   ÚchildrenÚsort)rm   r3  r9  Úsheet_groupr]  s    ``  r\   rQ  Ú7MsExcelDocumentBackend._sort_sheet_children_by_positionZ  s:   ù€ ð  —k‘kˆØÑØ÷	 ð 	 ð 	×Ñ×!Ñ! hÐ!Ò/rb   c                óä  • U R                   Gba  U R                  U5      nU R                  U5      u  pV[        U R                  [
        5      =(       a    U R                  R                  nU GHw  nU R                  U5      u  p˜UR                  S   n
UR                  S   nUR                  nUR                  nU	b‹  UR                  U	R                  [        R                  U R                  [!        US["        R$                  " X©R&                  -   US-
  X©R&                  -   U	R(                  -   U4[*        R,                  S9S9US9  U(       a�  [/        UR0                  5      S:X  aw  UR                  UR0                  S   R                  [        R                  U R                  [!        US["        R$                  " U
UX­-   X¼-   4[*        R,                  S9S9US9  GMr  [3        UU/ S9nUR0                   Hœ  n[5        UR                  UR6                  UR(                  UR8                  UR8                  UR6                  -   UR&                  UR&                  UR(                  -   UR8                  S:H  S	S
9	nUR:                  R=                  U5        Mž     UR?                  UU R                  [!        US["        R$                  " U
UX­-   X¼-   4[*        R,                  S9S9US9  GMz     URA                  5        GHs  u  u  nnu  nnn/ nU(       a  UR=                  SU 35        U(       a#  URC                  SS9nUR=                  SU 35        U(       a  U(       a  SSRE                  U5       SU 3nOU(       a  SSRE                  U5       S3nOUnU RG                  XUU5      nU(       a  U/OSnURI                  [J        RL                  SURN                   SURQ                  US-   US-   S9RR                   3[T        RV                  S9nURY                  UUUS9  U(       a  GM/  [Z        R]                  SURN                   SURQ                  US-   US-   S9RR                   S35        GMv     U$ )a}  Find all tables in an Excel sheet and attach them to a DoclingDocument.

Also extracts comments from cells and links them to their corresponding table cells.

Args:
    doc: The DoclingDocument to be updated.
    sheet: The Excel worksheet to be parsed.
    page_no: The dense (1-based) page number for this sheet in the output document.

Returns:
    The updated DoclingDocument.
Nr   rk   ©r   r   ©r-  ©r9  ÚcharspanrZ  )r£   r;  rÂ   rV  r<  ©rª   r«   Útable_cellsF©	r£   r¤   r¥   Ústart_row_offset_idxÚend_row_offset_idxÚstart_col_offset_idxÚend_col_offset_idxÚcolumn_headerÚ
row_header)r¬   rÂ   rV  r<  zauthor: Úmilliseconds)Útimespecztime: Ú[z, z]: Ú]zcomment-r7  ©r�   r�   )r;  r  r<  )r£   ÚtargetsrÂ   zComment at Ú!z has no cell item for linking)/rÅ   rA  Ú_find_data_tablesrŽ   rÏ   r0   Útreat_singleton_as_textÚ_split_leading_section_labelrŠ   rª   r«   Úadd_textr£   r   ÚTEXTrÂ   r   r   Ú
from_tupler™   r¥   r   ÚTOPLEFTr!  r¬   r    r   r¤   r�   rj  ÚappendÚ	add_tableÚitemsÚ	isoformatÚjoinÚ_find_cell_itemr?  r   ÚCOMMENT_SECTIONrÝ   r‘   Ú
coordinater   ÚNOTESÚadd_commentrî   ró   )rm   r3  rˆ   r9  r<  ÚtablesÚcomment_maprz  Úexcel_tableÚ
title_cellÚ
origin_colÚ
origin_rowrª   r«   Ú
table_dataÚ
excel_cellr‘   r�   r™   r
  Úraw_textr  Úmetadata_partsr  Ú	full_textÚ	cell_itemrw  Úcomment_groups                               r\   rN  Ú,MsExcelDocumentBackend._find_tables_in_sheety  sa  € ð  �=‰=Ò$Ø ×9Ñ9¸%Ó@ˆMØ"&×"8Ñ"8¸Ó"?ÑˆFô ˜4Ÿ<™<Ô)>Ó?÷ 9Ø—L‘L×8Ñ8ð $ô
  &�Ø*.×*KÑ*KÈKÓ*XÑ'�
Ø(×/Ñ/°Ñ2�
Ø(×/Ñ/°Ñ2�
Ø&×/Ñ/�Ø&×/Ñ/�ØÑ)Ø—L‘LØ'Ÿ_™_Ü*×/Ñ/Ø#Ÿ{™{Ü+Ø$+Ø%+Ü!,×!7Ò!7à$.·±Ñ$?Ø$.°¡NØ$.·±Ñ$?À*×BUÑBUÑ$UØ$.ð	!"ô (3×':Ñ':ñ"ñð '4ð# !ñ ö( +¬s°;×3CÑ3CÓ/DÈÓ/IØ—L‘LØ(×-Ñ-¨aÑ0×5Ñ5Ü*×/Ñ/Ø#Ÿ{™{Ü+Ø$+Ø%+Ü!,×!7Ò!7à$.Ø$.Ø$.Ñ$9Ø$.Ñ$9ð	!"ô (3×':Ñ':ñ"ñð '4ð# !ô ô( "+Ø!)Ø!)Ø$&ñ"�Jð '2×&6Ô&6˜
Ü(Ø!+§¡Ø%/×%8Ñ%8Ø%/×%8Ñ%8Ø1;·±Ø/9¯~©~À
×@SÑ@SÑ/SØ1;·±Ø/9¯~©~À
×@SÑ@SÑ/SØ*4¯.©.¸AÑ*=Ø',ñ
 ˜ð #×.Ñ.×5Ñ5°dÖ;ñ '7ð —M‘MØ'Ø#Ÿ{™{Ü+Ø$+Ø%+Ü!,×!7Ò!7à$.Ø$.Ø$.Ñ$9Ø$.Ñ$9ð	!"ô (3×':Ñ':ñ"ñð '4ð! "ô ñI  &ðp >I×=NÑ=N×=PÑ9‘
��cÑ9˜V X¨yØ!#�ÞØ"×)Ñ)¨H°V°HÐ*=Ô>ÞØ$-×$7Ñ$7ÀÐ$7Ð$P�MØ"×)Ñ)¨F°=°/Ð*BÔCæ!¦hØ"# D§I¡I¨nÓ$=Ð#>¸cÀ(ÀÐ L‘IÞ#Ø"# D§I¡I¨nÓ$=Ð#>¸aÐ @‘Ià (�Ià ×0Ñ0°¸sÀCÓH�	Þ)2˜9™+¸�à #§¡Ü$×4Ñ4Ø# E§K¡K =°°%·*±*ÀÀqÁÐQTÐWXÑQX°*Ð2Y×2dÑ2dÐ1eÐfÜ".×"4Ñ"4ð !.ð !�ð
 —‘Ø"Ø#Ø(ð  ñ ÷ ‘wÜ—J‘JØ% e§k¡k ]°!°E·J±JÀ3ÈÁ7ÐSVÐYZÑSZ°JÐ4[×4fÑ4fÐ3gð h7ð 8÷ñ= >QðF ˆ
rb   c                óH  • UR                   S:  d  UR                  S:  a  SU4$ UR                   Vs/ s H  o"R                  S:X  d  M  UPM     nnU Vs/ s H%  o"R                  R                  5       (       d  M#  UPM'     nn[        U5      S:w  a  SU4$ US   nUR                  S:w  d:  UR                  S:w  d*  UR                  S::  d  UR                  UR                  :”  a  SU4$ UR                   Vs/ s HJ  nUR                  S:X  d  M  UR                  R                  5       (       d  M6  UR                  S:X  d  MH  UPML     nn[        U5      S:  a  SU4$ UR                   Vs/ s HW  nUR                  S:”  d  M  [        UR                  S-
  UR                  UR                  UR                  UR                  S9PMY     nnU[        UR                  S   UR                  S   S-   4UR                   S-
  UR                  US94$ s  snf s  snf s  snf s  snf )z9Split a merged section label from an adjacent data table.é   Nr   rk   ©r�   r™   r£   r¤   r¥   ©rŠ   rª   r«   r¬   )rª   r«   r¬   r�   r£   Ústripr!  r™   r¤   r¥   r    r¨   rŠ   )rm   Útabler‘   Úfirst_row_cellsÚfirst_row_text_cellsr�  Úsecond_row_header_cellsr¬   s           r\   r{  Ú3MsExcelDocumentBackend._split_leading_section_label  sû  € ð �>‰>˜AÓ §¡°!Ó!3Ø˜�;Ðà,1¯JªJÓHªJ D¿(¹(Àa¹-Ÿ4©JˆÐHÙ1@ÓV²¨ÇIÁIÇOÁO×DU§±ÐÐVÜÐ#Ó$¨Ó)Ø˜�;Ðà)¨!Ñ,ˆ
à�N‰N˜aÓØ×"Ñ" aÓ'Ø×"Ñ" aÓ'Ø×"Ñ" U§^¡^Ó3à˜�;Ðð Ÿ
š
ó#
â"�Ø�x‰x˜1‰}ó à!%§¡§¡×!2ó à7;·}±}ÈÑ7I÷ Ù"ð 	 ð #
ô
 Ð&Ó'¨!Ó+Ø˜�;Ðð Ÿ
š
ó

ò #�Ø�x‰x˜!‰|óŒIØ—H‘H˜q‘LØ—H‘HØ—Y‘YØŸ™ØŸ™ôñ #ð 	ð 

ð ÜØŸ™ Q™¨¯©°a©¸1Ñ)<Ð=ØŸ™¨!Ñ+ØŸ™Øñ	ð
ð 	
ùòC IùÚVùò#
ùò

s<   ³HÁ
HÁ"HÁ<HÃ8HÄHÄ1HÅHÅ,HÆAHc                óT  • Su  p4Su  pVUR                   R                  5        H_  nUR                  c  M  UR                  UR                  p˜Uc  UO
[        X85      nUc  U	O
[        XI5      n[        XX5      n[        Xi5      nMa     UR                  b~  UR                  n
Uc  U
R                  O[        X:R                  5      nUc  U
R                  O[        XJR                  5      n[        XZR                  5      n[        XjR                  5      nUb  Uc  S=n=n=pV[        X5XF5      $ )a[  Find the true data boundaries (min/max rows and columns) in a worksheet.

This function scans all cells to find the smallest rectangular region that contains
all non-empty cells or merged cell ranges. It returns the minimal and maximal
row/column indices that bound the actual data region.

Args:
    sheet: The worksheet to analyze.
    merged_cell_index: Index containing merged-cell anchors, spans, and
        bounds for the worksheet.

Returns:
    A data region representing the smallest rectangle that covers all data and merged cells.
    If the sheet is empty, returns (1, 1, 1, 1) by default.
)NNre  rk   )Ú_cellsÚvaluesÚvaluer�   r�   r…   r†   r‡   rf   rh   rg   ri   rd   )rm   rˆ   Úmerged_cell_indexrf   rh   rg   ri   r‘   rÕ   ÚcÚmerged_boundss              r\   Ú_find_true_data_boundsÚ-MsExcelDocumentBackend._find_true_data_boundsA  s#  € ð$ &ÑˆØÑˆà—L‘L×'Ñ'Ö)ˆDØ�z‰zÓ%Ø—x‘x §¡�1Ø&™™!´C¸³O�Ø&™™!´C¸³O�Ü˜g›/�Ü˜g›/’ñ *ð ×#Ñ#Ñ/Ø-×4Ñ4ˆMð ‘?ð ×%Ò%ä˜×"7Ñ"7Ó8ð ð ‘?ð ×%Ò%ä˜×"7Ñ"7Ó8ð ô
 ˜'×#8Ñ#8Ó9ˆGÜ˜'×#8Ñ#8Ó9ˆGð ‰?˜g™oØ45Ð5ˆGÐ5�gÐ5 ä˜'¨GÓ=Ð=rb   c                ó   • [        U5      nU R                  X5      n/ n[        5       n0 nU R                  UR                  5      n[        UR                  UR                  UR                  UR                  UR                  SS9UR                  S-
  S9 GHr  u  p‰[        X“R                  S-
  S9 GHQ  u  p«UR                  bÚ  UR                  R                  =(       d    SnUR                  R                  (       a-  [        UR                  R                  5      R                  5       OSnSnUR                   nX÷;   a  X   u  pÍnOGUR#                  S5      (       a1  SU;   a+  S	U;   a#  UR%                  S	S5      S   R                  5       nS
nU(       a  XÍU4XhU
4'   UR&                  b  XŠ4U;   a  GM  U R)                  UUU
UR                  UR                  US9u  nnUR+                  U5        UR-                  U5        GMT     GMu     XF4$ )aˆ  Find all compact rectangular data tables in an Excel worksheet.

Also collects comments from cells during the same iteration for efficiency.

Args:
    sheet: The Excel worksheet to be parsed.

Returns:
    A tuple containing:
        - A list of ExcelTable objects representing the data tables
        - A dict mapping (row, col) to (author, comment_text, timestamp) for cells with comments
F)rf   rg   rh   ri   Úvalues_onlyrk   )ÚstartNrã   ztc={z[Threaded comment]z	Comment:
zThreaded comment)Ú	start_rowÚ	start_colrg   ri   r¦  )r~   r©  rD  r  rÝ   rõ   Ú	iter_rowsrf   rg   rh   ri   r  r
  r£   r¢   rœ  r‡  rY   rù   r¥  Ú_find_table_boundsÚupdater€  )rm   rˆ   r¦  r‡   rŠ  Úvisitedr‹  rü   Úrir�   Úrjr‘   r
  r’  r  Ú
cell_coordÚtable_boundsÚvisited_cellss                     r\   ry  Ú(MsExcelDocumentBackend._find_data_tablest  sø  € ô" -¨UÓ3ÐØ×,Ñ,¨UÓFˆØ#%ˆÜ(+«ˆð ð 	ð
 !×9Ñ9¸%¿+¹+ÓFÐô !Ø�O‰OØŸ™ØŸ™ØŸ™ØŸ™Ø!ð ð ð —.‘. 1Ñ$õ	
‰GˆBô & c·±À!Ñ1CÕD‘�à—<‘<Ñ+Ø!Ÿ\™\×0Ñ0×6°B�Fà:>¿,¹,×:K×:Kœ˜DŸL™L×-Ñ-Ó.×4Ñ4Ô6ÐQSð ð !%�Ið "&§¡�JØ!Ó6Ø6GÑ6SÑ3˜©)Ø×*Ñ*¨6×2Ñ2Ð7KÈxÓ7Wà'¨8Ó3Ø'/§~¡~°lÀAÓ'FÀqÑ'I×'OÑ'OÓ'Q˜HØ!3˜æØ17À9Ð0M˜¨ HÑ-à—:‘:Ñ%¨"¨°WÓ)<Úð /3×.EÑ.EØØ Ø Ø"ŸN™NØ"ŸN™NØ&7ð /Fð /Ñ+�˜mð —‘˜}Ô-Ø—‘˜l×+ôG Eñ	
ð^ Ð"Ð"rb   c               ó
  ^^^^• [        [        U R                  5      R                  n[        R
                  " X#4/5      n[        5       n	U	R                  X#45        X"pºX3pÜS	UUUU4S jjnU(       aÂ  UR                  5       u  nn[        X¯5      n
[        X¿5      n[        UU5      n[        UU5      n/ SQnU Hn  u  nn[        SUS-   5       HU  nUUU-  -   UUU-  -   nnUU4U	;   a    M3  U" UU5      (       d  M.  U	R                  UU45        UR                  UU45          Ml     Mp     U(       a  MÂ  / n[        X«S-   5       H¡  n[        XÍS-   5       HŒ  nTR                  US-   US-   S9nTR                  U5      (       a  M1  UR                  b  [!        UR                  5      OSnTR#                  UU5      u  nnUR                  [%        UU
-
  UU-
  UUUS95        MŽ     M£     ['        XÊ4US-   U
-
  US-   U-
  US9U	4$ )
aj  Determine table bounds using a Flood Fill (BFS) strategy.

This method identifies contiguous regions of non-empty cells in an Excel worksheet
using a breadth-first search algorithm. It accurately detects non-rectangular tables
(e.g., L-shapes, staggered columns) by exploring connected cells with optional gap
tolerance.

The algorithm operates in two phases:
1. Flood Fill: Uses BFS to find all connected cells starting from the given position
2. Data Extraction: Builds a rectangular bounding box and extracts cell data,
   handling merged cells appropriately

Args:
    sheet: The Excel worksheet to analyze.
    start_row: The starting row index (0-based) for the flood fill.
    start_col: The starting column index (0-based) for the flood fill.
    max_row: The exclusive row bound to consider in the worksheet.
    max_col: The exclusive column bound to consider in the worksheet.

Returns:
    A tuple containing:
        - ExcelTable: An object representing the detected table with its anchor
          position, dimensions, and cell data.
        - set[tuple[int, int]]: A set of (row, col) tuples representing all cells
          that were visited during the flood fill, used to prevent re-scanning.

Note:
    The method respects the GAP_TOLERANCE option, which allows cells separated by
    empty cells to be considered part of the same table if within tolerance distance.
c                ó    >• U S:  d  US:  d  U T:¼  d  UT:¼  a  gTR                  U S-   US-   S9nUR                  b  gTR                  U5      $ )Nr   Frk   rv  T)r‘   r¥  r’   )rÕ   r§  r‘   ri   rg   r¦  rˆ   s      €€€€r\   Úhas_contentÚ>MsExcelDocumentBackend._find_table_bounds.<locals>.has_contentù  s[   ø€ Ø�1‹u˜˜A›  g£°°g³Øð —:‘: ! a¡%°°A±�:Ð6ˆDØ�z‰zÑ%Øð %×-Ñ-¨dÓ3Ð3rb   ))r   rk   )r   éÿÿÿÿ)rk   r   )r¾  r   rk   r™  rv  rã   rš  r›  )rÕ   r�   r§  r�   ru   r5   )r   r0   rÏ   Úgap_toleranceÚcollectionsÚdequerD  ÚaddÚpopleftr…   r†   Úranger€  r‘   r•   r¥  r¢   rš   r    r¨   )rm   rˆ   r®  r¯  rg   ri   r¦  ÚGAP_TOLERANCEÚqueuerj  Úmin_rÚmax_rÚmin_cÚmax_cr¼  Úcurr_rÚcurr_cÚ
directionsÚdrÚdcÚstepÚnrÚncr¬   r´  rµ  r‘   Ú	cell_textr¤   r¥   s    `  ```                       r\   r±  Ú)MsExcelDocumentBackend._find_table_boundsÂ  s+  û€ ôP Ô2°D·L±LÓA×OÑOˆô ×!Ò! IÐ#9Ð":Ó;ˆô -0«EˆØ�‰˜Ð.Ô/ð !ˆuØ ˆu÷
	4ò 
	4ö Ø"Ÿ]™]›_‰NˆF�Fô ˜Ó&ˆEÜ˜Ó&ˆEÜ˜˜vÓ&ˆEÜ˜˜vÓ&ˆEòˆJó %‘��Bô " ! ]°QÑ%6Ö7�DØ# r¨D¡yÑ1°6¸RÀ$¹YÑ3G˜�Bà˜B�x ;Ó.Úá" 2 r×*Ó*Ø#Ÿ™¨¨R¨Ô1ØŸ™ b¨" XÔ.âó 8ñ %÷% ˆeðD ˆô ˜ q™yÖ)ˆBÜ˜E¨1¡9Ö-�ð —z‘z b¨1¡f°R¸!±V�zÐ<�Ø$×.Ñ.¨t×4Ñ4Ùà/3¯z©zÑ/EœC §
¡
œOÈ2�	ð &7×%>Ñ%>¸rÀ2Ó%FÑ"�˜(à—‘ÜØ ™JØ ™JØ&Ø!)Ø!)ñöó! .ñ *ô@ Ø�~Ø ™ UÑ*Ø ™ UÑ*Øñ	ð ð
ð 	
rb   c                óÈ  • [        U [        5      (       a\  U R                  R                  U R                  R                  U R
                  R                  S-   U R
                  R                  S-   4$ [        U [        5      (       a\  U R                  R                  U R                  R                  U R                  R                  S-   U R                  R                  S-   4$ g)záConvert an openpyxl anchor object to a (left_col, top_row, right_col, bottom_row) tuple.

Args:
    anchor: A TwoCellAnchor, OneCellAnchor, or unknown anchor type.

Returns:
    A 4-tuple suitable for BoundingBox.from_tuple.
rk   )r   r   r   r   )rŽ   r?   Ú_fromr™   r�   Útor=   )rŠ   s    r\   Ú_anchor_to_tupleÚ'MsExcelDocumentBackend._anchor_to_tupleU  s±   € ô �fœm×,Ñ,à—‘× Ñ Ø—‘× Ñ Ø—	‘	—‘ Ñ!Ø—	‘	—‘ Ñ!ð	ð ô �fœm×,Ñ,à—‘× Ñ Ø—‘× Ñ Ø—‘× Ñ  1Ñ$Ø—‘× Ñ  1Ñ$ð	ð ð rb   c                óÄ   • U R                   (       a  U R                  $ SU l         [        5       U l        U R                  c  [        R	                  S5        U R                  $ )a  Lazily initialize and return a LibreOffice converter callable.

The converter accepts ``(input_path: Path, output_path: Path)`` and
converts the input file to PDF at the given output path.

Returns:
    A converter callable, or None when LibreOffice is not available.
TuA   LibreOffice not found â€” EMF/WMF images in XLSX will be skipped.)rÄ   rÃ   r/   rî   ró   rl   s    r\   Ú_get_libreoffice_converterÚ1MsExcelDocumentBackend._get_libreoffice_convertero  sU   € ð ×*×*Ø×-Ñ-Ð-à*.ˆÔ'Ü%>Ó%@ˆÔ"Ø×%Ñ%Ñ-Ü�J‰JØSôð ×)Ñ)Ð)rb   c                ó
  • U R                  5       nUc  gUSS S:X  a  SOSn[        [        5       5      n USU 3-  nUS-  nUR                  U5        U" XV5        UR	                  5       (       d7  [
        R                  SUR                  5         [        R                  " US	S
9  g[        R                  " [        U5      5      nUS   n[        UR                  SS9R                  5       5      n	UR!                  5         UR!                  5         U	[        R                  " US	S
9  $ ! ["         a5  n
[
        R                  SU
5         Sn
A
[        R                  " US	S
9  gSn
A
ff = f! [        R                  " US	S
9  f = f)u§  Convert a raw EMF or WMF image to a PIL Image via LibreOffice.

LibreOffice can convert standalone ``.emf``/``.wmf`` files to PDF
directly â€” no DOCX or XLSX wrapper is needed.  The raw bytes are
written to a temp file, converted to PDF, and the first page is
rendered with pypdfium2.

Args:
    image_bytes: Raw EMF or WMF image data.

Returns:
    A PIL Image, or None if LibreOffice is unavailable or conversion fails.
Né   s   ×ÍÆšz.wmfz.emfÚimagez	image.pdfz)LibreOffice produced no PDF output for %sT©Úignore_errorsr   r™  ©Úscalez-EMF/WMF conversion via LibreOffice failed: %s)rÛ  r   r   Úwrite_bytesÚexistsrî   ró   r  ÚshutilÚrmtreeÚ	pypdfium2ÚPdfDocumentr¢   r.   ÚrenderÚto_pilÚcloserÌ   )rm   Úimage_bytesÚ	converterÚsuffixÚtemp_dirÚ
input_pathÚoutput_pathÚpdfrJ  Ú	pil_imageÚexcs              r\   Ú_convert_emf_to_pilÚ*MsExcelDocumentBackend._convert_emf_to_pilƒ  sN  € ð ×3Ñ3Ó5ˆ	ØÑØð ' r¨˜?Ð.AÓA‘ÀvˆÜœ›	“?ˆð	8Ø! e¨F¨8Ð$4Ñ4ˆJØ" [Ñ0ˆKØ×"Ñ" ;Ô/Ù�jÔ.Ø×%Ñ%×'Ñ'Ü—
‘
ÐFÈ
ÏÉÔXØô �MŠM˜(°$Ó7ô ×'Ò'¬¨KÓ(8Ó9ˆCØ�q‘6ˆDÜ'¨¯©¸!¨Ð(<×(CÑ(CÓ(EÓFˆIØ�J‰JŒLØ�I‰IŒKØô
 �MŠM˜(°$Ó7øô	 ó 	Ü�J‰JÐFÈÔLÛä�MŠM˜(°$Ó7ûð		ûô �MŠM˜(°$Ó7ús1   ¶AD) Â(A+D) Ä)
E(Ä3E#Å	E+ Å#E(Å(E+ Å+Fc           	     ó²  • UR                   R                  [        R                  5       Vs/ s H  nUR                  PM     nnU(       d  U$ U R                  U5      n[        U R                  [        5      (       a  U R                  R                  S5         [        U R                  S5       n[        UR                  5       5      (       a   [        R                  S5        UsSSS5        $ U H  nU R                  XXƒU5      nM     SSS5        U$ s  snf ! , (       d  f       U$ = f! [          a!  n	[        R#                  SU	5         Sn	A	U$ Sn	A	ff = f)a.  Find EMF/WMF images dropped by openpyxl and add them via LibreOffice.

openpyxl silently drops WMF images and raises ``OSError`` for EMF
images (which PIL cannot decode natively).  This method re-parses the
drawing relationships for the sheet directly from the XLSX zip archive
and converts those unsupported images using LibreOffice.

Args:
    doc: The DoclingDocument to be updated.
    sheet: The Excel worksheet to be parsed.
    page_no: The dense (1-based) page number for this sheet in the output document.

Returns:
    The updated DoclingDocument.
r   rÕ   z=Skipping EMF/WMF scan: XLSX archive contains unsafe ZIP pathsNz7Could not scan drawing files for unsupported images: %s)Ú_relsr÷   r>   Ú	_rel_typeÚtargetrA  rŽ   rÊ   r   rí   r   ra   r`   rî   rï   Ú'_process_drawing_for_unsupported_imagesrÌ   ró   )
rm   r3  rˆ   r9  ÚrelÚdrawing_pathsr<  ÚzfÚdrawing_pathrõ  s
             r\   Ú!_find_unsupported_images_in_sheetÚ8MsExcelDocumentBackend._find_unsupported_images_in_sheet¬  s3  € ð* —{‘{×'Ñ'Ô(:×(DÑ(DÔEó$
âE�ð �JŒJÙEð 	ð $
ö ØˆJà×5Ñ5°eÓ<ˆä�d×)Ñ)¬7×3Ñ3Ø×Ñ×$Ñ$ QÔ'ð	WÜ˜×,Ñ,¨cÔ2°bÜ(¨¯©«×7Ñ7Ü—L‘LØWôð ÷ 3Ñ2ó %2�LØ×FÑFØ ¸ó’Cñ %2÷ 3ð ˆ
ùò7$
÷ 3Ô2ð ˆ
ûô ó 	WÜ�J‰JÐPÐRU×VÐVàˆ
ûð	WúsG   ¬DÂD+ Â/5DÃ$	D+ Ã.DÄ
D+ Ä
D(Ä#D+ Ä(D+ Ä+
EÄ5EÅEc                ó  • X2R                  5       ;  a  U$ [        R                  " UR                  U5      [        S9n [
        R                  " U5      n[        U5      nX‚R                  5       ;  a  U$ [        X(5      n	UR                   H§  n
U	R                  U
R                  5      nUR                  [        :w  d  UR                  UR                  5       ;  a  MR  UR                  UR                  5      n [         R"                  " [%        U5      5      n[%        5       nUR'                  USS9  M©     U$ ! [         a    Us $ f = f! [(        [*        4 a     Of = fU R-                  U5      nUc#  [.        R1                  SUR                  5        GM  UR3                  U R4                  [6        R8                  " USS9S[;        US[<        R>                  " U RA                  U
RB                  5      [D        RF                  S	9S
9US9  GM‚  )an  Scan one drawing XML file and convert any EMF/WMF blips found.

Args:
    doc: The DoclingDocument to update.
    zf: Open ZipFile for the xlsx archive.
    drawing_path: Absolute path inside the zip to the drawing XML.
    page_no: Page number (1-based) for provenance.
    content_layer: ContentLayer for added pictures.

Returns:
    The updated DoclingDocument.
rÖ   ÚPNG)r½   Nz`Could not convert unsupported image '%s'. Install LibreOffice for EMF/WMF support in XLSX files.éH   ©rß  Údpire  rf  rg  ©rÂ   rß  ÚcaptionrV  r<  )$r`   r"   rñ   rð   rJ   r>   Ú	from_treeÚ	TypeErrorrA   r@   Ú
_blip_relsr˜   ÚembedÚTyperE   rû  ÚPILImageÚopenr   Úsaver$   ÚOSErrorrö  rî   rï   Úadd_picturerÂ   r   Úfrom_pilr   r   r~  rØ  rŠ   r   r  )rm   r3  rÿ  r   r9  r<  ÚtreeÚdrawingÚ	rels_pathÚdepsrý  Údeprí  Ú	pil_probeÚ	probe_bufrô  s                   r\   rü  Ú>MsExcelDocumentBackend._process_drawing_for_unsupported_imagesÜ  sÇ  € ð( Ÿ{™{›}Ó,ØˆJä×Ò §¡¨Ó 5Ô>NÑOˆð	Ü(×2Ò2°4Ó8ˆGô " ,Ó/ˆ	ØŸK™K›MÓ)ØˆJÜ˜bÓ,ˆà×%Ô%ˆCØ—(‘(˜3Ÿ9™9Ó%ˆCØ�x‰xœ8Ó# s§z¡z¸¿¹»Ó'FÙàŸ'™' #§*¡*Ó-ˆKðÜ$ŸMšM¬'°+Ó*>Ó?�	Ü#›I�	Ø—‘˜y°�Ñ7Ùñ &ðP ˆ
øôa ó 	ØŠJð	ûô* +¬GÐ4ó Ùðúð ×0Ñ0°Ó=ˆIØÑ Ü—‘ðMà—J‘Jôò
 à�O‰OØ—{‘{Ü×'Ò'¨i¸RÑ@ØÜ#Ø#Ø#Ü$×/Ò/Ø×-Ñ-¨c¯j©jÓ9Ü*×2Ñ2ññð ,ð ô s#   ¿D9 Ã:9EÄ9EÅEÅEÅEc                óh  • U R                   bý  U R                  U5      nUR                   HÊ  n [        [        U5      nUR
                  n[        U[        R                  5      (       a  UO[        R                  " U5      nUR                  U R                  [        R                  " USS9S[        US[        R                  " U R!                  UR"                  5      [$        R&                  S9S9US9  MÌ     U R/                  XU5      nU$ ! [(         a    [*        R-                  S5         GM  f = f)	a&  Find images in the Excel sheet and attach them to the DoclingDocument.

Args:
    doc: The DoclingDocument to be updated.
    sheet: The Excel worksheet to be parsed.
    page_no: The dense (1-based) page number for this sheet in the output document.

Returns:
    The updated DoclingDocument.
Nr  r  re  rf  rg  r  z-could not extract the image from excel sheets)rÅ   rA  Ú_imagesr   r#   râ   rŽ   r  r  r  rÂ   r   r  r   r   r~  rØ  rŠ   r   r  rÌ   rî   rF  r  )	rm   r3  rˆ   r9  r<  r\  rß  râ   rô  s	            r\   rO  Ú,MsExcelDocumentBackend._find_images_in_sheet(  s  € ð �=‰=Ñ$Ø ×9Ñ9¸%Ó@ˆMàŸœ�ðPÜ#'¬¨tÓ#4�EØŸ)™)�Cä)¨#¬x¯~©~×>Ñ>™ÄHÇMÂMÐRUÓDVð ð —O‘OØ#Ÿ{™{Ü&×/Ò/°iÀRÑHØ $Ü+Ø$+Ø%+Ü!,×!7Ò!7Ø $× 5Ñ 5°e·l±lÓ CÜ'2×':Ñ':ñ"ñð '4ð $ó ñ &ð2 ×8Ñ8¸ÀWÓMˆCàˆ
øô !ó PÜ—J‘JÐN×OÐOðPús   ¯CDÄD1Ä0D1c                óv  • [        U R                  [        5      (       a  U R                  R                  (       d  U$ U R	                  U5      nUR
                  nU(       d  U$ U R                  R                  nU(       a,  U R                  5       c  [        R                  [        5        SnU GH<  n [        R                  UR                  [        R                  5      nU R!                  U5      n	U R#                  U5      n
[$        R&                  " U R)                  UR*                  5      [,        R.                  S9nSnU(       a*   U R1                  U5      nUb  [2        R4                  " USS9nU	(       a  UR9                  [:        R<                  U	US	9OSnUR?                  U R@                  UU[C        US
US9US9n[E        [G        [I        US9/S9U
b	  [K        U
S9OSS9Ul&        GM?     U$ ! [6         a    [        R                  SSS9   NŸf = f! [6         a    [        RO                  SSS9   GM‰  f = f)aq  Find native charts on a sheet and attach them as classified pictures.

openpyxl parses each embedded chart (bar, line, pie, scatter, ...) into
``sheet._charts``.  For every chart we emit a PictureItem whose meta
carries (a) the chart-type classification and (b) the chart's underlying
numbers reconstructed as a TableData.  This mirrors the ODF backend so
XLSX and ODS charts have the same downstream shape.

Args:
    doc: The DoclingDocument to update.
    sheet: The worksheet or chart sheet being parsed.
    page_no: The 1-based page number of this sheet, used for provenance.

Returns:
    The updated DoclingDocument.
NFrf  r  r  z@could not render a chart image; keeping chart data without imageT)Úexc_info)r;  r£   r<  re  rg  r  )Ú
class_name)Úpredictions)Ú
chart_data)ÚclassificationÚtabular_chartz.could not extract a chart from the excel sheet)(rŽ   rÏ   r0   Úparse_chartsrA  Ú_chartsÚrender_chart_imagesrÛ  rî   rï   Ú_CHART_RENDER_HINTrU   r˜   Útagnamer   ÚOTHER_CHARTÚ_chart_title_textÚ_chart_to_table_datar   r~  rØ  rŠ   r   r  Ú_render_chart_imager   r  rÌ   r|  r   ÚCAPTIONr  rÂ   r   r   r   r   r!   ÚmetarF  )rm   r3  rˆ   r9  r<  ÚchartsÚrender_chartsÚchartr%  Úcaption_textr�  rZ  Ú	image_refÚchart_imageÚcaption_itemÚpictures                   r\   rP  Ú+MsExcelDocumentBackend._find_chart_in_sheetU  s.  € ô* �t—|‘|Ô%:×;Ñ;Ø—‘×)×)àˆJØ×5Ñ5°eÓ<ˆà—‘ˆÞØˆJð Ÿ™×8Ñ8ˆÞ˜T×<Ñ<Ó>ÑFÜ�L‰LÔ+Ô,Ø!ˆMäˆEð@Ü!A×!EÑ!EØ—M‘MÔ#=×#IÑ#Ió"�ð  $×5Ñ5°eÓ<�Ø!×6Ñ6°uÓ=�
ä"×-Ò-Ø×)Ñ)¨%¯,©,Ó7Ü&×.Ñ.ñ�ð !�	Þ ð	Ø&*×&>Ñ&>¸uÓ&E˜Ø&Ñ2Ü(0×(9Ò(9ÀÐQSÑ(T˜Iö $ð —L‘LÜ*×2Ñ2Ø)Ø&3ð !ñ ð ð ð Ÿ/™/ØŸ;™;Ø#Ø(Ü'Ø 'Ø!'Ø!ñð
 #0ð *ð 
�ô  +Ü#Aä;À~ÑVð%ñ$ð &Ñ1ô .¸ÒDà!ñ �—ñe ðF ˆ
øô] %ó ÜŸ™ð,à%)ð %ó ðûôR ó Ü—
‘
ØDÈtð õ ðús8   Â3BHÅ	)G1Å2A8HÇ1HÈHÈHÈHÈH8È7H8c                óÂ  • U R                   nUc  g[        U[        5      (       a  U=(       d    S$ UR                  nUb  UR                  c  g/ nUR                  R
                   HM  nUR                  =(       d    /  H1  nUR                  (       d  M  UR                  UR                  5        M3     MO     SR                  U5      R                  5       nU=(       d    S$ )aÔ  Extract the plain-text title of an openpyxl chart, if any.

A chart title is stored as DrawingML rich text: ``chart.title`` is a
Title object whose ``.tx.rich.p`` is a list of paragraphs, each with a
list of runs ``.r``, each run carrying its text in ``.t``.  We flatten
all runs into a single string.  Returns None when the chart has no title.

Args:
    chart: An openpyxl chart object (BarChart, LineChart, ...).

Returns:
    The concatenated title text, or None.
Nrã   )rÝ   rŽ   r¢   ÚtxÚrichÚprÕ   r[  r€  r„  rœ  )r4  rÝ   r<  ÚrunsÚ	paragraphÚrunr£   s          r\   r-  Ú(MsExcelDocumentBackend._chart_title_text¿  s­   € ð —‘ˆØ‰=Øä�eœS×!Ñ!Ø—=˜DÐ Ø�X‰XˆØ‰:˜Ÿ™™ØàˆØŸ™ŸœˆIØ —{‘{×( bÒ(�Ø—5—5‘5Ø—K‘K §¡Ö&ó )ñ #ð �w‰w�t‹}×"Ñ"Ó$ˆØ�|�tÐrb   c                ó®  • [        UR                  5      nU(       d  g/ nU HZ  nU R                  UR                  5      =(       d    U R                  UR                  5      nU(       d  MI  U R                  U5      n  O   / nU Hô  nU R                  UR                  5      =(       d    U R                  UR                  5      nU(       a  U R                  U5      O/ nU R                  UR                  5      n	U	(       a   U R                  U	5      n
U
(       a  U
S   OSnOFUR                  b7  UR                  R                  b   [        UR                  R                  5      nOSnUR                  X¸45        Mö     [        [        U5      /U VVs/ s H  u  pÈ[        U5      PM     snn-   5      nUS:X  a  gUS-   nS[        U5      -   n/ nS/U VVs/ s H  u  p¼UPM	     snn-   n[        U5       H)  u  nnUR                  [        USSSSUUS-   SSS9	5        M+     [!        U5       H‹  nUS-   nU[        U5      :  a  UU   OSnU/U VVs/ s H  u  pÈU[        U5      :  a  UU   OSPM     snn-   n[        U5       H/  u  nnUR                  [        USSUUS-   UUS-   SUS:H  S9	5        M1     M�     [#        XïUS9$ s  snnf s  snnf s  snnf )	au  Reconstruct a chart's underlying data grid as a TableData.

Layout produced (categories down the first column, one column per series):

    | <blank> | <series 0 name> | <series 1 name> | ...
    | cat_0   | val_0,0         | val_1,0         | ...
    | cat_1   | val_0,1         | val_1,1         | ...

Chart data is stored in openpyxl as *references* back into the workbook
(e.g. "'Sheet1'!$B$2:$B$7"), which we resolve to cached cell values.
Scatter charts use ``xVal``/``yVal`` instead of ``cat``/``val``.

Args:
    chart: An openpyxl chart object.

Returns:
    A TableData, or None if the chart exposes no usable series.
Nr   rã   rk   TFrk  ri  )ÚlistÚseriesÚ_ref_formulaÚcatÚxValÚ_resolve_referenceÚvalÚyValr<  Úvr¢   r€  r†   r!  rõ   r   rÄ  r    )rm   r4  Úseries_listÚ
categoriesrE  Úcat_refÚcolumnsÚ	value_refr¤  Úname_refÚresolvedr  Ú_Únum_data_rowsrª   r«   ÚcellsÚheader_labelsÚcol_idxr;  Údata_rowÚrow_idxr·   Ú	row_textsr£   s                            r\   r.  Ú+MsExcelDocumentBackend._chart_to_table_dataà  s»  € ô& ˜5Ÿ<™<Ó(ˆÞØà "ˆ
Û!ˆFØ×'Ñ'¨¯
©
Ó3×U°t×7HÑ7HÈÏÉÓ7UˆGßˆwØ!×4Ñ4°WÓ=�
Ùñ	 "ð 02ˆã!ˆFØ×)Ñ)¨&¯*©*Ó5×W¸×9JÑ9JÈ6Ï;É;Ó9WˆIÞ;D�T×,Ñ,¨YÔ7È"ˆFà×(Ñ(¨¯©Ó3ˆHÞØ×2Ñ2°8Ó<�Þ&.�x ’{°B‘Ø—‘Ñ&¨6¯9©9¯;©;Ñ+BÜ˜6Ÿ9™9Ÿ;™;Ó'‘à�Ø�N‰N˜D˜>Ö*ñ "ô œS ›_Ð-ÉgÔ0VÊgÁÀ´°V¶ÉgÒ0VÑVÓWˆØ˜AÓØà  1Ñ$ˆØ”s˜7“|Ñ#ˆØ!#ˆà˜±GÔ<²G©¨£±GÒ<Ñ<ˆÜ'¨Ö6‰NˆG�UØ�L‰LÜØØØØ)*Ø'(Ø)0Ø'.°¡{Ø"&Ø$ñ
öñ 7ô ˜mÖ,ˆHØ ‘lˆGØ/7¼#¸j»/Ó/I�z (Ò+ÈrˆHØ!˜
á!(ô&â!(‘I�Að &.´°F³Ó%;�˜Ò!ÀÒCÙ!(ò&ñ ˆIô "+¨9Ö!5‘�˜Ø—‘ÜØ!Ø!"Ø!"Ø-4Ø+2°Q©;Ø-4Ø+2°Q©;Ø&+Ø$+¨q¡Lñ
öó "6ñ -ô,  (È5ÑQÐQùó[ 1Wùó  =ùó$&s   ÆKÇKÉ"K
c                óF   •  [        U 5      $ ! [        [        4 a     gf = f)zõParse a cell string into a float, or None if it is not numeric.

Cached values arrive as strings; a chart plots them only when they are
written back as numbers. Non-numeric cells (blank, labels) return None
so the caller can leave them as text.
N)rX  r  Ú
ValueError)r£   s    r\   Ú	_to_floatÚ MsExcelDocumentBackend._to_float=  s(   € ð	Ü˜“;ÐøÜœ:Ð&ó 	Ùð	ús   ‚
 � Ÿ c                óþ  • U R                  5       nUc  gU R                  U5      nUc  g[        [        5       5      n US-  nUS-  nUR	                  U5        U" XV5        UR                  5       (       d,  [        R                  S5         [        R                  " USS9  g[        R                  " [        U5      5      nUS   n[        UR                  SS	9R                  5       5      n	UR!                  5         UR!                  5         U	[        R                  " USS9  $ ! ["         a5  n
[        R                  S
U
5         Sn
A
[        R                  " USS9  gSn
A
ff = f! [        R                  " USS9  f = f)un  Render a native chart to an image via LibreOffice.

XLSX stores charts as vector definitions with no embedded raster.  To
obtain a picture we isolate the chart into a throwaway single-chart
workbook (its data copied onto hidden sheets so the series still
resolve), convert that to PDF with LibreOffice â€” the same external tool
already used for EMF/WMF images â€” and rasterize the first page with
pypdfium2, trimming the surrounding whitespace.

Args:
    chart: An openpyxl chart object.

Returns:
    A PIL Image, or None when LibreOffice is unavailable, the chart has
    no resolvable data, or the conversion fails.
Nz
chart.xlsxz	chart.pdfz.LibreOffice produced no PDF output for a chartTrà  r   r™  râ  z*Chart rendering via LibreOffice failed: %s)rÛ  Ú _build_standalone_chart_workbookr   r   r  rå  rî   ró   ræ  rç  rè  ré  r¢   r.   rê  rë  rì  rÌ   )rm   r4  rî  Ú
standalonerð  rñ  rò  ró  rJ  rô  rõ  s              r\   r/  Ú*MsExcelDocumentBackend._render_chart_imageJ  sA  € ð" ×3Ñ3Ó5ˆ	ØÑØà×:Ñ:¸5ÓAˆ
ØÑØäœ›	“?ˆð	8Ø! LÑ0ˆJØ" [Ñ0ˆKØ�O‰O˜JÔ'Ù�jÔ.Ø×%Ñ%×'Ñ'Ü—
‘
ÐKÔLØô �MŠM˜(°$Ó7ô ×'Ò'¬¨KÓ(8Ó9ˆCØ�q‘6ˆDÜ'¨¯©¸!¨Ð(<×(CÑ(CÓ(EÓFˆIØ�J‰JŒLØ�I‰IŒKØô
 �MŠM˜(°$Ó7øô	 ó 	Ü�J‰JÐCÀSÔIÛä�MŠM˜(°$Ó7ûð		ûô �MŠM˜(°$Ó7ús1   ¾AD# Â"A+D# Ä#
E"Ä-EÅE% ÅE"Å"E% Å%E<c                ó  • U R                   c  g/ nUR                   Hn  nUR                  UR                  UR                  UR
                  UR                  4 H.  nU R                  U5      nU(       d  M  UR                  U5        M0     Mp     U(       d  g[        5       nUR                  S   nSUl        U Vs/ s H  o€R                  Xh5      PM     n	n[        U	5      (       d  gUR                  [        U5      S5        U$ s  snf )u8  Build a one-chart workbook with the chart's data on hidden sheets.

A chart references its data by sheet-qualified ranges (e.g.
``"'Sheet1'!$B$2:$B$7"``).  Every referenced range is recreated here â€”
same sheet name, same cell coordinates, cached values â€” on hidden
sheets, and the chart is anchored alone on a single visible sheet.
LibreOffice prints only the visible sheet, so the resulting PDF holds
just the chart.

Args:
    chart: An openpyxl chart object.

Returns:
    A populated Workbook, or None when the chart exposes no resolvable
    data references.
Nr   Úchart_renderÚA1)rÅ   rE  rG  rH  rJ  rK  r<  rF  r€  r8   rö   rÝ   Ú_copy_reference_intor_   Ú	add_chartr   )
rm   r4  Ú
referencesrE  Údata_sourceÚformularc  Úchart_sheetrâ   Úcopieds
             r\   rb  Ú7MsExcelDocumentBackend._build_standalone_chart_workbookx  sð   € ð" �=‰=Ñ Øà "ˆ
Ø—l”lˆFà—
‘
Ø—‘Ø—
‘
Ø—‘Ø—	‘	ó �ð ×+Ñ+¨KÓ8�ß�7Ø×%Ñ% gÖ.ó ñ #ö Øä“Zˆ
ð !×+Ñ+¨AÑ.ˆØ*ˆÔñ ISÓSÊ
À×+Ñ+¨JÖ<É
ˆÐSÜ�6�{‰{Øð 	×Ñœh u›o¨tÔ4ØÐùò Ts   Â;Dc                óÜ  • U R                   b  SU;  a  gUR                  SS5      u  p4UR                  5       nUR                  S5      (       a+  UR	                  S5      (       a  USS R                  SS5      nX0R                   R                  ;  a  [        R                  SU5        g [        U5      n[        S	 U 5       5      (       a  [        R                  S
U5        g[        [        [        [        [        [        4   U5      u  pgp‰U R                   U   n
X1R                  ;   a  X   nOUR                  U5      nSUl        [#        XyS-   5       Hl  n[#        XhS-   5       HW  nU
R%                  XÍS9R&                  n[)        U[*        5      (       a  U R-                  U5      nUb  UnUR%                  XÍUS9  MY     Mn     g! [         a    [        R                  SU5         gf = f)aä  Copy one chart data range into ``target`` on a hidden sheet.

Recreates the referenced sheet by name if needed, hides it, and writes
the cached cell values at their original coordinates.  Numeric-looking
text is coerced to numbers so the chart plots it as values rather than
labels.

Args:
    target: The standalone Workbook being assembled.
    ref: A sheet-qualified range reference, e.g. "'Sheet1'!$B$2:$B$7".

Returns:
    True when the referenced range was resolved and copied.
rx  Frk   Ú'r¾  ú''ú!Chart references unknown sheet %rúCould not parse chart range %rc              3  ó(   #   • U  H  oS L v •  M
     g 7frÜ   rt   )rZ   Úbounds     r\   r]   Ú>MsExcelDocumentBackend._copy_reference_into.<locals>.<genexpr>Ê  s   é € Ð1ª& ˜�}ª&ùs   ‚z#Chart range %r is not fully boundedÚhiddenrv  )r�   r�   r¥  T)rÅ   Úrsplitrœ  rY   Úendswithrø   r"  rî   ró   rC   rÌ   r_   r   Útupler�   Úcreate_sheetÚsheet_staterÄ  r‘   r¥  rŽ   r¢   r_  )rm   rû  râ   Ú
sheet_partÚ
cell_ranger‡   rh   rf   ri   rg   Úsource_sheetÚ
dest_sheetr�   r™   r¥  Únumerics                   r\   rh  Ú+MsExcelDocumentBackend._copy_reference_into«  s¾  € ð �=‰=Ñ  C¨s£NØà!$§¡¨C°Ó!3Ñˆ
Ø×%Ñ%Ó'ˆ
Ø× Ñ  ×%Ñ%¨*×*=Ñ*=¸c×*BÑ*BØ# A bÐ)×1Ñ1°$¸Ó<ˆJØŸ]™]×5Ñ5Ó5Ü�J‰JÐ:¸JÔGØð	Ü% jÓ1ˆFô Ñ1©&Ó1×1Ñ1ä�J‰JÐ<¸jÔIØÜ-1´%¼¼SÄ#ÄsÐ8JÑ2KÈVÓ-TÑ*ˆ˜'à—}‘} ZÑ0ˆØ×*Ñ*Ó*ØÑ+‰Jà×,Ñ,¨ZÓ8ˆJØ%-ˆJÔ"ä˜¨A¡+Ö.ˆCÜ˜W°¡kÖ2�Ø$×)Ñ)¨cÐ)Ð>×DÑD�Ü˜e¤S×)Ñ)Ø"Ÿn™n¨UÓ3�GØÑ*Ø '˜Ø—‘ C¸5�ÓAó 3ñ /ð øô1 ó 	Ü�J‰JÐ7¸ÔDÙð	ús   Â+G Ç G+Ç*G+c                óÀ   • U c  g[        U SS5      nUb  UR                  (       a  UR                  $ [        U SS5      nUb  UR                  (       a  UR                  $ g)u[  Return the cell-range formula string from a chart data source.

A chart's series title/categories/values are each a small openpyxl
object (SeriesLabel, AxDataSource, NumDataSource) that may hold either a
numeric reference (``.numRef``) or a string reference (``.strRef``); both
expose the range formula on ``.f`` (e.g. "'Sheet1'!$B$2:$B$7").  These
objects don't share a common base exposing both attributes, so we probe
each â€” a narrowly-scoped getattr against a third-party API.

Args:
    data_source: A chart data-source object, or None.

Returns:
    The range-formula string, or None if absent.
NÚnumRefÚstrRef)rY  Úf)rk  Únum_refÚstr_refs      r\   rF  Ú#MsExcelDocumentBackend._ref_formulaá  sX   € ð" ÑØÜ˜+ x°Ó6ˆØÑ 7§9§9Ø—9‘9ÐÜ˜+ x°Ó6ˆØÑ 7§9§9Ø—9‘9ÐØrb   c                óø  • U R                   c  / $ SU;   ah  UR                  SS5      u  p#UR                  5       nUR                  S5      (       a+  UR	                  S5      (       a  USS R                  SS5      nUnO!U R                   R                  R                  Up4X@R                   R                  ;  a  [        R                  SU5        / $ U R                   U   n [        U5      u  pgp‰/ n
[        XyS-   5       HP  n[        XhS-   5       H;  nUR                  X¼S9R                  nU
R!                  Uc  S	O
[#        U5      5        M=     MR     U
$ ! [         a    [        R                  SU5        / s $ f = f)
aÑ  Resolve a chart range reference to a flat list of cell-value strings.

Charts point at their data by reference rather than embedding it, e.g.
``"'Duck Observations'!$B$2:$B$7"``.  We split off the (possibly quoted)
sheet name, convert the range to bounds with openpyxl's
``range_boundaries``, and read the *cached* values from the workbook
(the backend loads with ``data_only=True``, so we get computed numbers,
not formulas).  Values are returned in row-major order.

Args:
    ref: A range reference string, optionally sheet-qualified.

Returns:
    The referenced cell values as strings ("" for empty cells).  Returns
    an empty list if the sheet is missing (e.g. filtered out) or the
    reference can't be parsed.
rx  rk   rq  r¾  rr  rs  rt  rv  rã   )rÅ   ry  rœ  rY   rz  rø   ÚactiverÝ   r"  rî   ró   rC   rÌ   rÄ  r‘   r¥  r€  r¢   )rm   râ   r~  r  rà   Útarget_sheetrh   rf   ri   rg   r¤  r�   r™   r¥  s                 r\   rI  Ú)MsExcelDocumentBackend._resolve_referenceü  s_  € ð$ �=‰=Ñ ØˆIà�#‹:Ø%(§Z¡Z°°QÓ%7Ñ"ˆJØ#×)Ñ)Ó+ˆJØ×$Ñ$ S×)Ñ)¨j×.AÑ.AÀ#×.FÑ.FØ'¨¨"Ð-×5Ñ5°d¸CÓ@�
Ø#‰Jà%)§]¡]×%9Ñ%9×%?Ñ%?À˜
ØŸ]™]×5Ñ5Ó5Ü�J‰JÐ:¸JÔGØˆIà—}‘} ZÑ0ˆð	Ü1AÀ*Ó1MÑ.ˆG˜gð
 ˆÜ˜¨A¡+Ö.ˆCÜ˜W°¡kÖ2�Ø$×)Ñ)¨cÐ)Ð>×DÑD�Ø—‘ E¡M™b´s¸5³zÖBó 3ñ /ð
 ˆøô ó 	Ü�J‰JÐ7¸ÔDØŠIð	ús   Ã E Å"E9Å8E9c                óR  • SnSnU R                  SU[        [        5      S9 H  u  pE[        U[        5      (       d  M  UR
                   HS  nUR                  U:w  a  M  [        X&R                  R                  5      n[        X6R                  R                  5      nMU     M�     X#4$ )u—  Return (width, height) for the given page in cell-index units.

Width and height are the maximum ``r`` and ``b`` bbox coordinates seen
across all items on the page, regardless of content layer.  Because
bboxes use ``CoordOrigin.TOPLEFT`` with the sheet origin at ``(0, 0)``,
the page extent equals the largest right/bottom value â€” not the span
between the leftmost/topmost and rightmost/bottommost edges.
g        T)Útraverse_picturesr9  Úincluded_content_layers)Úiterate_itemsrD  r   rŽ   r   rV  r9  r†   rZ  rÕ   Úb)r3  r9  rn   rr   r\  rT  Ú
provenances          r\   rC  Ú&MsExcelDocumentBackend._find_page_size-  s›   € ð ˆØˆØ×(Ñ(Ø"ØÜ$'¬Ó$5ð )ó 
‰GˆDô
 ˜d¤G×,Ñ,ÙØ"Ÿiœi�
Ø×%Ñ%¨Ó0ÙÜ˜E§?¡?×#4Ñ#4Ó5�Ü˜V§_¡_×%6Ñ%6Ó7’ó	 (ñ
ð ˆÐrb   c                óh  • UR                  US9 HŸ  u  pV[        U[        5      (       d  M  UR                   Hs  nUR                  U:w  a  M  UR
                  nUR                  Us=::  a  UR                  :  d  MD  O  MH  UR                  Us=::  a  UR                  :  d  Mk  O  Mo  Us  s  $    M¡     g)a   Find the DocItem (table cell or text) at the given row/col position.

Args:
    doc: The DoclingDocument to search.
    page_no: The page number to search in.
    row: Row index (0-based).
    col: Column index (0-based).

Returns:
    The DocItem at that position, or None if not found.
)r9  N)
r’  rŽ   r   rV  r9  rZ  ÚlrÕ   r[  r“  )	rm   r3  r9  r�   r™   r\  rT  rV  rZ  s	            r\   r…  Ú&MsExcelDocumentBackend._find_cell_itemJ  s�   € ð ×(Ñ(°Ð(Ó9‰GˆDÜ˜d¤G×,Ñ,ÙàŸ	œ	�Ø—<‘< 7Ó*Ùà—y‘y�à—6‘6˜SÕ) 4§6¡6×)Ñ)¨d¯f©f¸Õ.D¸d¿f¹f×.DÑ.DØ”Kó "ñ	 :ð rb   c                ób   • U R                   [        R                  :X  a  S $ [        R                  $ rÜ   )r}  rD   ÚSHEETSTATE_VISIBLEr   Ú	INVISIBLE)rˆ   s    r\   rA  Ú/MsExcelDocumentBackend._get_sheet_content_layerg  s2   € ð × Ñ ¤I×$@Ñ$@Ó@ð ð	
ô ×'Ñ'ð	
rb   )rÁ   rÂ   rË   rÅ   rÃ   rÄ   rÜ   )rÎ   r3   rÊ   zBytesIO | PathrÏ   zMsExcelBackendOptions | Noneru   rœ   )rà   r¢   ru   z+dict[str, tuple[str, str, datetime | None]])ru   r5   )ru   r�   )ru   zset[InputFormat])ru   r   )r3  r   ru   r   )r3  r   rˆ   zWorksheet | Chartsheetr9  r�   ru   r   )r3  r   r9  r�   ru   rœ   )r3  r   rˆ   rD   r9  r�   ru   r   )r�  r¨   ru   z#tuple[ExcelCell | None, ExcelTable])rˆ   rD   r¦  r~   ru   rd   )rˆ   rD   ru   zPtuple[list[ExcelTable], dict[tuple[int, int], tuple[str, str, datetime | None]]])rˆ   rD   r®  r�   r¯  r�   rg   r�   ri   r�   r¦  r~   ru   z'tuple[ExcelTable, set[tuple[int, int]]])rŠ   r
   ru   ztuple[int, int, int, int])ru   zCallable | None)rí  Úbytesru   úPILImage.Image | None)r3  r   rÿ  r   r   r¢   r9  r�   r<  úContentLayer | Noneru   r   )r4  r
   ru   ú
str | None)r4  r
   ru   zTableData | None)r£   r¢   ru   zfloat | None)r4  r
   ru   rž  )r4  r
   ru   zWorkbook | None)rû  r8   râ   r¢   ru   r5   )rk  r
   ru   r   )râ   r¢   ru   ú	list[str])r3  r   r9  r(   ru   ztuple[float, float])
r3  r   r9  r�   r�   r�   r™   r�   ru   zDocItem | None)rˆ   rD   ru   rŸ  ).rv   rw   rx   ry   rz   r°   r{   r±   r*   r‹   r  r  Úclassmethodr  r$  r(  r4  r1  rB  rQ  rN  r{  r©  ry  r±  rž   rØ  rÛ  rö  r  rü  rO  rP  r-  r.  r_  r/  rb  rh  rF  rI  rC  r…  rA  r|   Ú__classcell__)rÑ   s   @r\   r®   r®   ù   s¬  ø‡ ñð: &6Ð˜
Ó5Ø#3Ð�jÓ3àð
 15ð	AàðAð 'ðAð .ð	Að
 
÷Aó ðAðFg!Øðg!à	4ôg!ðR óó ðð Øóó ó ðð óó ðð Øó3ó ó ð3ð óó ðô6?ðBØ"ðØ+AðØLOðà	ôð.0Ø"ð0Ø-0ð0à	ô0ð>TØ"ðTØ+4ðTØ?BðTà	ôTðl0
Øð0
à	,ô0
ðd1>Øð1>Ø3Cð1>à	ô1>ðfL#ØðL#ð
ôL#ð\Q
àðQ
ð ð	Q
ð
 ðQ
ð ðQ
ð ðQ
ð ,ðQ
ð 
1ôQ
ðf óó ðô2*ô('8ðR.Ø"ð.Ø+4ð.Ø?Bð.à	ô.ð`JàðJð ðJð ð	Jð
 ðJð +ðJð 
ôJðX+Ø"ð+Ø+4ð+Ø?Bð+à	ô+ðZhØ"ðhØ+AðhØLOðhà	ôhðT óó ðô@[Rðz ó
ó ð
ô,8ô\1ôf4ðl óó ðô4/ðb ðØðØ'2ðà	óó ðð8Ø"ðØ-0ðØ7:ðØADðà	ôð: ó
ó ö
rb   r®   )r`   r¡  ru   r5   )|Ú
__future__r   rÀ  ÚloggingÚ	posixpathræ  rÆ   Úcopyr   r   Úior   Úpathlibr   Útempfiler   Útypingr	   r
   r   r   r   Úzipfiler   rè  Údocling_core.types.docr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   Úlxmlr"   ÚPILr#   r  r$   Úpydanticr%   r&   r'   r(   Úpydantic.dataclassesr)   Útyping_extensionsr*   Ú docling.backend.abstract_backendr+   r,   Ú$docling.backend.docx.drawingml.utilsr-   r.   r/   Ú!docling.datamodel.backend_optionsr0   Údocling.datamodel.base_modelsr1   r2   Údocling.datamodel.documentr3   Údocling.exceptionsr4   Ú	getLoggerrv   rî   r6   r{   r7   Úopenpyxlr8   r9   Úopenpyxl.cell.cellr:   r;   Úopenpyxl.chartsheet.chartsheetr<   Úopenpyxl.drawing.imageÚ$openpyxl.drawing.spreadsheet_drawingr=   r>   r?   Úopenpyxl.packaging.relationshipr@   rA   Úopenpyxl.stylesrB   Úopenpyxl.utils.cellrC   Úopenpyxl.worksheet.worksheetrD   Úopenpyxl.xml.constantsrE   r»   rÐ   r¼   Ú	XMLParserrJ   r*  Ú	BAR_CHARTÚ
LINE_CHARTÚ	PIE_CHARTÚSCATTER_CHARTr,  rU   ra   rd   r~   r    r¨   r®   rt   rb   r\   Ú<module>rÉ     sñ  ðö #ã Û Û Û Û Ý Ý Ý Ý Ý ß 8Õ 8Ý ã ÷÷ ÷ ÷ ÷ ñ õ* ß 9ß BÓ BÝ *Ý &÷÷ñ õ
 Dß GÝ 4Ý 0à×Ò˜Ó"€à!Ð �TÓ !Ø-1Ð Ð*Ó 1ðß0ß3Ý9Ý,÷ñ ÷
 NÝ+Ý4Ý6Ý/àÐð
Að ð  Ÿ/š/ØØØØñ	Ð �%ó ðð ð +×4Ñ4Ø,×6Ñ6Ø+×6Ñ6Ø-×8Ñ8Ø*×4Ñ4Ø,×6Ñ6Ø/×9Ñ9Ø.×<Ñ<Ø+×7Ñ7Ø-×9Ñ9ñRÐ  Ð"Nó ôAð
 ÷/ð /ó ð/÷29:ñ 9:ôx�	ô ô$�ô ô"t
Ð7Ð9Qõ t
øðO ó ØÕûðús   Ã/AH
 È
HÈHÈH