docling-rs 2.2.2__tar.gz → 2.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of docling-rs might be problematic. Click here for more details.

Files changed (257) hide show
  1. {docling_rs-2.2.2 → docling_rs-2.2.4}/PKG-INFO +1 -1
  2. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/Cargo.toml +4 -4
  3. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/cfb.rs +176 -7
  4. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/doc.rs +0 -36
  5. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/html.rs +25 -2
  6. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/html_tree.rs +14 -13
  7. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/mhtml.rs +0 -11
  8. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/ppt.rs +0 -44
  9. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/pptx.rs +0 -46
  10. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/quattro.rs +0 -22
  11. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/wps.rs +0 -17
  12. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xls.rs +0 -22
  13. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/Cargo.toml +3 -3
  14. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/Cargo.toml +1 -1
  15. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/markdown.rs +0 -2
  16. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-onnx/Cargo.toml +2 -2
  17. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/Cargo.toml +3 -3
  18. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/ocr_prep.rs +15 -4
  19. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/tesseract.rs +17 -11
  20. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/text_layer.rs +3 -8
  21. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/Cargo.lock +5 -5
  22. {docling_rs-2.2.2 → docling_rs-2.2.4}/pyproject.toml +1 -1
  23. {docling_rs-2.2.2 → docling_rs-2.2.4}/README.md +0 -0
  24. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/README.md +0 -0
  25. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/examples/convert.rs +0 -0
  26. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/examples/perf_serialize.rs +0 -0
  27. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/examples/profile_declarative.rs +0 -0
  28. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/examples/stream.rs +0 -0
  29. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/sample.html +0 -0
  30. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/sample.md +0 -0
  31. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/archive.rs +0 -0
  32. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/abw.rs +0 -0
  33. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/asciidoc.rs +0 -0
  34. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/browser.rs +0 -0
  35. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/chandra.rs +0 -0
  36. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/csv.rs +0 -0
  37. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/deepseek.rs +0 -0
  38. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/djvu.rs +0 -0
  39. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/doclang.rs +0 -0
  40. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/doclang_tree.rs +0 -0
  41. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/docling_json.rs +0 -0
  42. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/docx.rs +0 -0
  43. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/docx_tree.rs +0 -0
  44. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/ebcdic.rs +0 -0
  45. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/ebcdic_tables.rs +0 -0
  46. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/email.rs +0 -0
  47. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/epub.rs +0 -0
  48. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/images.rs +0 -0
  49. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/interchange.rs +0 -0
  50. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/iwork.rs +0 -0
  51. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/iwork_charts.rs +0 -0
  52. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/jats.rs +0 -0
  53. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/keynote.rs +0 -0
  54. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/keynote_iwa.rs +0 -0
  55. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/keynote_xml.rs +0 -0
  56. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/latex.rs +0 -0
  57. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/latex_walker.rs +0 -0
  58. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/lotus.rs +0 -0
  59. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/markdown.rs +0 -0
  60. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/mc.rs +0 -0
  61. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/md_tree.rs +0 -0
  62. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/metafile.rs +0 -0
  63. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/mod.rs +0 -0
  64. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/msg.rs +0 -0
  65. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/odf.rs +0 -0
  66. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/odf_tree.rs +0 -0
  67. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/officeart.rs +0 -0
  68. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/omml.rs +0 -0
  69. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/ooxml.rs +0 -0
  70. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/pages.rs +0 -0
  71. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/pages_iwa.rs +0 -0
  72. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/pages_xml.rs +0 -0
  73. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/rtf.rs +0 -0
  74. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/staroffice5.rs +0 -0
  75. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/svg.rs +0 -0
  76. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/symbol_fonts.rs +0 -0
  77. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/uspto.rs +0 -0
  78. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/uspto_entities.rs +0 -0
  79. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/visio.rs +0 -0
  80. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/webvtt.rs +0 -0
  81. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/wpd.rs +0 -0
  82. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xbrl.rs +0 -0
  83. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xbrl_dts.rs +0 -0
  84. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xlsx.rs +0 -0
  85. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xlsx_drawings.rs +0 -0
  86. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/backend/xml_depth.rs +0 -0
  87. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/chunks.rs +0 -0
  88. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/converter.rs +0 -0
  89. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/dclx.rs +0 -0
  90. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/email_attachments.rs +0 -0
  91. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/error.rs +0 -0
  92. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/format.rs +0 -0
  93. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/lib.rs +0 -0
  94. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/options.rs +0 -0
  95. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/result.rs +0 -0
  96. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/sniff.rs +0 -0
  97. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/source.rs +0 -0
  98. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/stream.rs +0 -0
  99. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/video.rs +0 -0
  100. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling/src/vlm.rs +0 -0
  101. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/README.md +0 -0
  102. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/examples/preset_check.rs +0 -0
  103. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/audio.rs +0 -0
  104. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/lib.rs +0 -0
  105. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/mel.rs +0 -0
  106. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/nemo_mel.rs +0 -0
  107. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/parakeet.rs +0 -0
  108. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/tokenizer.rs +0 -0
  109. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/vad.rs +0 -0
  110. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-asr/src/whisper.rs +0 -0
  111. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/README.md +0 -0
  112. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/assets.rs +0 -0
  113. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/base64.rs +0 -0
  114. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/chunker.rs +0 -0
  115. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/confidence.rs +0 -0
  116. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/doclang.rs +0 -0
  117. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/doctags.rs +0 -0
  118. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/document.rs +0 -0
  119. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/env.rs +0 -0
  120. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/html.rs +0 -0
  121. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/jpeg.rs +0 -0
  122. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/json.rs +0 -0
  123. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/labels.rs +0 -0
  124. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/latex.rs +0 -0
  125. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/lib.rs +0 -0
  126. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/mathml/mod.rs +0 -0
  127. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/mathml/symbols.rs +0 -0
  128. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/pandoc.rs +0 -0
  129. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/pixel_digest.rs +0 -0
  130. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-core/src/tree.rs +0 -0
  131. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-onnx/README.md +0 -0
  132. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-onnx/src/lib.rs +0 -0
  133. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/README.md +0 -0
  134. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/dump_page_png.rs +0 -0
  135. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/dump_regions.rs +0 -0
  136. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/dump_stages.rs +0 -0
  137. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/extract.rs +0 -0
  138. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/layout.rs +0 -0
  139. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/render_compare.rs +0 -0
  140. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/snapshot.rs +0 -0
  141. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/text_layer.rs +0 -0
  142. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/textparse_dump.rs +0 -0
  143. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/textparse_glyphs.rs +0 -0
  144. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/tf_otsl.rs +0 -0
  145. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/examples/word_cells.rs +0 -0
  146. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/ffi/docling-parse-render/dparse_render.cpp +0 -0
  147. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/assemble.rs +0 -0
  148. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/checkbox.rs +0 -0
  149. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/dp_lines.rs +0 -0
  150. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/dparse_render.rs +0 -0
  151. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/enrich.rs +0 -0
  152. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/font_style.rs +0 -0
  153. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/heading_hierarchy.rs +0 -0
  154. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/layout.rs +0 -0
  155. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/lib.rs +0 -0
  156. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/mets.rs +0 -0
  157. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/ocr.rs +0 -0
  158. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/ocr_det.rs +0 -0
  159. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/orient.rs +0 -0
  160. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/outline.rs +0 -0
  161. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/pdf_meta.rs +0 -0
  162. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/pdfium_backend.rs +0 -0
  163. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/quality.rs +0 -0
  164. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/raster/fax.rs +0 -0
  165. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/raster/filters.rs +0 -0
  166. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/raster/jpeg_tests.rs +0 -0
  167. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/raster/mod.rs +0 -0
  168. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/raster/stretch.rs +0 -0
  169. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/reading_order.rs +0 -0
  170. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/color.rs +0 -0
  171. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/content.rs +0 -0
  172. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/cmap.rs +0 -0
  173. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/dotsection.rs +0 -0
  174. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/encodings.rs +0 -0
  175. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/fallback.rs +0 -0
  176. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/mod.rs +0 -0
  177. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/font/type1.rs +0 -0
  178. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/function.rs +0 -0
  179. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/geom.rs +0 -0
  180. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/image.rs +0 -0
  181. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/jpx.rs +0 -0
  182. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/mod.rs +0 -0
  183. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/objects.rs +0 -0
  184. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/render/prepass.rs +0 -0
  185. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/resample.rs +0 -0
  186. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/scanned.rs +0 -0
  187. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/std14.rs +0 -0
  188. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/tableformer.rs +0 -0
  189. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/textparse.rs +0 -0
  190. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/tf_core.rs +0 -0
  191. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/tf_match.rs +0 -0
  192. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/src/timing.rs +0 -0
  193. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/fax/shapes.g3 +0 -0
  194. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/fax/shapes.g3_2d +0 -0
  195. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/fax/shapes.g4 +0 -0
  196. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/fax/shapes.png +0 -0
  197. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/gray_444.jpg +0 -0
  198. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/gray_444.png +0 -0
  199. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/gray_progressive.jpg +0 -0
  200. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/gray_progressive.png +0 -0
  201. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420.jpg +0 -0
  202. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420.png +0 -0
  203. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_big.jpg +0 -0
  204. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_big.png +0 -0
  205. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_progressive.jpg +0 -0
  206. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_progressive.png +0 -0
  207. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_restart.jpg +0 -0
  208. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_420_restart.png +0 -0
  209. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_422.jpg +0 -0
  210. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_422.png +0 -0
  211. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_422_optimize.jpg +0 -0
  212. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_422_optimize.png +0 -0
  213. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_444.jpg +0 -0
  214. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_444.png +0 -0
  215. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_444_progressive.jpg +0 -0
  216. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpeg/rgb_444_progressive.png +0 -0
  217. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpx/gray_12x9.j2k +0 -0
  218. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpx/gray_12x9.jp2 +0 -0
  219. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpx/rgb_8x6.jp2 +0 -0
  220. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-pdf/tests/data/jpx/rgba_8x6.jp2 +0 -0
  221. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/Cargo.toml +0 -0
  222. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/README.md +0 -0
  223. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/examples/langchain/README.md +0 -0
  224. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/examples/langchain/loader_basics.py +0 -0
  225. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/examples/langchain/picture_description.py +0 -0
  226. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/examples/langchain/rag_agent.py +0 -0
  227. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/src/lib.rs +0 -0
  228. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/data/langchain/README.md +0 -0
  229. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/data/langchain/dl_doc_1.json +0 -0
  230. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/data/langchain/lc_doc_chunks_1.json +0 -0
  231. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/data/langchain/lc_doc_md_1.json +0 -0
  232. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_archive.py +0 -0
  233. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_chunking.py +0 -0
  234. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_config.py +0 -0
  235. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_dropin.py +0 -0
  236. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_email.py +0 -0
  237. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_langchain.py +0 -0
  238. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_namespace_parity.py +0 -0
  239. {docling_rs-2.2.2 → docling_rs-2.2.4}/docling-py/tests/test_pandoc.py +0 -0
  240. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/__init__.py +0 -0
  241. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/chunking.py +0 -0
  242. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/datamodel/__init__.py +0 -0
  243. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/datamodel/accelerator_options.py +0 -0
  244. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/datamodel/base_models.py +0 -0
  245. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/datamodel/document.py +0 -0
  246. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/datamodel/pipeline_options.py +0 -0
  247. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/document_converter.py +0 -0
  248. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/exceptions.py +0 -0
  249. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/langchain/__init__.py +0 -0
  250. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/langchain/loader.py +0 -0
  251. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/langchain/picture_description.py +0 -0
  252. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/models.py +0 -0
  253. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/options.py +0 -0
  254. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/pandoc.py +0 -0
  255. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/picture_description.py +0 -0
  256. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/utils/__init__.py +0 -0
  257. {docling_rs-2.2.2 → docling_rs-2.2.4}/python/docling_rs/utils/model_downloader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docling-rs
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "docling"
3
3
  description = "DocumentConverter and format backends for docling.rs (a Rust port of docling)."
