docling-rs 2.2.0__tar.gz → 2.2.2__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.0 → docling_rs-2.2.2}/PKG-INFO +1 -1
  2. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/Cargo.toml +4 -4
  3. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/error.rs +24 -1
  4. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/Cargo.toml +3 -3
  5. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/Cargo.toml +1 -1
  6. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/chunker.rs +2 -0
  7. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/doclang.rs +14 -0
  8. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/document.rs +29 -0
  9. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/json.rs +44 -0
  10. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/latex.rs +3 -0
  11. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/markdown.rs +1 -0
  12. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-onnx/Cargo.toml +2 -2
  13. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/Cargo.toml +3 -3
  14. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/assemble.rs +32 -10
  15. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/lib.rs +34 -0
  16. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/pdfium_backend.rs +5 -3
  17. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/text_layer.rs +12 -3
  18. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/Cargo.lock +5 -5
  19. {docling_rs-2.2.0 → docling_rs-2.2.2}/pyproject.toml +1 -1
  20. {docling_rs-2.2.0 → docling_rs-2.2.2}/README.md +0 -0
  21. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/README.md +0 -0
  22. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/examples/convert.rs +0 -0
  23. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/examples/perf_serialize.rs +0 -0
  24. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/examples/profile_declarative.rs +0 -0
  25. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/examples/stream.rs +0 -0
  26. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/sample.html +0 -0
  27. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/sample.md +0 -0
  28. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/archive.rs +0 -0
  29. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/abw.rs +0 -0
  30. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/asciidoc.rs +0 -0
  31. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/browser.rs +0 -0
  32. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/cfb.rs +0 -0
  33. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/chandra.rs +0 -0
  34. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/csv.rs +0 -0
  35. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/deepseek.rs +0 -0
  36. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/djvu.rs +0 -0
  37. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/doc.rs +0 -0
  38. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/doclang.rs +0 -0
  39. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/doclang_tree.rs +0 -0
  40. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/docling_json.rs +0 -0
  41. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/docx.rs +0 -0
  42. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/docx_tree.rs +0 -0
  43. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/ebcdic.rs +0 -0
  44. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/ebcdic_tables.rs +0 -0
  45. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/email.rs +0 -0
  46. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/epub.rs +0 -0
  47. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/html.rs +0 -0
  48. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/html_tree.rs +0 -0
  49. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/images.rs +0 -0
  50. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/interchange.rs +0 -0
  51. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/iwork.rs +0 -0
  52. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/iwork_charts.rs +0 -0
  53. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/jats.rs +0 -0
  54. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/keynote.rs +0 -0
  55. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/keynote_iwa.rs +0 -0
  56. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/keynote_xml.rs +0 -0
  57. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/latex.rs +0 -0
  58. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/latex_walker.rs +0 -0
  59. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/lotus.rs +0 -0
  60. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/markdown.rs +0 -0
  61. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/mc.rs +0 -0
  62. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/md_tree.rs +0 -0
  63. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/metafile.rs +0 -0
  64. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/mhtml.rs +0 -0
  65. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/mod.rs +0 -0
  66. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/msg.rs +0 -0
  67. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/odf.rs +0 -0
  68. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/odf_tree.rs +0 -0
  69. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/officeart.rs +0 -0
  70. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/omml.rs +0 -0
  71. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/ooxml.rs +0 -0
  72. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/pages.rs +0 -0
  73. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/pages_iwa.rs +0 -0
  74. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/pages_xml.rs +0 -0
  75. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/ppt.rs +0 -0
  76. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/pptx.rs +0 -0
  77. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/quattro.rs +0 -0
  78. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/rtf.rs +0 -0
  79. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/staroffice5.rs +0 -0
  80. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/svg.rs +0 -0
  81. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/symbol_fonts.rs +0 -0
  82. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/uspto.rs +0 -0
  83. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/uspto_entities.rs +0 -0
  84. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/visio.rs +0 -0
  85. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/webvtt.rs +0 -0
  86. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/wpd.rs +0 -0
  87. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/wps.rs +0 -0
  88. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xbrl.rs +0 -0
  89. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xbrl_dts.rs +0 -0
  90. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xls.rs +0 -0
  91. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xlsx.rs +0 -0
  92. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xlsx_drawings.rs +0 -0
  93. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/backend/xml_depth.rs +0 -0
  94. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/chunks.rs +0 -0
  95. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/converter.rs +0 -0
  96. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/dclx.rs +0 -0
  97. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/email_attachments.rs +0 -0
  98. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/format.rs +0 -0
  99. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/lib.rs +0 -0
  100. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/options.rs +0 -0
  101. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/result.rs +0 -0
  102. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/sniff.rs +0 -0
  103. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/source.rs +0 -0
  104. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/stream.rs +0 -0
  105. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/video.rs +0 -0
  106. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling/src/vlm.rs +0 -0
  107. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/README.md +0 -0
  108. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/examples/preset_check.rs +0 -0
  109. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/audio.rs +0 -0
  110. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/lib.rs +0 -0
  111. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/mel.rs +0 -0
  112. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/nemo_mel.rs +0 -0
  113. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/parakeet.rs +0 -0
  114. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/tokenizer.rs +0 -0
  115. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/vad.rs +0 -0
  116. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-asr/src/whisper.rs +0 -0
  117. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/README.md +0 -0
  118. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/assets.rs +0 -0
  119. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/base64.rs +0 -0
  120. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/confidence.rs +0 -0
  121. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/doctags.rs +0 -0
  122. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/env.rs +0 -0
  123. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/html.rs +0 -0
  124. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/jpeg.rs +0 -0
  125. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/labels.rs +0 -0
  126. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/lib.rs +0 -0
  127. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/mathml/mod.rs +0 -0
  128. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/mathml/symbols.rs +0 -0
  129. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/pandoc.rs +0 -0
  130. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/pixel_digest.rs +0 -0
  131. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-core/src/tree.rs +0 -0
  132. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-onnx/README.md +0 -0
  133. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-onnx/src/lib.rs +0 -0
  134. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/README.md +0 -0
  135. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/dump_page_png.rs +0 -0
  136. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/dump_regions.rs +0 -0
  137. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/dump_stages.rs +0 -0
  138. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/extract.rs +0 -0
  139. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/layout.rs +0 -0
  140. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/render_compare.rs +0 -0
  141. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/snapshot.rs +0 -0
  142. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/text_layer.rs +0 -0
  143. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/textparse_dump.rs +0 -0
  144. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/textparse_glyphs.rs +0 -0
  145. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/tf_otsl.rs +0 -0
  146. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/examples/word_cells.rs +0 -0
  147. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/ffi/docling-parse-render/dparse_render.cpp +0 -0
  148. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/checkbox.rs +0 -0
  149. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/dp_lines.rs +0 -0
  150. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/dparse_render.rs +0 -0
  151. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/enrich.rs +0 -0
  152. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/font_style.rs +0 -0
  153. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/heading_hierarchy.rs +0 -0
  154. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/layout.rs +0 -0
  155. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/mets.rs +0 -0
  156. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/ocr.rs +0 -0
  157. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/ocr_det.rs +0 -0
  158. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/ocr_prep.rs +0 -0
  159. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/orient.rs +0 -0
  160. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/outline.rs +0 -0
  161. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/pdf_meta.rs +0 -0
  162. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/quality.rs +0 -0
  163. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/raster/fax.rs +0 -0
  164. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/raster/filters.rs +0 -0
  165. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/raster/jpeg_tests.rs +0 -0
  166. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/raster/mod.rs +0 -0
  167. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/raster/stretch.rs +0 -0
  168. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/reading_order.rs +0 -0
  169. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/color.rs +0 -0
  170. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/content.rs +0 -0
  171. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/cmap.rs +0 -0
  172. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/dotsection.rs +0 -0
  173. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/encodings.rs +0 -0
  174. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/fallback.rs +0 -0
  175. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/mod.rs +0 -0
  176. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/font/type1.rs +0 -0
  177. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/function.rs +0 -0
  178. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/geom.rs +0 -0
  179. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/image.rs +0 -0
  180. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/jpx.rs +0 -0
  181. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/mod.rs +0 -0
  182. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/objects.rs +0 -0
  183. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/render/prepass.rs +0 -0
  184. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/resample.rs +0 -0
  185. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/scanned.rs +0 -0
  186. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/std14.rs +0 -0
  187. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/tableformer.rs +0 -0
  188. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/tesseract.rs +0 -0
  189. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/textparse.rs +0 -0
  190. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/tf_core.rs +0 -0
  191. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/tf_match.rs +0 -0
  192. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/src/timing.rs +0 -0
  193. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/fax/shapes.g3 +0 -0
  194. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/fax/shapes.g3_2d +0 -0
  195. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/fax/shapes.g4 +0 -0
  196. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/fax/shapes.png +0 -0
  197. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/gray_444.jpg +0 -0
  198. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/gray_444.png +0 -0
  199. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/gray_progressive.jpg +0 -0
  200. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/gray_progressive.png +0 -0
  201. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420.jpg +0 -0
  202. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420.png +0 -0
  203. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_big.jpg +0 -0
  204. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_big.png +0 -0
  205. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_progressive.jpg +0 -0
  206. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_progressive.png +0 -0
  207. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_restart.jpg +0 -0
  208. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_420_restart.png +0 -0
  209. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_422.jpg +0 -0
  210. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_422.png +0 -0
  211. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_422_optimize.jpg +0 -0
  212. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_422_optimize.png +0 -0
  213. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_444.jpg +0 -0
  214. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_444.png +0 -0
  215. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_444_progressive.jpg +0 -0
  216. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpeg/rgb_444_progressive.png +0 -0
  217. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpx/gray_12x9.j2k +0 -0
  218. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpx/gray_12x9.jp2 +0 -0
  219. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpx/rgb_8x6.jp2 +0 -0
  220. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-pdf/tests/data/jpx/rgba_8x6.jp2 +0 -0
  221. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/Cargo.toml +0 -0
  222. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/README.md +0 -0
  223. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/examples/langchain/README.md +0 -0
  224. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/examples/langchain/loader_basics.py +0 -0
  225. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/examples/langchain/picture_description.py +0 -0
  226. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/examples/langchain/rag_agent.py +0 -0
  227. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/src/lib.rs +0 -0
  228. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/data/langchain/README.md +0 -0
  229. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/data/langchain/dl_doc_1.json +0 -0
  230. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/data/langchain/lc_doc_chunks_1.json +0 -0
  231. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/data/langchain/lc_doc_md_1.json +0 -0
  232. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_archive.py +0 -0
  233. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_chunking.py +0 -0
  234. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_config.py +0 -0
  235. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_dropin.py +0 -0
  236. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_email.py +0 -0
  237. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_langchain.py +0 -0
  238. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_namespace_parity.py +0 -0
  239. {docling_rs-2.2.0 → docling_rs-2.2.2}/docling-py/tests/test_pandoc.py +0 -0
  240. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/__init__.py +0 -0
  241. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/chunking.py +0 -0
  242. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/datamodel/__init__.py +0 -0
  243. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/datamodel/accelerator_options.py +0 -0
  244. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/datamodel/base_models.py +0 -0
  245. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/datamodel/document.py +0 -0
  246. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/datamodel/pipeline_options.py +0 -0
  247. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/document_converter.py +0 -0
  248. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/exceptions.py +0 -0
  249. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/langchain/__init__.py +0 -0
  250. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/langchain/loader.py +0 -0
  251. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/langchain/picture_description.py +0 -0
  252. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/models.py +0 -0
  253. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/options.py +0 -0
  254. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/pandoc.py +0 -0
  255. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/picture_description.py +0 -0
  256. {docling_rs-2.2.0 → docling_rs-2.2.2}/python/docling_rs/utils/__init__.py +0 -0
  257. {docling_rs-2.2.0 → docling_rs-2.2.2}/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.0