4
- version = "2.2.2"
4
+ version = "2.2.4"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -79,9 +79,9 @@ headless_chrome = { version = "1.0", optional = true }
79
79
  # O_EXCL temp file with an unpredictable name for the browser pre-render page
80
80
  # (avoids a predictable /tmp path a local attacker could pre-plant a symlink at).
81
81
  tempfile = { version = "3", optional = true }
82
- docling-asr = { path = "../docling-asr", version = "2.2.2", optional = true }
83
- docling-core = { path = "../docling-core", version = "2.2.2" }
84
- docling-pdf = { path = "../docling-pdf", version = "2.2.2", optional = true, default-features = false }
82
+ docling-asr = { path = "../docling-asr", version = "2.2.4", optional = true }
83
+ docling-core = { path = "../docling-core", version = "2.2.4" }
84
+ docling-pdf = { path = "../docling-pdf", version = "2.2.4", optional = true, default-features = false }
85
85
  image = { version = "0.25", default-features = false, features = ["png", "jpeg", "gif", "bmp", "tiff", "webp"] }
86
86
  mail-parser = "0.11"
87
87
  pulldown-cmark = { version = "0.13.4", default-features = false }
@@ -8,9 +8,10 @@
8
8
  //! bounded by the sector count (no cycle can loop forever) and stream sizes