3
+ Version: 2.2.2
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.0"
4
+ version = "2.2.2"
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.0", optional = true }
83
- docling-core = { path = "../docling-core", version = "2.2.0" }
84
- docling-pdf = { path = "../docling-pdf", version = "2.2.0", optional = true, default-features = false }
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 }
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 }
@@ -81,7 +81,14 @@ impl fmt::Display for ConversionError {
81
81
  ConversionError::Panic(msg) => write!(f, "conversion panicked: {msg}"),
82
82
  ConversionError::Timeout(msg) => write!(f, "document timeout: {msg}"),
83
83
  ConversionError::WithSource { context, source } => {
84
- write!(f, "parse error: {context}: {source}")
84
+ // A source that already names its format (`PdfError`'s
85
+ // `pdf: …`) is not prefixed twice (`pdf: pdf: …`).
86
+ let msg = source.to_string();
87
+ if msg.starts_with(&format!("{context}: ")) {
88
+ write!(f, "parse error: {msg}")
89
+ } else {
90
+ write!(f, "parse error: {context}: {msg}")
91
+ }
85
92
  }
86
93
  }
87
94
  }
@@ -121,6 +128,22 @@ mod tests {
121
128
  );
122
129
  }
123
130
 
131
+ /// A source whose message already names the context is not prefixed
132
+ /// with it again (`parse error: pdf: pdf: …`).
133
+ #[test]
134
+ fn with_source_does_not_repeat_the_context() {
135
+ let cause = std::io::Error::other("pdf: not a readable PDF");
136
+ assert_eq!(
137
+ ConversionError::with_source("pdf", cause).to_string(),
138
+ "parse error: pdf: not a readable PDF"
139
+ );
140
+ let cause = std::io::Error::other("bad header");
141
+ assert_eq!(
142
+ ConversionError::with_source("pdf", cause).to_string(),
143
+ "parse error: pdf: bad header"
144
+ );
145
+ }
146
+
124
147
  #[test]
125
148
  fn stringly_variants_have_no_source() {
126
149
  assert!(ConversionError::Parse("x".into()).source().is_none());
@@ -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.0"
4
+ version = "2.2.2"
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.0" }
28
+ docling-core = { path = "../docling-core", version = "2.2.2" }
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.0" }
31
+ docling-onnx = { path = "../docling-onnx", version = "2.2.2" }
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.0"
4
+ version = "2.2.2"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -471,6 +471,7 @@ impl Walker<'_> {
471
471
  let self_ref = self.alloc.text();
472
472
  self.emit_inline(text, self_ref);
473
473
  }
474
+ Node::LabeledText { .. } => self.one(&node.labeled_as_paragraph()),
474
475
  Node::CheckboxItem { checked, text } => {
475
476
  let self_ref = self.alloc.text();
476
477
  let mark = if *checked { "- [x] " } else { "- [ ] " };
@@ -1120,6 +1121,7 @@ fn cell_chunk_text(t: &Table, r: usize, c: usize) -> String {
1120
1121
  fn block_chunk_text(node: &Node) -> String {
1121
1122
  match node {
1122
1123
  Node::Paragraph { text } => unescape_text(text),
1124
+ Node::LabeledText { .. } => block_chunk_text(&node.labeled_as_paragraph()),
1123
1125
  Node::InlineGroup { md_text, .. } => unescape_text(md_text),
1124
1126
  Node::Code { text, .. } => format!("```\n{}\n```", unescape_text(text)),
1125
1127
  Node::Table(inner) => triplet_table_text(inner),
@@ -901,6 +901,17 @@ fn emit_nodes(out: &mut Out, depth: i32, nodes: &[Node], i: &mut usize, level: u
901
901
  emit_caption(out, depth, text, href.as_deref());
902
902
  *i += 1;
903
903
  }
904
+ Node::LabeledText { .. } => {
905
+ let para = nodes[*i].labeled_as_paragraph();
906
+ emit_nodes(
907
+ out,
908
+ depth,
909
+ std::slice::from_ref(para.as_ref()),
910
+ &mut 0,
911
+ level,
912
+ );
913
+ *i += 1;
914
+ }
904
915
  Node::CheckboxItem { checked, text } => {
905
916
  // A `<text>` with a `<checkbox class="selected|unselected"/>` head
906
917
  // element and the label text child (block form).
@@ -1722,6 +1733,9 @@ fn emit_located(out: &mut Out, depth: i32, location: &[u16; 4], inner: &Node) {
1722
1733
  Node::Paragraph { text } => {
1723
1734
  emit_text_element(out, depth, "text", "text", text, Some(location));
1724
1735
  }
1736
+ Node::LabeledText { .. } => {
1737
+ emit_located(out, depth, location, &inner.labeled_as_paragraph())
1738
+ }
1725
1739
  Node::Picture {
1726
1740
  caption,
1727
1741
  caption_href,
@@ -173,6 +173,18 @@ pub enum Node {
173
173
  /// renders it as `[text](href)`, JSON puts `hyperlink` on the caption
174
174
  /// item, DocLang emits the block-form `<caption>` with an `<href>` head.
175
175
  Caption { text: String, href: Option<String> },
176
+ /// A body text item with a docling label of its own and an optional
177
+ /// hyperlink — the PDF pipeline's `footnote` regions, whose item docling
178
+ /// keeps as a `footnote` text with the link annotation covering it as its
179
+ /// `hyperlink` (`PageAssembleModel._match_hyperlink`). The JSON writes
180
+ /// exactly that (label, raw text, `hyperlink`); every other serializer
181
+ /// renders it as the paragraph [`Self::labeled_markdown`] spells —
182
+ /// docling's Markdown wraps a linked item's whole text, `[text](uri)`.
183
+ LabeledText {
184
+ label: String,
185
+ text: String,
186
+ href: Option<String>,
187
+ },
176
188
  /// A chart (docling's `PictureItem` classified as a chart, carrying a
177
189
  /// `PictureTabularChartData` annotation). Markdown and JSON render it exactly
178
190
  /// like a [`Node::Picture`] placeholder (an `<!-- image -->` / `picture`
@@ -658,6 +670,23 @@ pub struct ListItemDclx {
658
670
  pub runs: Vec<InlineRun>,
659
671
  }
660
672
 
673
+ impl Node {
674
+ /// The paragraph a [`Node::LabeledText`] renders as outside the JSON:
675
+ /// its text, wrapped as `[text](uri)` when it carries a hyperlink. Any
676
+ /// other node is returned unchanged.
677
+ pub fn labeled_as_paragraph(&self) -> std::borrow::Cow<'_, Node> {
678
+ match self {
679
+ Node::LabeledText { text, href, .. } => std::borrow::Cow::Owned(Node::Paragraph {
680
+ text: match href {
681
+ Some(uri) => format!("[{text}]({uri})"),
682
+ None => text.clone(),
683
+ },
684
+ }),
685
+ other => std::borrow::Cow::Borrowed(other),
686
+ }
687
+ }
688
+ }
689
+
661
690
  impl InlineRun {
662
691
  /// A run with no active formatting (renders as bare inline text).
663
692
  pub fn is_plain(&self) -> bool {
@@ -1103,6 +1103,15 @@ impl Builder {
1103
1103
  };
1104
1104
  Some(self.add_text("caption", text, parent, extra))
1105
1105
  }
1106
+ // A PDF footnote: docling's own label, and its link as the item's
1107
+ // `hyperlink` rather than Markdown baked into the text.
1108
+ Node::LabeledText { label, text, href } => {
1109
+ let extra = match href {
1110
+ Some(url) => json!({ "hyperlink": url }),
1111
+ None => json!({}),
1112
+ };
1113
+ Some(self.add_text(label, text, parent, extra))
1114
+ }
1106
1115
  Node::Paragraph { text } => {
1107
1116
  // A whole-paragraph display equation is a formula item (docling
1108
1117
  // wraps it in `$$…$$` and, unlike a text item, never escapes it).
@@ -3624,4 +3633,39 @@ mod tests {
3624
3633
  assert_eq!(v["pictures"][0]["children"], serde_json::json!([]));
3625
3634
  assert_eq!(v["tables"][0]["children"], serde_json::json!([]));
3626
3635
  }
3636
+
3637
+ /// A PDF footnote (#620 follow-up): the JSON keeps docling's
3638
+ /// `footnote` label, the raw text and the link as `hyperlink`; the
3639
+ /// Markdown still wraps the whole item as `[text](uri)`, and the plain
3640
+ /// text drops the link like docling-core's `export_to_text`.
3641
+ #[test]
3642
+ fn a_labeled_text_keeps_its_label_and_hyperlink_out_of_the_text() {
3643
+ let mut doc = DoclingDocument::new("t");
3644
+ doc.push(Node::LabeledText {
3645
+ label: "footnote".into(),
3646
+ text: "1 https://example.com/a\\_b".into(),
3647
+ href: Some("https://example.com/a_b".into()),
3648
+ });
3649
+ doc.push(Node::LabeledText {
3650
+ label: "footnote".into(),
3651
+ text: "© 2022 the authors".into(),
3652
+ href: None,
3653
+ });
3654
+ let v: Value = serde_json::from_str(&doc.export_to_json()).unwrap();
3655
+ let t = &v["texts"][0];
3656
+ assert_eq!(t["label"], "footnote");
3657
+ assert_eq!(t["text"], "1 https://example.com/a_b");
3658
+ assert_eq!(t["orig"], "1 https://example.com/a_b");
3659
+ assert_eq!(t["hyperlink"], "https://example.com/a_b");
3660
+ assert_eq!(v["texts"][1]["label"], "footnote");
3661
+ assert!(v["texts"][1].get("hyperlink").is_none());
3662
+ assert_eq!(
3663
+ doc.export_to_markdown(),
3664
+ "[1 https://example.com/a\\_b](https://example.com/a_b)\n\n© 2022 the authors\n"
3665
+ );
3666
+ assert_eq!(
3667
+ doc.export_to_text(),
3668
+ "1 https://example.com/a_b\n\n© 2022 the authors"
3669
+ );
3670
+ }
3627
3671
  }
@@ -458,6 +458,9 @@ fn render_one(node: &Node, list_level: usize, inline: bool, parts: &mut Vec<Stri
458
458
  }
459
459
  // A standalone caption item is a text item to the LaTeX serializer.
460
460
  Node::Caption { text, .. } => push(parts, inline_md(text)),
461
+ Node::LabeledText { .. } => {
462
+ render_one(&node.labeled_as_paragraph(), list_level, inline, parts)
463
+ }
461
464
  Node::Paragraph { text } => {
462
465
  // A whole-paragraph `$$…$$` is a formula item in the JSON — never
463
466
  // escaped, re-wrapped by the serializer.
@@ -959,6 +959,7 @@ fn render_item(node: &Node, blocks: &mut Vec<String>, ctx: &mut Ctx) {
959
959
  // nothing to Markdown — only DocLang/JSON keep it.
960
960
  Node::Paragraph { text } if text.is_empty() => {}
961
961
  Node::Paragraph { text } => blocks.push(ctx.breaks(&ctx.text(text))),
962
+ Node::LabeledText { .. } => render_item(&node.labeled_as_paragraph(), blocks, ctx),
962
963
  // A standalone caption item renders like a text item; its hyperlink
963
964
  // annotation becomes a Markdown link around the whole caption.
964
965
  Node::Caption { text, .. } if text.is_empty() => {}
@@ -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.0"
4
+ version = "2.2.2"
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.0" }
44
+ docling-core = { path = "../docling-core", version = "2.2.2" }
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"] }
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "docling-pdf"
3
3
  description = "PDF/image backend for docling.rs: pure-Rust text layer and page renderer + ONNX layout/table/OCR pipeline."
4
- version = "2.2.0"
4
+ version = "2.2.2"
5
5
  edition = "2021"
6
6
  license = "MIT"
7
7
  repository = "https://github.com/docling-project/docling.rs"
@@ -71,10 +71,10 @@ ort-load-dynamic = ["ml", "docling-onnx/load-dynamic", "ort/load-dynamic"]
71
71
  # what current LTS distros ship (Ubuntu 24.04 = 1.17). Newer libheif-rs
72
72
  # requires libheif >= 1.18 and can be selected once baselines move.
73
73
  libheif-rs = { version = "~1.0", default-features = false, optional = true }
74
- docling-core = { path = "../docling-core", version = "2.2.0" }
74
+ docling-core = { path = "../docling-core", version = "2.2.2" }
75
75
  # Shared ONNX execution-provider selection (DOCLING_RS_EP, #74/#288);
76
76
  # re-exported as `docling_pdf::ep`.
77
- docling-onnx = { path = "../docling-onnx", version = "2.2.0", optional = true }
77
+ docling-onnx = { path = "../docling-onnx", version = "2.2.2", optional = true }
78
78
  pdfium-render = { version = "0.8", optional = true }
79
79
  # Runtime loading of the opt-in docling-parse renderer plugin (#478,
80
80
  # `dparse_render.rs`) — the same mechanism pdfium-render uses for libpdfium.
@@ -3516,18 +3516,27 @@ pub fn assemble_page(
3516
3516
  let hyperlink = (region.label == "footnote")
3517
3517
  .then(|| region_hyperlink(region, &page.links))
3518
3518
  .flatten();
3519
- let text = match hyperlink {
3520
- Some(uri) => {
3521
- // The strict-mode anchor pairs this item covers are
3522
- // superseded by the baked whole-item link.
3523
- links.retain(|(anchor, href)| {
3524
- !(href == &uri && region_texts[i].contains(anchor.as_str()))
3525
- });
3526
- format!("[{escaped}]({uri})")
3519
+ if let Some(uri) = &hyperlink {
3520
+ // The strict-mode anchor pairs this item covers are
3521
+ // superseded by the whole-item link.
3522
+ links.retain(|(anchor, href)| {
3523
+ !(href == uri && region_texts[i].contains(anchor.as_str()))
3524
+ });
3525
+ }
3526
+ // A footnote keeps docling's label and carries its link as
3527
+ // the item's `hyperlink` (the JSON's `text` stays the raw
3528
+ // footnote, not Markdown); every other serializer renders it
3529
+ // as the `[text](uri)` paragraph it was before.
3530
+ let node = if region.label == "footnote" {
3531
+ Node::LabeledText {
3532
+ label: "footnote".into(),
3533
+ text: escaped,
3534
+ href: hyperlink,
3527
3535
  }
3528
- None => escaped,
3536
+ } else {
3537
+ Node::Paragraph { text: escaped }
3529
3538
  };
3530
- nodes.push(located(loc, Node::Paragraph { text }))
3539
+ nodes.push(located(loc, node))
3531
3540
  }
3532
3541
  }
3533
3542
  }
@@ -3711,9 +3720,22 @@ fn is_merge_trailer(n: &Node) -> bool {
3711
3720
  | Node::PictureChildren(_)
3712
3721
  )
3713
3722
  || matches!(n, Node::Located { inner, .. } if matches!(inner.as_ref(), Node::Table(_)))
3723
+ || is_footnote_node(n)
3714
3724
  || as_paragraph(n).is_some_and(looks_like_caption)
3715
3725
  }
3716
3726
 
3727
+ /// Whether a node is a footnote item (a [`Node::LabeledText`] labelled
3728
+ /// `footnote`), looking through a [`Node::Located`] wrapper — one of
3729
+ /// docling's merge skip-labels: a footnote between the two halves of a
3730
+ /// paragraph is looked past, never merged into.
3731
+ fn is_footnote_node(n: &Node) -> bool {
3732
+ let n = match n {
3733
+ Node::Located { inner, .. } => inner.as_ref(),
3734
+ other => other,
3735
+ };
3736
+ matches!(n, Node::LabeledText { label, .. } if label == "footnote")
3737
+ }
3738
+
3717
3739
  /// Rebuild node `i` as a paragraph with `text`, preserving its `<location>`
3718
3740
  /// wrapper (and thus provenance) if it had one.
3719
3741
  fn reparagraph(node: &Node, text: String) -> Node {
@@ -263,6 +263,12 @@ pub fn convert_text_layer(bytes: &[u8], name: &str) -> Result<DoclingDocument, P
263
263
  convert_text_layer_pages(bytes, name, None)
264
264
  }
265
265
 
266
+ /// The error for bytes no reader here can parse as a PDF — not a PDF at
267
+ /// all, or one damaged past the parser's repairs. Said about the file, not
268
+ /// about a missing renderer or text layer: no library would read it either.
269
+ pub(crate) const UNREADABLE: &str =
270
+ "not a readable PDF: the file is damaged or not a PDF (its structure could not be parsed)";
271
+
266
272
  /// [`convert_text_layer`] restricted to a **1-based inclusive** page window
267
273
  /// (issue #80's `--pages`); `None` converts everything. The window is
268
274
  /// validated the same way as [`Pipeline::pages`]: `first <= last`, 1-based,
@@ -282,6 +288,11 @@ pub fn convert_text_layer_pages(
282
288
  let mut doc = DoclingDocument::new(name);
283
289
  let mut total = 0usize;
284
290
  let parsed = textparse::pdf_text_pages(bytes);
291
+ // No pages at all: tell a file nothing can open apart from a PDF that
292
+ // merely has no text layer (the caller's "scanned? needs OCR" hint).
293
+ if parsed.is_empty() && textparse::load_document(bytes).is_none() {
294
+ return Err(PdfError::Document(UNREADABLE.into()));
295
+ }
285
296
  // A vestigial layer (a few typed-in form fields over scanned pages) is not
286
297
  // the document's text: return the empty document, which callers already
287
298
  // report as "no text layer" — so an OCR-capable caller falls back to OCR
@@ -3499,6 +3510,29 @@ mod median_tests {
3499
3510
  }
3500
3511
  }
3501
3512
 
3513
+ #[cfg(test)]
3514
+ mod unreadable_tests {
3515
+ /// The text-layer path (the wasm build's only one) tells a file nothing
3516
+ /// can open apart from a PDF that merely has no text layer: garbage is
3517
+ /// an error about the file, a real PDF converts.
3518
+ #[test]
3519
+ fn text_layer_path_reports_an_unreadable_file() {
3520
+ for bytes in [b"garbage".as_slice(), b"", b"%PDF-1.7\n%%EOF\n"] {
3521
+ let err = super::convert_text_layer(bytes, "x.pdf").unwrap_err();
3522
+ assert!(err.to_string().contains("not a readable PDF"), "{err}");
3523
+ }
3524
+ let real = std::fs::read(
3525
+ std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
3526
+ .join("../../tests/data/pdf/sources/multi_page.pdf"),
3527
+ )
3528
+ .unwrap();
3529
+ assert!(!super::convert_text_layer(&real, "multi_page.pdf")
3530
+ .unwrap()
3531
+ .nodes
3532
+ .is_empty());
3533
+ }
3534
+ }
3535
+
3502
3536
  #[cfg(test)]
3503
3537
  mod send_check {
3504
3538
  /// The Node bindings (`docling-node`) run a shared [`super::Pipeline`] on
@@ -1302,10 +1302,12 @@ mod native {
1302
1302
  pub(super) struct Session<'a>(Infallible, PhantomData<&'a ()>);
1303
1303
  pub(super) struct Page<'a>(Infallible, PhantomData<&'a ()>);
1304
1304
 
1305
+ /// Reached only for a file the pure-Rust object model could not read
1306
+ /// (every entry point tries it first), so the error is about the file:
1307
+ /// naming the missing library sent users of a corrupt or non-PDF upload
1308
+ /// looking for a pdfium install that would not have helped either.
1305
1309
  pub(super) fn bind() -> Result<Lib, PdfError> {
1306
- Err(PdfError::Document(
1307
- "pdfium support is not compiled in (docling-pdf feature `pdfium`)".into(),
1308
- ))
1310
+ Err(PdfError::Document(crate::UNREADABLE.into()))
1309
1311
  }
1310
1312
 
1311
1313
  impl Lib {
@@ -89,10 +89,19 @@ fn scanned_page_extracts_table_and_keeps_chart() {
89
89
 
90
90
  #[test]
91
91
  fn scanned_pdf_yields_empty_document() {
92
- // No content stream at all — the no-text-layer contract is an empty doc,
93
- // not an error (callers decide whether to fall back to OCR).
94
- let doc = docling_pdf::convert_text_layer(b"%PDF-1.4\n%%EOF", "scan.pdf").expect("no error");
92
+ // An image-only page — the no-text-layer contract is an empty doc, not
93
+ // an error (callers decide whether to fall back to OCR).
94
+ let scan = std::fs::read(
95
+ std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
96
+ .join("../../tests/data/scanned/sources/ocr_test_raster.pdf"),
97
+ )
98
+ .unwrap();
99
+ let doc = docling_pdf::convert_text_layer(&scan, "scan.pdf").expect("no error");
95
100
  assert!(doc.nodes.is_empty());
101
+ // Bytes that are no readable PDF at all are an error about the file,
102
+ // not an empty "scanned" document.
103
+ let err = docling_pdf::convert_text_layer(b"%PDF-1.4\n%%EOF", "x.pdf").unwrap_err();
104
+ assert!(err.to_string().contains("not a readable PDF"), "{err}");
96
105
  }
97
106
 
98
107
  /// #211: HEIC is detected by content, and without the `heif` feature the
@@ -764,7 +764,7 @@ dependencies = [
764
764
 
765
765
  [[package]]
766
766
  name = "docling"
767
- version = "2.2.0"
767
+ version = "2.2.2"
768
768
  dependencies = [
769
769
  "calamine",
770
770
  "csv",
@@ -794,7 +794,7 @@ dependencies = [
794
794
 
795
795
  [[package]]
796
796
  name = "docling-asr"
797
- version = "2.2.0"
797
+ version = "2.2.2"
798
798
  dependencies = [
799
799
  "docling-core",
800
800
  "docling-onnx",
@@ -805,7 +805,7 @@ dependencies = [
805
805
 
806
806
  [[package]]
807
807
  name = "docling-core"
808
- version = "2.2.0"
808
+ version = "2.2.2"
809
809
  dependencies = [
810
810
  "png 0.18.1",
811
811
  "pulldown-cmark",
@@ -816,7 +816,7 @@ dependencies = [
816
816
 
817
817
  [[package]]
818
818
  name = "docling-onnx"
819
- version = "2.2.0"
819
+ version = "2.2.2"
820
820
  dependencies = [
821
821
  "docling-core",
822
822
  "libloading 0.8.9",
@@ -825,7 +825,7 @@ dependencies = [
825
825
 
826
826
  [[package]]
827
827
  name = "docling-pdf"
828
- version = "2.2.0"
828
+ version = "2.2.2"
829
829
  dependencies = [
830
830
  "docling-core",
831
831
  "docling-onnx",
@@ -10,7 +10,7 @@ build-backend = "maturin"
10
10
  name = "docling-rs"
11
11
  # Fallback for local maturin builds — pypi-publish.yml overwrites this with the
12
12
  # workspace version (root Cargo.toml) on every release.
13
- version = "2.2.0"
13
+ version = "2.2.2"
14
14
  description = "Rust port of docling (docling.rs): convert PDF/DOCX/HTML/... to Markdown or docling JSON. Python bindings."
15
15
  readme = "README.md"
16
16
  license = { text = "MIT" }
File without changes
File without changes
File without changes