9
9
  //! are capped by the same per-part budget as OOXML parts.
10
10
  //!
11
- //! Layout ([MS-CFB]): a 512-byte header names the first sectors of the DIFAT
12
- //! (which locates the FAT), the directory chain, and the mini FAT. Streams
13
- //! ≥ `mini_stream_cutoff` (4096) chain through the FAT; smaller ones live in
11
+ //! Layout ([MS-CFB]): a 512-byte header (zero-padded to a whole 4096-byte
12
+ //! sector in a version 4 file) names the first sectors of the DIFAT (which
13
+ //! locates the FAT), the directory chain, and the mini FAT. Streams ≥
14
+ //! `mini_stream_cutoff` (4096) chain through the FAT; smaller ones live in
14
15
  //! the *mini stream* (the root entry's stream) and chain through the mini FAT
15
16
  //! in 64-byte mini sectors.
16
17
 
@@ -313,9 +314,14 @@ impl<'a> CompoundFile<'a> {
313
314
  }
314
315
  }
315
316
 
316
- /// Byte offset of sector `n` (sector 0 starts right after the 512-byte header).
317
+ /// Byte offset of sector `n`. The header occupies the file's first sector
318
+ /// ([MS-CFB] 2.2): 512 bytes in a version 3 file, and in a version 4 file
319
+ /// (4096-byte sectors) the same 512 bytes zero-padded to a whole sector, so
320
+ /// sector 0 starts at 4096 there — `512 + n * 4096` read every v4 structure
321
+ /// 3584 bytes early and rejected valid files as damaged (#623; PowerPoint
322
+ /// writes v4 when an edited .ppt is saved in place).
317
323
  fn sector_offset(n: u32, sector_size: usize) -> usize {
318
- 512 + n as usize * sector_size
324
+ sector_size.max(512) + n as usize * sector_size
319
325
  }
320
326
 
321
327
  /// Follow a FAT chain from `start`, concatenating sectors, truncated to `size`
@@ -547,6 +553,166 @@ mod tests {
547
553
  );
548
554
  }
549
555
 
556
+ /// Rewrite a version 3 compound file as version 4 (#623): a 4096-byte
557
+ /// header sector, 4096-byte sectors, every FAT-chained stream, the mini
558
+ /// stream, the mini FAT and the directory re-chained in that order, the
559
+ /// FAT sector last. The directory gets a padding sector of unused entries
560
+ /// so its chain spans two sectors, and with `difat_sector` the FAT
561
+ /// sector is listed in a DIFAT sector instead of the header — a layout
562
+ /// only files of >109 FAT sectors (≈450 MB at 4 KiB) need, so it is the
563
+ /// only way a test reaches that read. Mini sectors stay 64 bytes in both
564
+ /// versions: the mini stream and mini FAT are copied verbatim.
565
+ fn to_v4(v3: &[u8], difat_sector: bool) -> Vec<u8> {
566
+ const SZ: usize = 4096;
567
+ let cfb = CompoundFile::open(v3).expect("valid v3 CFB");
568
+ assert_eq!(cfb.sector_size, 512, "source must be version 3");
569
+ let mut dir = read_chain(v3, &cfb.fat, u32_at(v3, 48).unwrap(), 512, u64::MAX).unwrap();
570
+ let mut body: Vec<u8> = Vec::new();
571
+ let mut fat: Vec<u32> = Vec::new();
572
+ let mut put = |bytes: &[u8], fat: &mut Vec<u32>| -> (u32, u32) {
573
+ let (start, n) = (fat.len() as u32, bytes.len().div_ceil(SZ).max(1) as u32);
574
+ fat.extend((1..=n).map(|i| if i == n { ENDOFCHAIN } else { start + i }));
575
+ body.extend_from_slice(bytes);
576
+ body.resize(fat.len() * SZ, 0);
577
+ (start, n)
578
+ };
579
+ let set = |dir: &mut Vec<u8>, i: usize, start: u32| {
580
+ dir[i * 128 + 116..i * 128 + 120].copy_from_slice(&start.to_le_bytes());
581
+ dir[i * 128 + 124..i * 128 + 128].fill(0); // v4 sizes are 64-bit
582
+ };
583
+ for (i, e) in cfb.entries.iter().enumerate() {
584
+ if e.object_type == 2 && e.size >= 4096 {
585
+ let (start, _) = put(&cfb.stream_by_index(i).unwrap(), &mut fat);
586
+ set(&mut dir, i, start);
587
+ } else if e.object_type == 5 && !cfb.mini_stream.is_empty() {
588
+ let (start, _) = put(&cfb.mini_stream, &mut fat);
589
+ set(&mut dir, i, start);
590
+ } else {
591
+ set(&mut dir, i, e.start_sector);
592
+ }
593
+ }
594
+ let mini_fat: Vec<u8> = cfb.mini_fat.iter().flat_map(|v| v.to_le_bytes()).collect();
595
+ let (mfat_start, mfat_n) = put(&mini_fat, &mut fat);
596
+ let entries = dir.len() / 128;
597
+ dir.resize((entries.div_ceil(32) + 1) * SZ, 0); // unused entries: name length 0
598
+ let (dir_start, dir_n) = put(&dir, &mut fat);
599
+ // The FAT itself: its own sector (and the DIFAT sector) marked, one
600
+ // 1024-entry sector covers every test file.
601
+ let fat_sector = fat.len() as u32;
602
+ fat.push(0xFFFF_FFFD); // FATSECT
603
+ if difat_sector {
604
+ fat.push(0xFFFF_FFFC); // DIFSECT
605
+ }
606
+ assert!(
607
+ fat.len() <= SZ / 4,
608
+ "test file too large for one FAT sector"
609
+ );
610
+ let mut fat_bytes: Vec<u8> = fat.iter().flat_map(|v| v.to_le_bytes()).collect();
611
+ fat_bytes.resize(SZ, 0xFF);
612
+ body.extend_from_slice(&fat_bytes);
613
+ let mut header = vec![0u8; SZ];
614
+ header[..8].copy_from_slice(&HEADER_MAGIC);
615
+ header[24..26].copy_from_slice(&0x003Eu16.to_le_bytes());
616
+ header[26..28].copy_from_slice(&4u16.to_le_bytes()); // major 4
617
+ header[28..30].copy_from_slice(&0xFFFEu16.to_le_bytes());
618
+ header[30..32].copy_from_slice(&12u16.to_le_bytes()); // 4096-byte sectors
619
+ header[32..34].copy_from_slice(&6u16.to_le_bytes());
620
+ header[40..44].copy_from_slice(&dir_n.to_le_bytes()); // v4 only
621
+ header[44..48].copy_from_slice(&1u32.to_le_bytes());
622
+ header[48..52].copy_from_slice(&dir_start.to_le_bytes());
623
+ header[56..60].copy_from_slice(&4096u32.to_le_bytes());
624
+ header[60..64].copy_from_slice(&mfat_start.to_le_bytes());
625
+ header[64..68].copy_from_slice(&mfat_n.to_le_bytes());
626
+ header[76..512].fill(0xFF);
627
+ if difat_sector {
628
+ let mut difat = vec![0xFFu8; SZ];
629
+ difat[..4].copy_from_slice(&fat_sector.to_le_bytes());
630
+ difat[SZ - 4..].copy_from_slice(&ENDOFCHAIN.to_le_bytes());
631
+ body.extend_from_slice(&difat);
632
+ header[68..72].copy_from_slice(&(fat_sector + 1).to_le_bytes());
633
+ header[72..76].copy_from_slice(&1u32.to_le_bytes());
634
+ } else {
635
+ header[68..72].copy_from_slice(&ENDOFCHAIN.to_le_bytes());
636
+ header[76..80].copy_from_slice(&fat_sector.to_le_bytes());
637
+ }
638
+ header.extend_from_slice(&body);
639
+ header
640
+ }
641
+
642
+ /// #623: version 4 files (4096-byte sectors, the header padded to one
643
+ /// sector) read every stream exactly as their version 3 source — FAT-
644
+ /// chained streams, the mini stream, a two-sector directory, a DIFAT
645
+ /// sector — and convert to the same document through each CFB backend
646
+ /// (.xls goes through calamine's own reader).
647
+ #[test]
648
+ fn v4_files_read_like_their_v3_source() {
649
+ use crate::{DocumentConverter, InputFormat, SourceDocument};
650
+ let own = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/");
651
+ let mirror = concat!(env!("CARGO_MANIFEST_DIR"), "/../../tests/data/");
652
+ let md = |name: &str, bytes: Vec<u8>| {
653
+ let ext = name.rsplit('.').next().unwrap();
654
+ let fmt = InputFormat::from_extension(ext).unwrap();
655
+ DocumentConverter::new()
656
+ .convert(SourceDocument::from_bytes(name, fmt, bytes))
657
+ .unwrap_or_else(|e| panic!("{name}: {e}"))
658
+ .document
659
+ .export_to_markdown()
660
+ };
661
+ for path in [
662
+ format!("{mirror}doc/sources/docx_lists.doc"),
663
+ format!("{own}doc/sources/embedded_word_object.doc"),
664
+ format!("{mirror}ppt/sources/powerpoint_sample.ppt"),
665
+ format!("{mirror}xls/sources/xlsx_05_table_with_title.xls"),
666
+ format!("{own}quattro/sources/formatcorpus_test.qpw"),
667
+ format!("{own}staroffice5/sources/Writer_3.1.sdw"),
668
+ ] {
669
+ let name = path.rsplit('/').next().unwrap();
670
+ let v3 = std::fs::read(&path).unwrap();
671
+ let want_streams = all_streams(&CompoundFile::open(&v3).unwrap());
672
+ let want_md = md(name, v3.clone());
673
+ assert!(!want_md.trim().is_empty(), "{name}: empty baseline");
674
+ for difat in [false, true] {
675
+ let v4 = to_v4(&v3, difat);
676
+ let cfb = CompoundFile::open(&v4)
677
+ .unwrap_or_else(|| panic!("{name} v4 (difat {difat}): open failed"));
678
+ assert_eq!(cfb.sector_size, 4096);
679
+ assert!(
680
+ all_streams(&cfb) == want_streams,
681
+ "{name} v4 (difat {difat}): streams differ"
682
+ );
683
+ assert_eq!(md(name, v4), want_md, "{name} v4 (difat {difat})");
684
+ }
685
+ }
686
+ }
687
+
688
+ /// The PowerPoint-written version 4 file of #623 (an edited .ppt saved
689
+ /// in place) opens, and losing its last sector — the end of the
690
+ /// `\u{5}SummaryInformation` stream — is still reported as damage to
691
+ /// that stream, not read as a shorter one.
692
+ #[test]
693
+ fn real_v4_file_opens_and_its_truncation_is_damage() {
694
+ let data = std::fs::read(concat!(
695
+ env!("CARGO_MANIFEST_DIR"),
696
+ "/tests/data/ppt/sources/ppt_cfb_v4_edit_save.ppt"
697
+ ))
698
+ .unwrap();
699
+ assert_eq!(u16_at(&data, 26), Some(4), "fixture must be version 4");
700
+ let cfb = CompoundFile::open(&data).expect("valid v4 CFB");
701
+ assert_eq!(cfb.sector_size, 4096);
702
+ let len = |cfb: &CompoundFile, name| cfb.stream(name).map(|s| s.len());
703
+ assert_eq!(len(&cfb, "PowerPoint Document"), Some(35685));
704
+ assert_eq!(len(&cfb, "Current User"), Some(32)); // a mini stream entry
705
+ assert_eq!(len(&cfb, "\u{5}SummaryInformation"), Some(43648));
706
+ let short = &data[..data.len() - 4096];
707
+ let cfb = CompoundFile::open(short).expect("directory and FAT intact");
708
+ assert_eq!(len(&cfb, "PowerPoint Document"), Some(35685));
709
+ assert_eq!(len(&cfb, "\u{5}SummaryInformation"), None);
710
+ assert_eq!(
711
+ cfb.stream_error("ppt", "\u{5}SummaryInformation"),
712
+ "ppt: \u{5}SummaryInformation stream unreadable (file truncated or corrupt)"
713
+ );
714
+ }
715
+
550
716
  #[test]
551
717
  fn truncated_header_is_rejected_not_panicked() {
552
718
  let data = std::fs::read(concat!(
@@ -554,9 +720,12 @@ mod tests {
554
720
  "/../../tests/data/doc/sources/docx_lists.doc"
555
721
  ))
556
722
  .unwrap();
557
- // Every truncation point must fail cleanly, never panic.
723
+ // Every truncation point must fail cleanly — `None`, never a panic.
558
724
  for cut in [8, 76, 512, 700] {
559
- let _ = CompoundFile::open(&data[..cut.min(data.len())]);
725
+ assert!(
726
+ CompoundFile::open(&data[..cut.min(data.len())]).is_none(),
727
+ "cut at {cut}"
728
+ );
560
729
  }
561
730
  }
562
731
  }
@@ -2396,42 +2396,6 @@ mod tests {
2396
2396
  assert_eq!(furniture, [("page_header", "MARK_HEADER_TEXTBOX_TEXT")]);
2397
2397
  }
2398
2398
 
2399
- #[test]
2400
- fn extracts_headings_lists_and_paragraphs() {
2401
- let doc = DocBackend
2402
- .convert(&fixture("docx_lists.doc"))
2403
- .expect("converts");
2404
- let headings = doc
2405
- .nodes
2406
- .iter()
2407
- .filter(|n| matches!(n, Node::Heading { .. }))
2408
- .count();
2409
- let lists = doc
2410
- .nodes
2411
- .iter()
2412
- .filter(|n| matches!(n, Node::ListItem { .. }))
2413
- .count();
2414
- assert!(headings > 0, "expected headings: {:?}", doc.nodes);
2415
- assert!(lists > 0, "expected list items: {:?}", doc.nodes);
2416
- }
2417
-
2418
- #[test]
2419
- fn extracts_tables_with_cells() {
2420
- let doc = DocBackend
2421
- .convert(&fixture("docx_rich_tables_01.doc"))
2422
- .expect("converts");
2423
- let tables: Vec<&Table> = doc
2424
- .nodes
2425
- .iter()
2426
- .filter_map(|n| match n {
2427
- Node::Table(t) => Some(t),
2428
- _ => None,
2429
- })
2430
- .collect();
2431
- assert!(!tables.is_empty(), "expected tables: {:?}", doc.nodes);
2432
- assert!(tables[0].rows.len() > 1 && tables[0].rows[0].len() > 1);
2433
- }
2434
-
2435
2399
  /// #567: prose is Markdown-escaped like the DOCX backend's — `OBJ_DIR`
2436
2400
  /// becomes `OBJ\_DIR` in the Markdown (the reporter's Word 2.133 and
2437
2401
  /// DOCX outputs), and the JSON export restores the raw text.
@@ -2240,14 +2240,37 @@ fn is_row_header(cells: &[ElementRef]) -> bool {
2240
2240
  /// HTML5's "rowspan=0 spans to the end of the row group" is not modelled,
2241
2241
  /// the cell simply keeps one slot).
2242
2242
  fn span_attr(cell: ElementRef, name: &str) -> usize {
2243
- let Some(v) = cell.value().attr(name) else {
2243
+ let limit = if name == "colspan" {
2244
+ MAX_COLSPAN
2245
+ } else {
2246
+ MAX_ROWSPAN
2247
+ };
2248
+ span_value(cell.value().attr(name), limit)
2249
+ }
2250
+
2251
+ /// docling's `MAX_COLSPAN` / `MAX_ROWSPAN` (`backend/utils/table_spans.py`,
2252
+ /// HTML's own limits): a declared span past them is clamped, so a
2253
+ /// `colspan="999999999"` cannot make the grid builder allocate billions of
2254
+ /// slots.
2255
+ pub(super) const MAX_COLSPAN: usize = 1000;
2256
+ pub(super) const MAX_ROWSPAN: usize = 65_534;
2257
+
2258
+ /// One span attribute as `_get_cell_spans` reads it — [`span_attr`]'s rule,
2259
+ /// clamped to `[1, limit]` (`clamp_span`); a digit run too long to parse is
2260
+ /// over every limit, so it is the limit. Shared with the JSON item tree
2261
+ /// (`html_tree`), which must see the same grid.
2262
+ pub(super) fn span_value(raw: Option<&str>, limit: usize) -> usize {
2263
+ let Some(v) = raw else {
2244
2264
  return 1;
2245
2265
  };
2246
2266
  if !v.starts_with(|c: char| c.is_numeric()) {
2247
2267
  return 1;
2248
2268
  }
2249
2269
  let digits: String = v.chars().take_while(|c| c.is_ascii_digit()).collect();
2250
- digits.parse::<usize>().map_or(1, |n| n.max(1))
2270
+ match digits.trim_start_matches('0') {
2271
+ "" => 1,
2272
+ d => d.parse::<usize>().map_or(limit, |n| n.clamp(1, limit)),
2273
+ }
2251
2274
  }
2252
2275
 
2253
2276
  /// Render a table cell to Markdown. docling treats a cell as "rich" (and
@@ -303,18 +303,15 @@ fn has_list_ancestor(elem: NodeRef<'_, HtmlNode>, boundary: NodeRef<'_, HtmlNode
303
303
  }
304
304
 
305
305
  /// upstream's `_get_cell_spans`: `colspan` / `rowspan`, each the leading
306
- /// digit run when the attribute starts with a digit, else 1 (so `"0"` is 0).
306
+ /// digit run when the attribute starts with a digit, else 1, clamped to
307
+ /// `[1, MAX_*SPAN]` — the same reading as the Markdown grid (`html.rs`), so
308
+ /// a `rowspan="0"` keeps its slot in the JSON too (docling#4287, 2.129).
307
309
  fn cell_spans(cell: ElementRef) -> (usize, usize) {
308
- let num = |attr: &str| -> usize {
309
- let raw = cell.value().attr(attr).unwrap_or("1");
310
- if raw.starts_with(|c: char| c.is_ascii_digit()) {
311
- let digits: String = raw.chars().take_while(|c| c.is_ascii_digit()).collect();
312
- digits.parse().unwrap_or(1)
313
- } else {
314
- 1
315
- }
316
- };
317
- (num("colspan"), num("rowspan"))
310
+ use super::html::{span_value, MAX_COLSPAN, MAX_ROWSPAN};
311
+ (
312
+ span_value(cell.value().attr("colspan"), MAX_COLSPAN),
313
+ span_value(cell.value().attr("rowspan"), MAX_ROWSPAN),
314
+ )
318
315
  }
319
316
 
320
317
  /// An `href` as docling's `AnnotatedText.hyperlink` serializes it: a URL
@@ -1968,11 +1965,15 @@ mod tests {
1968
1965
  #[test]
1969
1966
  fn cell_spans_follow_get_cell_spans() {
1970
1967
  let html = Html::parse_fragment(
1971
- r#"<table><tr><td colspan="3x" rowspan="0">a</td><td rowspan="x2">b</td></tr></table>"#,
1968
+ r#"<table><tr><td colspan="3x" rowspan="0">a</td><td rowspan="x2">b</td>
1969
+ <td colspan="99999999999999999999999" rowspan="70000">c</td></tr></table>"#,
1972
1970
  );
1973
1971
  let cells: Vec<ElementRef> = html.select(sel!("td")).collect();
1974
- assert_eq!(cell_spans(cells[0]), (3, 0));
1972
+ // A zero span keeps one slot (docling#4287), not none.
1973
+ assert_eq!(cell_spans(cells[0]), (3, 1));
1975
1974
  assert_eq!(cell_spans(cells[1]), (1, 1));
1975
+ // Past HTML's limits: clamped, even past `usize`.
1976
+ assert_eq!(cell_spans(cells[2]), (1000, 65_534));
1976
1977
  }
1977
1978
 
1978
1979
  /// The shape upstream gives a small page: the `<title>` and the prelude
@@ -641,17 +641,6 @@ mod tests {
641
641
  assert!(md(data).contains("Привет, мир"));
642
642
  }
643
643
 
644
- #[test]
645
- fn real_blink_fixture_decodes_quoted_printable_html() {
646
- // docling's `tests/data/mhtml/sources/example.mhtml`, mirrored (#386).
647
- let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
648
- .join("../../tests/data/mhtml/sources/example.mhtml");
649
- let bytes = std::fs::read(path).expect("mirrored upstream fixture");
650
- let markdown = md(bytes);
651
- assert!(markdown.contains("# Example Domain"), "{markdown}");
652
- assert!(markdown.contains("[Learn more](https://iana.org/domains/example)"));
653
- }
654
-
655
644
  #[test]
656
645
  fn embedded_images_resolve_by_location_and_cid() {
657
646
  let parts = png_part(
@@ -792,50 +792,6 @@ mod tests {
792
792
  use super::*;
793
793
  use crate::InputFormat;
794
794
 
795
- fn fixture(name: &str) -> SourceDocument {
796
- let path = format!(
797
- "{}/../../tests/data/ppt/sources/{name}",
798
- env!("CARGO_MANIFEST_DIR")
799
- );
800
- let bytes = std::fs::read(&path).expect("fixture exists");
801
- SourceDocument::from_bytes(name, InputFormat::Ppt, bytes)
802
- }
803
-
804
- #[test]
805
- fn extracts_slide_titles_and_text() {
806
- let doc = PptBackend
807
- .convert(&fixture("powerpoint_sample.ppt"))
808
- .expect("converts");
809
- let headings = doc
810
- .nodes
811
- .iter()
812
- .filter(|n| matches!(n, Node::Heading { .. }))
813
- .count();
814
- assert!(headings > 0, "expected slide titles: {:?}", doc.nodes);
815
- }
816
-
817
- #[test]
818
- fn reconstructs_grouped_shapes_into_a_table() {
819
- let doc = PptBackend
820
- .convert(&fixture("powerpoint_sample.ppt"))
821
- .expect("converts");
822
- let tables: Vec<&Table> = doc
823
- .nodes
824
- .iter()
825
- .filter_map(|n| match n {
826
- Node::Table(t) => Some(t),
827
- _ => None,
828
- })
829
- .collect();
830
- assert!(!tables.is_empty(), "expected a reconstructed table");
831
- let t = tables[0];
832
- assert!(
833
- t.rows.len() >= 2 && t.rows[0].len() >= 2,
834
- "grid too small: {:?}",
835
- t.rows
836
- );
837
- }
838
-
839
795
  #[test]
840
796
  fn grid_table_rejects_a_column_of_shapes() {
841
797
  // Four stacked shapes (one column) must NOT read as a table.
@@ -1299,52 +1299,6 @@ fn cell_text(tc: XmlNode) -> String {
1299
1299
  fn descendant<'a, 'input>(node: XmlNode<'a, 'input>, name: &str) -> Option<XmlNode<'a, 'input>> {
1300
1300
  node.descendants().find(|n| n.has_tag_name(name))
1301
1301
  }
1302
- #[cfg(test)]
1303
- mod chart_tests {
1304
- use super::*;
1305
- use crate::backend::DeclarativeBackend;
1306
- use crate::{InputFormat, SourceDocument};
1307
-
1308
- /// docling PR #3794: a native chart frame becomes a classified chart with
1309
- /// its cached data grid and the chart title as the caption.
1310
- #[test]
1311
- fn native_chart_yields_classified_data_grid() {
1312
- let path = format!(
1313
- "{}/../../tests/data/pptx/sources/pptx_chart.pptx",
1314
- env!("CARGO_MANIFEST_DIR")
1315
- );
1316
- let bytes = std::fs::read(&path).expect("fixture exists");
1317
- let src = SourceDocument::from_bytes("c.pptx", InputFormat::Pptx, bytes);
1318
- let doc = PptxBackend.convert(&src).expect("converts");
1319
- let chart = doc
1320
- .nodes
1321
- .iter()
1322
- // Slide content hangs off the slide's own group (#402).
1323
- .flat_map(|n| match n {
1324
- Node::Group { children, .. } => children.as_slice(),
1325
- other => std::slice::from_ref(other),
1326
- })
1327
- .find_map(|n| match n {
1328
- Node::Chart {
1329
- kind,
1330
- table,
1331
- caption,
1332
- ..
1333
- } => Some((kind.clone(), table.clone(), caption.clone())),
1334
- _ => None,
1335
- })
1336
- .expect("a chart node");
1337
- assert_eq!(chart.0, "bar_chart");
1338
- assert_eq!(
1339
- chart.2.as_deref(),
1340
- Some("Wild Duck Observations by Year"),
1341
- "chart title as caption"
1342
- );
1343
- assert_eq!(chart.1.rows[0][1], "Freshwater Ducks");
1344
- assert_eq!(chart.1.rows[1], vec!["2019", "120", "80"]);
1345
- }
1346
- }
1347
-
1348
1302
  #[cfg(test)]
1349
1303
  mod list_marker_tests {
1350
1304
  use super::{list_kind, Placeholder};
@@ -707,26 +707,4 @@ mod tests {
707
707
  assert_eq!(g[&(3, 1)], "1.5");
708
708
  assert_eq!(g[&(4, 1)], "-2");
709
709
  }
710
-
711
- #[test]
712
- fn corpus_samples_convert() {
713
- let dir =
714
- std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/data/quattro/sources");
715
- for (f, needle) in [
716
- ("formatcorpus_ksbase.wq1", "OBSERV"),
717
- (
718
- "formatcorpus_ks4000.wq2",
719
- "SATURATED HYDRAULIC CONDUCTIVITY",
720
- ),
721
- ("formatcorpus_test.wb1", "| X |"),
722
- ("formatcorpus_test.wb2", "| X |"),
723
- ("formatcorpus_test.wb3", "This is an example spreadsheet"),
724
- ("formatcorpus_test.qpw", "This is an example spreadsheet"),
725
- ] {
726
- let bytes = std::fs::read(dir.join(f)).unwrap();
727
- let doc = convert(bytes).unwrap_or_else(|e| panic!("{f}: {e}"));
728
- let md = md(&doc);
729
- assert!(md.contains(needle), "{f}:\n{md}");
730
- }
731
- }
732
710
  }
@@ -1105,21 +1105,4 @@ mod tests {
1105
1105
  .to_string();
1106
1106
  assert!(e.contains("not a Microsoft Works document"), "{e}");
1107
1107
  }
1108
-
1109
- #[test]
1110
- fn libreoffice_fixtures_parse() {
1111
- // LibreOffice's libwps smoke files are near-empty documents; they
1112
- // must at least parse and detect their generation.
1113
- let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/data/works/sources");
1114
- for f in [
1115
- "works2_dos.wps",
1116
- "works3.wps",
1117
- "works45.wps",
1118
- "works5.wps",
1119
- "works6.wps",
1120
- ] {
1121
- let bytes = std::fs::read(dir.join(f)).unwrap();
1122
- convert(bytes).unwrap_or_else(|e| panic!("{f}: {e}"));
1123
- }
1124
- }
1125
1108
  }
@@ -124,28 +124,6 @@ mod tests {
124
124
  use super::*;
125
125
  use crate::InputFormat;
126
126
 
127
- fn fixture(name: &str) -> SourceDocument {
128
- let path = format!(
129
- "{}/../../tests/data/xls/sources/{name}",
130
- env!("CARGO_MANIFEST_DIR")
131
- );
132
- let bytes = std::fs::read(&path).expect("fixture exists");
133
- SourceDocument::from_bytes(name, InputFormat::Xls, bytes)
134
- }
135
-
136
- #[test]
137
- fn parses_xls_tables_like_the_xlsx_twin() {
138
- let doc = XlsBackend::default()
139
- .convert(&fixture("xlsx_01.xls"))
140
- .expect("converts");
141
- let tables: Vec<_> = doc
142
- .nodes
143
- .iter()
144
- .filter(|n| matches!(n, Node::Table(_)))
145
- .collect();
146
- assert!(!tables.is_empty(), "expected tables, got: {:?}", doc.nodes);
147
- }
148
-
149
127
  #[test]
150
128
  fn garbage_is_an_error_not_a_panic() {
151
129
  let src = SourceDocument::from_bytes("x.xls", InputFormat::Xls, vec![0u8; 64]);
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "docling-asr"
3
3
  description = "Audio/ASR pipeline for docling.rs (Whisper via ONNX; a Rust port of docling's ASR pipeline)."
4
- version = "2.2.2"
4
+ version = "2.2.4"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -25,10 +25,10 @@ xnnpack = ["docling-onnx/xnnpack"]
25
25
  ort-load-dynamic = ["docling-onnx/load-dynamic", "ort/load-dynamic"]
26
26
 
27
27
  [dependencies]
28
- docling-core = { path = "../docling-core", version = "2.2.2" }
28
+ docling-core = { path = "../docling-core", version = "2.2.4" }
29
29
  # Shared ONNX execution-provider selection (DOCLING_RS_EP, #288) — routes the
30
30
  # Whisper encoder/decoder sessions through the same switch as docling-pdf.
31
- docling-onnx = { path = "../docling-onnx", version = "2.2.2" }
31
+ docling-onnx = { path = "../docling-onnx", version = "2.2.4" }
32
32
  # Same ort as docling-pdf so the workspace keeps a single ONNX runtime.
33
33
  ort = { version = "2.0.0-rc.13", default-features = false, features = ["std", "download-binaries", "tls-rustls"] }
34
34
  serde_json = "1"
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "docling-core"
3
3
  description = "Core DoclingDocument data model and serializers for docling.rs (a Rust port of docling)."
4
- version = "2.2.2"
4
+ version = "2.2.4"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -2628,7 +2628,6 @@ mod tests {
2628
2628
  /// buffered export.
2629
2629
  #[test]
2630
2630
  fn streamer_matches_buffered_export_with_options() {
2631
- use crate::document::ContentLayer;
2632
2631
  let doc = options_doc();
2633
2632
  let options = MarkdownExportOptions {
2634
2633
  layers: ContentLayers::ALL,
@@ -2651,7 +2650,6 @@ mod tests {
2651
2650
  buffered.contains("(img)") && buffered.contains("R&D _ report"),
2652
2651
  "{buffered}"
2653
2652
  );
2654
- let _ = ContentLayer::Notes;
2655
2653
  }
2656
2654
 
2657
2655
  /// #613: the plain export of a document holding every decoration the
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "docling-onnx"
3
3
  description = "Shared ONNX Runtime execution-provider selection for docling.rs (DOCLING_RS_EP)."
4
- version = "2.2.2"
4
+ version = "2.2.4"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -41,7 +41,7 @@ xnnpack = ["ort/xnnpack"]
41
41
  load-dynamic = ["ort/load-dynamic", "dep:libloading"]
42
42
 
43
43
  [dependencies]
44
- docling-core = { path = "../docling-core", version = "2.2.2" }
44
+ docling-core = { path = "../docling-core", version = "2.2.4" }
45
45
  # Same ort build as docling-pdf/docling-asr so the workspace keeps a single
46
46
  # ONNX Runtime.
47
47
  ort = { version = "2.0.0-rc.13", default-features = false, features = ["std", "download-binaries", "tls-rustls"] }