edgeparse 0.3.0__tar.gz → 0.3.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.
Files changed (239) hide show
  1. {edgeparse-0.3.0 → edgeparse-0.3.2}/Cargo.lock +96 -16
  2. {edgeparse-0.3.0 → edgeparse-0.3.2}/Cargo.toml +1 -1
  3. {edgeparse-0.3.0 → edgeparse-0.3.2}/PKG-INFO +1 -1
  4. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/Cargo.toml +6 -0
  5. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/api/config.rs +9 -0
  6. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/hybrid.rs +2 -3
  7. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/lib.rs +218 -30
  8. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/chunks.rs +18 -0
  9. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/markdown.rs +156 -34
  10. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/chunk_parser.rs +888 -99
  11. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/encryption.rs +48 -28
  12. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/font.rs +539 -113
  13. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/font_style.rs +159 -0
  14. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/font_type3.rs +206 -0
  15. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/font_type3_ocr.rs +118 -0
  16. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/graphics_state.rs +10 -2
  17. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/image_codecs.rs +217 -0
  18. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/image_extractor.rs +530 -0
  19. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/image_region.rs +146 -0
  20. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/inmem_raster.rs +280 -46
  21. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/line_extractor.rs +97 -35
  22. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/loader.rs +20 -8
  23. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/mod.rs +13 -0
  24. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/ocg.rs +188 -0
  25. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/ocr/mod.rs +1 -1
  26. edgeparse-0.3.2/crates/edgeparse-core/src/pdf/pdf_string.rs +107 -0
  27. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/raster_table_ocr.rs +30 -10
  28. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/text_extractor.rs +20 -30
  29. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/caption_linker.rs +1 -0
  30. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/cluster_table_detector.rs +7 -7
  31. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/figure_detector.rs +1 -0
  32. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/header_footer.rs +19 -4
  33. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/heading_detector.rs +129 -58
  34. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/id_assignment.rs +1 -0
  35. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/list_detector.rs +71 -1
  36. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/paragraph_detector.rs +1 -0
  37. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/reading_order.rs +1 -0
  38. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/table_detector.rs +2 -0
  39. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/text_block_grouper.rs +1 -0
  40. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/text_line_grouper.rs +62 -0
  41. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/tagged/struct_tree.rs +73 -4
  42. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/image_dedup.rs +1 -1
  43. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/xycut.rs +1 -0
  44. edgeparse-0.3.2/crates/edgeparse-core/testdata/pattern_form.pdf +0 -0
  45. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-python/src/lib.rs +5 -1
  46. {edgeparse-0.3.0 → edgeparse-0.3.2}/edgeparse/__init__.py +4 -0
  47. edgeparse-0.3.0/crates/edgeparse-core/src/pdf/image_extractor.rs +0 -330
  48. {edgeparse-0.3.0 → edgeparse-0.3.2}/README.md +0 -0
  49. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/README.md +0 -0
  50. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/api/batch.rs +0 -0
  51. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/api/config_loader.rs +0 -0
  52. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/api/filter.rs +0 -0
  53. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/api/mod.rs +0 -0
  54. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/bbox.rs +0 -0
  55. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/content.rs +0 -0
  56. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/document.rs +0 -0
  57. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/enums.rs +0 -0
  58. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/list.rs +0 -0
  59. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/mod.rs +0 -0
  60. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/semantic.rs +0 -0
  61. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/table.rs +0 -0
  62. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/models/text.rs +0 -0
  63. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/csv.rs +0 -0
  64. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/docx.rs +0 -0
  65. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/html.rs +0 -0
  66. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/json.rs +0 -0
  67. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/legacy_json.rs +0 -0
  68. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/mod.rs +0 -0
  69. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/text.rs +0 -0
  70. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/output/toc_builder.rs +0 -0
  71. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/annotation_enrichment.rs +0 -0
  72. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/annotation_extractor.rs +0 -0
  73. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/bookmark_extractor.rs +0 -0
  74. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/data/glyphlist.txt +0 -0
  75. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/form_extractor.rs +0 -0
  76. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/hyperlink_extractor.rs +0 -0
  77. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/metadata_writer.rs +0 -0
  78. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/ocr/host_engine.rs +0 -0
  79. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/ocr/ocrs_engine.rs +0 -0
  80. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/ocr/tesseract_cli.rs +0 -0
  81. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pdf/page_info.rs +0 -0
  82. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/error_recovery.rs +0 -0
  83. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/logging.rs +0 -0
  84. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/mod.rs +0 -0
  85. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/orchestrator.rs +0 -0
  86. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/parallel.rs +0 -0
  87. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/boxed_heading_promoter.rs +0 -0
  88. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/column_detector.rs +0 -0
  89. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/content_filter.rs +0 -0
  90. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/content_sanitizer.rs +0 -0
  91. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/cross_page_linker.rs +0 -0
  92. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/footnote_detector.rs +0 -0
  93. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/footnote_linker.rs +0 -0
  94. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/list_pass2.rs +0 -0
  95. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/mod.rs +0 -0
  96. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/nesting_level.rs +0 -0
  97. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/output_builder.rs +0 -0
  98. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/table_content_assigner.rs +0 -0
  99. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/toc_detector.rs +0 -0
  100. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/pipeline/stages/watermark_detector.rs +0 -0
  101. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/tagged/mod.rs +0 -0
  102. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/tagged/processor.rs +0 -0
  103. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/diff.rs +0 -0
  104. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/font_metrics_cache.rs +0 -0
  105. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/language_detector.rs +0 -0
  106. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/layout_analysis.rs +0 -0
  107. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/mod.rs +0 -0
  108. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/page_range.rs +0 -0
  109. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/sanitizer.rs +0 -0
  110. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/statistics.rs +0 -0
  111. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/text_normalizer.rs +0 -0
  112. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/src/utils/xref_index.rs +0 -0
  113. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/tests/integration_tests.rs +0 -0
  114. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-core/tests/perf_benchmarks.rs +0 -0
  115. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/edgeparse-python/Cargo.toml +0 -0
  116. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.cargo_vcs_info.json +0 -0
  117. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.chglog/CHANGELOG.tpl.md +0 -0
  118. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.chglog/config.yml +0 -0
  119. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.editorconfig +0 -0
  120. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.gitattributes +0 -0
  121. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.github/workflows/ci.yml +0 -0
  122. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.gitignore +0 -0
  123. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.vscode/launch.json +0 -0
  124. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/.vscode/settings.json +0 -0
  125. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/CHANGELOG.md +0 -0
  126. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/Cargo.toml +0 -0
  127. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/LICENSE +0 -0
  128. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/README.md +0 -0
  129. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/AnnotationDemo.pdf +0 -0
  130. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/Incremental.pdf +0 -0
  131. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/encrypted.pdf +0 -0
  132. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/example.pdf +0 -0
  133. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/pdf_icon.jpg +0 -0
  134. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/L16.png +0 -0
  135. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/L8.jpg +0 -0
  136. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/La16.png +0 -0
  137. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/La8.png +0 -0
  138. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/Rgb16.png +0 -0
  139. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/Rgb8.jpg +0 -0
  140. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/Rgba16.png +0 -0
  141. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/supported_color_type/Rgba8.png +0 -0
  142. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/assets/unicode.pdf +0 -0
  143. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/benches/datetime.rs +0 -0
  144. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/benches/parse.rs +0 -0
  145. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/add_barcode.rs +0 -0
  146. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/analyze_object_streams.rs +0 -0
  147. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/analyze_page_contents.rs +0 -0
  148. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/analyze_pdf.rs +0 -0
  149. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/analyze_references.rs +0 -0
  150. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/check_raw_objstream.rs +0 -0
  151. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/compress_existing_pdf.rs +0 -0
  152. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/create.rs +0 -0
  153. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/create_text_heavy_pdf.rs +0 -0
  154. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/debug_compression_detailed.rs +0 -0
  155. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/debug_compression_full.rs +0 -0
  156. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/debug_object_stream.rs +0 -0
  157. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/debug_save_with_objstreams.rs +0 -0
  158. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/decrypt.rs +0 -0
  159. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/encrypt.rs +0 -0
  160. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/extract_text.rs +0 -0
  161. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/extract_toc.rs +0 -0
  162. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/final_compression_test.rs +0 -0
  163. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/inspect_object_stream.rs +0 -0
  164. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/merge.rs +0 -0
  165. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/object_streams.rs +0 -0
  166. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/print_annotations.rs +0 -0
  167. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/replace_partial_text.rs +0 -0
  168. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/rotate.rs +0 -0
  169. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_better_compression.rs +0 -0
  170. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_decryption.rs +0 -0
  171. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_direct_objstream.rs +0 -0
  172. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_filter_roundtrip.rs +0 -0
  173. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_metadata.rs +0 -0
  174. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_object_stream_creation.rs +0 -0
  175. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_object_stream_roundtrip.rs +0 -0
  176. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_real_pdf_compression_with_fix.rs +0 -0
  177. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/test_structural_compression.rs +0 -0
  178. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/verify_decryption.rs +0 -0
  179. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/verify_page_compression.rs +0 -0
  180. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/verify_pdf.rs +0 -0
  181. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/examples/verify_trailer_compression_fix.rs +0 -0
  182. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/rustfmt.toml +0 -0
  183. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/bookmarks.rs +0 -0
  184. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/cmap_section.rs +0 -0
  185. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/common_data_structures/mod.rs +0 -0
  186. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/content.rs +0 -0
  187. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/creator.rs +0 -0
  188. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/datetime.rs +0 -0
  189. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/destinations.rs +0 -0
  190. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/document.rs +0 -0
  191. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encodings/cmap.rs +0 -0
  192. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encodings/glyphnames.rs +0 -0
  193. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encodings/mappings.rs +0 -0
  194. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encodings/mod.rs +0 -0
  195. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encryption/algorithms.rs +0 -0
  196. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encryption/crypt_filters.rs +0 -0
  197. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encryption/pkcs5.rs +0 -0
  198. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encryption/rc4.rs +0 -0
  199. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/encryption.rs +0 -0
  200. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/error.rs +0 -0
  201. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/filters/mod.rs +0 -0
  202. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/filters/png.rs +0 -0
  203. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/font.rs +0 -0
  204. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/incremental_document.rs +0 -0
  205. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/lib.rs +0 -0
  206. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/object.rs +0 -0
  207. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/object_stream.rs +0 -0
  208. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/outlines.rs +0 -0
  209. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/parser/cmap_parser.rs +0 -0
  210. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/parser/mod.rs +0 -0
  211. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/parser_aux.rs +0 -0
  212. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/processor.rs +0 -0
  213. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/reader.rs +0 -0
  214. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/save_options.rs +0 -0
  215. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/toc.rs +0 -0
  216. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/writer.rs +0 -0
  217. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/xobject.rs +0 -0
  218. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/src/xref.rs +0 -0
  219. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/annotation.rs +0 -0
  220. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/catalog_compression_integration_test.rs +0 -0
  221. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/decryption.rs +0 -0
  222. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/extract_test.rs +0 -0
  223. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/font_test.rs +0 -0
  224. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/incremental_document.rs +0 -0
  225. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/metadata_test.rs +0 -0
  226. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/modify.rs +0 -0
  227. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/object_stream_comprehensive_test.rs +0 -0
  228. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/object_stream_edge_cases_test.rs +0 -0
  229. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/object_stream_methods_test.rs +0 -0
  230. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/object_stream_performance_test.rs +0 -0
  231. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/resources/fonts/Montserrat-Regular.ttf +0 -0
  232. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/simple_object_stream_test.rs +0 -0
  233. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/trailer_reference_compression_test.rs +0 -0
  234. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/unicode.rs +0 -0
  235. {edgeparse-0.3.0 → edgeparse-0.3.2}/crates/pdf-cos/tests/utils.rs +0 -0
  236. {edgeparse-0.3.0 → edgeparse-0.3.2}/edgeparse/_types.py +0 -0
  237. {edgeparse-0.3.0 → edgeparse-0.3.2}/edgeparse/cli.py +0 -0
  238. {edgeparse-0.3.0 → edgeparse-0.3.2}/edgeparse/py.typed +0 -0
  239. {edgeparse-0.3.0 → edgeparse-0.3.2}/pyproject.toml +0 -0
@@ -184,7 +184,7 @@ dependencies = [
184
184
  "num-traits",
185
185
  "pastey",
186
186
  "rayon",
187
- "thiserror",
187
+ "thiserror 2.0.21",
188
188
  "v_frame",
189
189
  "y4m",
190
190
  ]
@@ -582,7 +582,7 @@ version = "1.0.0"
582
582
  source = "registry+https://github.com/rust-lang/crates.io-index"
583
583
  checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
584
584
  dependencies = [
585
- "thiserror",
585
+ "thiserror 2.0.21",
586
586
  ]
587
587
 
588
588
  [[package]]
@@ -655,7 +655,7 @@ dependencies = [
655
655
 
656
656
  [[package]]
657
657
  name = "edgeparse-cli"
658
- version = "0.3.0"
658
+ version = "0.3.2"
659
659
  dependencies = [
660
660
  "anyhow",
661
661
  "clap",
@@ -669,13 +669,15 @@ dependencies = [
669
669
 
670
670
  [[package]]
671
671
  name = "edgeparse-core"
672
- version = "0.3.0"
672
+ version = "0.3.2"
673
673
  dependencies = [
674
674
  "anyhow",
675
675
  "base64 0.22.1",
676
676
  "euclid",
677
+ "hayro-jbig2",
677
678
  "image",
678
679
  "indexmap",
680
+ "jpeg2k",
679
681
  "js-sys",
680
682
  "log",
681
683
  "ocrs",
@@ -688,7 +690,7 @@ dependencies = [
688
690
  "rten-imageproc",
689
691
  "serde",
690
692
  "serde_json",
691
- "thiserror",
693
+ "thiserror 2.0.21",
692
694
  "tokio",
693
695
  "ttf-parser",
694
696
  "unicode-normalization",
@@ -698,7 +700,7 @@ dependencies = [
698
700
 
699
701
  [[package]]
700
702
  name = "edgeparse-node"
701
- version = "0.3.0"
703
+ version = "0.3.2"
702
704
  dependencies = [
703
705
  "edgeparse-core",
704
706
  "napi",
@@ -710,7 +712,7 @@ dependencies = [
710
712
 
711
713
  [[package]]
712
714
  name = "edgeparse-python"
713
- version = "0.3.0"
715
+ version = "0.3.2"
714
716
  dependencies = [
715
717
  "edgeparse-core",
716
718
  "pyo3",
@@ -719,7 +721,7 @@ dependencies = [
719
721
 
720
722
  [[package]]
721
723
  name = "edgeparse-wasm"
722
- version = "0.3.0"
724
+ version = "0.3.2"
723
725
  dependencies = [
724
726
  "console_error_panic_hook",
725
727
  "console_log",
@@ -1030,6 +1032,21 @@ version = "0.17.1"
1030
1032
  source = "registry+https://github.com/rust-lang/crates.io-index"
1031
1033
  checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
1032
1034
 
1035
+ [[package]]
1036
+ name = "hayro-ccitt"
1037
+ version = "0.3.0"
1038
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1039
+ checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a"
1040
+
1041
+ [[package]]
1042
+ name = "hayro-jbig2"
1043
+ version = "0.3.0"
1044
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1045
+ checksum = "69374b3668dd45aeb3d3145cda68f2c7b4f223aaa2511e67d076f1c7d741388d"
1046
+ dependencies = [
1047
+ "hayro-ccitt",
1048
+ ]
1049
+
1033
1050
  [[package]]
1034
1051
  name = "heck"
1035
1052
  version = "0.5.0"
@@ -1442,6 +1459,20 @@ dependencies = [
1442
1459
  "libc",
1443
1460
  ]
1444
1461
 
1462
+ [[package]]
1463
+ name = "jpeg2k"
1464
+ version = "0.9.1"
1465
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1466
+ checksum = "f5b1c0ab2091b7044e98ec1910d63f68cc51358ec0dfde83f0b1d7aab654adb9"
1467
+ dependencies = [
1468
+ "anyhow",
1469
+ "image",
1470
+ "log",
1471
+ "openjp2",
1472
+ "openjpeg-sys",
1473
+ "thiserror 1.0.69",
1474
+ ]
1475
+
1445
1476
  [[package]]
1446
1477
  name = "js-sys"
1447
1478
  version = "0.3.106"
@@ -1845,7 +1876,7 @@ dependencies = [
1845
1876
  "rten",
1846
1877
  "rten-imageproc",
1847
1878
  "rten-tensor",
1848
- "thiserror",
1879
+ "thiserror 2.0.21",
1849
1880
  "wasm-bindgen",
1850
1881
  ]
1851
1882
 
@@ -1867,6 +1898,29 @@ version = "11.1.5"
1867
1898
  source = "registry+https://github.com/rust-lang/crates.io-index"
1868
1899
  checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
1869
1900
 
1901
+ [[package]]
1902
+ name = "openjp2"
1903
+ version = "0.5.3"
1904
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1905
+ checksum = "4c582a03232f5f2f2f0107a7327753df3c32f5505a195cd79cad6aa97d6bddf1"
1906
+ dependencies = [
1907
+ "bitflags 1.3.2",
1908
+ "libc",
1909
+ "log",
1910
+ "smallvec",
1911
+ "sprintf",
1912
+ ]
1913
+
1914
+ [[package]]
1915
+ name = "openjpeg-sys"
1916
+ version = "1.0.12"
1917
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1918
+ checksum = "92682cb92e7b01e2c020cf8ec12f3374558924bb24621df1112d8a7c93d419ef"
1919
+ dependencies = [
1920
+ "cc",
1921
+ "libc",
1922
+ ]
1923
+
1870
1924
  [[package]]
1871
1925
  name = "option-ext"
1872
1926
  version = "0.2.0"
@@ -1950,7 +2004,7 @@ dependencies = [
1950
2004
  "shellexpand",
1951
2005
  "stringprep",
1952
2006
  "tempfile",
1953
- "thiserror",
2007
+ "thiserror 2.0.21",
1954
2008
  "time",
1955
2009
  "tokio",
1956
2010
  "ttf-parser",
@@ -2180,7 +2234,7 @@ dependencies = [
2180
2234
  "rustc-hash",
2181
2235
  "rustls",
2182
2236
  "socket2",
2183
- "thiserror",
2237
+ "thiserror 2.0.21",
2184
2238
  "tokio",
2185
2239
  "tracing",
2186
2240
  "web-time",
@@ -2202,7 +2256,7 @@ dependencies = [
2202
2256
  "rustls",
2203
2257
  "rustls-pki-types",
2204
2258
  "slab",
2205
- "thiserror",
2259
+ "thiserror 2.0.21",
2206
2260
  "tinyvec",
2207
2261
  "tracing",
2208
2262
  "web-time",
@@ -2362,7 +2416,7 @@ dependencies = [
2362
2416
  "rand 0.9.5",
2363
2417
  "rand_chacha",
2364
2418
  "simd_helpers",
2365
- "thiserror",
2419
+ "thiserror 2.0.21",
2366
2420
  "v_frame",
2367
2421
  "wasm-bindgen",
2368
2422
  ]
@@ -2433,7 +2487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2433
2487
  checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0"
2434
2488
  dependencies = [
2435
2489
  "libredox",
2436
- "thiserror",
2490
+ "thiserror 2.0.21",
2437
2491
  ]
2438
2492
 
2439
2493
  [[package]]
@@ -2891,6 +2945,12 @@ dependencies = [
2891
2945
  "windows-sys 0.61.2",
2892
2946
  ]
2893
2947
 
2948
+ [[package]]
2949
+ name = "sprintf"
2950
+ version = "0.1.4"
2951
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2952
+ checksum = "6c0cdea5a20a06e7c57f627094e7b1618e5665592cd88f2d45fa4014e348db58"
2953
+
2894
2954
  [[package]]
2895
2955
  name = "stable_deref_trait"
2896
2956
  version = "1.2.1"
@@ -2981,13 +3041,33 @@ dependencies = [
2981
3041
  "windows-sys 0.61.2",
2982
3042
  ]
2983
3043
 
3044
+ [[package]]
3045
+ name = "thiserror"
3046
+ version = "1.0.69"
3047
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3048
+ checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
3049
+ dependencies = [
3050
+ "thiserror-impl 1.0.69",
3051
+ ]
3052
+
2984
3053
  [[package]]
2985
3054
  name = "thiserror"
2986
3055
  version = "2.0.21"
2987
3056
  source = "registry+https://github.com/rust-lang/crates.io-index"
2988
3057
  checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
2989
3058
  dependencies = [
2990
- "thiserror-impl",
3059
+ "thiserror-impl 2.0.21",
3060
+ ]
3061
+
3062
+ [[package]]
3063
+ name = "thiserror-impl"
3064
+ version = "1.0.69"
3065
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3066
+ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
3067
+ dependencies = [
3068
+ "proc-macro2 1.0.107",
3069
+ "quote 1.0.47",
3070
+ "syn 2.0.119",
2991
3071
  ]
2992
3072
 
2993
3073
  [[package]]
@@ -3771,7 +3851,7 @@ dependencies = [
3771
3851
  "flate2",
3772
3852
  "indexmap",
3773
3853
  "memchr",
3774
- "thiserror",
3854
+ "thiserror 2.0.21",
3775
3855
  "zopfli",
3776
3856
  ]
3777
3857
 
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/edgeparse-core", "crates/pdf-cos", "crates/edgeparse-python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.3.0"
6
+ version = "0.3.2"
7
7
  edition = "2021"
8
8
  rust-version = "1.85"
9
9
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edgeparse
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -40,6 +40,8 @@ rten = { version = "0.26", optional = true, default-features = false, features =
40
40
  rten-imageproc = { version = "0.26", optional = true }
41
41
  js-sys = { version = "0.3", optional = true }
42
42
  wasm-bindgen = { version = "0.2", optional = true }
43
+ hayro-jbig2 = { version = "0.3", optional = true, default-features = false, features = ["std"] }
44
+ jpeg2k = { version = "0.9", optional = true }
43
45
 
44
46
  [features]
45
47
  default = ["native"]
@@ -49,5 +51,9 @@ native = ["dep:rayon", "image", "dep:zip", "lopdf/rayon"]
49
51
  wasm = ["lopdf/wasm_js", "image", "dep:js-sys", "dep:wasm-bindgen"]
50
52
  hybrid = ["dep:reqwest", "dep:tokio"]
51
53
  ocr-ocrs = ["dep:ocrs", "dep:rten", "dep:rten-imageproc", "image"]
54
+ # Optional PDF image codecs for the OCR path (scanned / hybrid pages).
55
+ codecs-jbig2 = ["dep:hayro-jbig2", "image"]
56
+ codecs-jpx = ["dep:jpeg2k", "image"]
57
+ codecs = ["codecs-jbig2", "codecs-jpx"]
52
58
 
53
59
  [dev-dependencies]
@@ -116,6 +116,9 @@ pub struct ProcessingConfig {
116
116
  pub image_dir: Option<String>,
117
117
  /// Enable raster table OCR recovery on image-based tables
118
118
  pub raster_table_ocr: bool,
119
+ /// Soft wall-clock budget for raster OCR recovery (milliseconds).
120
+ /// `None` = unlimited. When exhausted, remaining images are skipped.
121
+ pub ocr_budget_ms: Option<u64>,
119
122
  /// Pages to extract (e.g., "1,3,5-7")
120
123
  pub pages: Option<String>,
121
124
  /// Include headers/footers in output
@@ -153,6 +156,7 @@ impl Default for ProcessingConfig {
153
156
  image_format: ImageFormat::Png,
154
157
  image_dir: None,
155
158
  raster_table_ocr: true,
159
+ ocr_budget_ms: None,
156
160
  pages: None,
157
161
  include_header_footer: false,
158
162
  hybrid: HybridBackend::Off,
@@ -178,6 +182,11 @@ impl ProcessingConfig {
178
182
  pub fn raster_table_ocr_enabled(&self) -> bool {
179
183
  self.raster_table_ocr
180
184
  }
185
+
186
+ /// OCR wall-clock budget for this document.
187
+ pub fn ocr_budget(&self) -> crate::pdf::image_region::OcrBudget {
188
+ crate::pdf::image_region::OcrBudget::from_millis(self.ocr_budget_ms)
189
+ }
181
190
  }
182
191
 
183
192
  #[cfg(test)]
@@ -381,11 +381,10 @@ fn typographic_heading_deficit(doc: &PdfDocument) -> bool {
381
381
  body_weights.push(w);
382
382
  }
383
383
  }
384
- ContentElement::Heading(h) => {
385
- if h.base.base.semantic_type != SemanticType::Caption {
384
+ ContentElement::Heading(h)
385
+ if h.base.base.semantic_type != SemanticType::Caption => {
386
386
  recovered += 1;
387
387
  }
388
- }
389
388
  ContentElement::NumberHeading(_) => {
390
389
  recovered += 1;
391
390
  }
@@ -31,9 +31,50 @@ use crate::pdf::raster_table_ocr::{
31
31
  };
32
32
  use crate::pipeline::orchestrator::{run_pipeline, PipelineState};
33
33
  use crate::pipeline::stages::heading_detector::refine_heading_hierarchy;
34
- use crate::tagged::struct_tree::build_mcid_map;
34
+ use crate::tagged::struct_tree::{build_mcid_map, should_use_struct_tree};
35
35
  use std::time::Instant;
36
36
 
37
+ /// Run dominant-image text + raster-table recovery for one page (native only).
38
+ ///
39
+ /// Single gate for OCR enablement + wall-clock budget so callers stay DRY.
40
+ #[cfg(not(target_arch = "wasm32"))]
41
+ fn recover_page_image_content(
42
+ input_path: &std::path::Path,
43
+ config: &ProcessingConfig,
44
+ page_info_by_number: &[Option<&page_info::PageInfo>],
45
+ page_num: u32,
46
+ page_chunks: &crate::pdf::chunk_parser::PageChunks,
47
+ ocr_budget: &mut crate::pdf::image_region::OcrBudget,
48
+ ) -> (
49
+ Vec<crate::models::chunks::TextChunk>,
50
+ Vec<crate::models::table::TableBorder>,
51
+ ) {
52
+ if !config.raster_table_ocr_enabled() || ocr_budget.exhausted() {
53
+ return (Vec::new(), Vec::new());
54
+ }
55
+ let Some(Some(page_info)) = page_info_by_number.get(page_num as usize) else {
56
+ return (Vec::new(), Vec::new());
57
+ };
58
+ let text = recover_dominant_image_text_chunks(
59
+ input_path,
60
+ &page_info.crop_box,
61
+ page_num,
62
+ &page_chunks.text_chunks,
63
+ &page_chunks.image_chunks,
64
+ );
65
+ if ocr_budget.exhausted() {
66
+ return (text, Vec::new());
67
+ }
68
+ let tables = recover_raster_table_borders(
69
+ input_path,
70
+ &page_info.crop_box,
71
+ page_num,
72
+ &page_chunks.text_chunks,
73
+ &page_chunks.image_chunks,
74
+ );
75
+ (text, tables)
76
+ }
77
+
37
78
  /// Main entry point: convert a PDF file to structured data.
38
79
  ///
39
80
  /// # Arguments
@@ -81,28 +122,17 @@ pub fn convert(
81
122
  let mut page_contents = Vec::with_capacity(pages_map.len());
82
123
 
83
124
  let phase_start = Instant::now();
125
+ let mut ocr_budget = config.ocr_budget();
84
126
  for (&page_num, &page_id) in &pages_map {
85
127
  let page_chunks = extract_page_chunks(&raw_doc.document, page_num, page_id)?;
86
- let mut recovered_text_chunks = Vec::new();
87
- let mut recovered_tables = Vec::new();
88
- if config.raster_table_ocr_enabled() {
89
- if let Some(Some(page_info)) = page_info_by_number.get(page_num as usize) {
90
- recovered_text_chunks = recover_dominant_image_text_chunks(
91
- input_path,
92
- &page_info.crop_box,
93
- page_num,
94
- &page_chunks.text_chunks,
95
- &page_chunks.image_chunks,
96
- );
97
- recovered_tables = recover_raster_table_borders(
98
- input_path,
99
- &page_info.crop_box,
100
- page_num,
101
- &page_chunks.text_chunks,
102
- &page_chunks.image_chunks,
103
- );
104
- }
105
- }
128
+ let (recovered_text_chunks, recovered_tables) = recover_page_image_content(
129
+ input_path,
130
+ config,
131
+ &page_info_by_number,
132
+ page_num,
133
+ &page_chunks,
134
+ &mut ocr_budget,
135
+ );
106
136
  let mut elements: Vec<ContentElement> = page_chunks
107
137
  .text_chunks
108
138
  .into_iter()
@@ -142,9 +172,12 @@ pub fn convert(
142
172
  }
143
173
  log_phase_duration(timing_enabled, "extract_page_chunks", phase_start);
144
174
 
145
- // Run the processing pipeline
146
175
  let phase_start = Instant::now();
147
- let mcid_map = build_mcid_map(&raw_doc.document);
176
+ let mcid_map = if should_use_struct_tree(&raw_doc.document, config.use_struct_tree) {
177
+ build_mcid_map(&raw_doc.document)
178
+ } else {
179
+ Default::default()
180
+ };
148
181
  let mut pipeline_state = PipelineState::with_mcid_map(page_contents, config.clone(), mcid_map)
149
182
  .with_page_info(page_info_list);
150
183
  run_pipeline(&mut pipeline_state)?;
@@ -248,6 +281,10 @@ pub struct ExtractSession {
248
281
  pub creation_date: Option<String>,
249
282
  /// Modification date metadata.
250
283
  pub modification_date: Option<String>,
284
+ /// Producer metadata.
285
+ pub producer: Option<String>,
286
+ /// Creator metadata.
287
+ pub creator: Option<String>,
251
288
  /// Page count.
252
289
  pub number_of_pages: u32,
253
290
  /// Per-page content elements (tables not yet recovered from OCR candidates).
@@ -302,11 +339,18 @@ pub fn extract_session(
302
339
 
303
340
  #[cfg(feature = "image")]
304
341
  if config.raster_table_ocr_enabled() {
342
+ let (page_w, page_h) = page_info_list
343
+ .iter()
344
+ .find(|p| p.page_number == page_num)
345
+ .map(|p| (p.width, p.height))
346
+ .unwrap_or((612.0, 792.0));
305
347
  let page_cands = pdf::inmem_raster::collect_raster_candidates(
306
348
  &raw_doc.document,
307
349
  page_id,
308
350
  &page_chunks.image_chunks,
309
351
  &page_chunks.text_chunks,
352
+ page_w,
353
+ page_h,
310
354
  &mut next_id,
311
355
  );
312
356
  candidates.extend(page_cands);
@@ -344,7 +388,11 @@ pub fn extract_session(
344
388
  }
345
389
  }
346
390
 
347
- let mcid_map = build_mcid_map(&raw_doc.document);
391
+ let mcid_map = if should_use_struct_tree(&raw_doc.document, config.use_struct_tree) {
392
+ build_mcid_map(&raw_doc.document)
393
+ } else {
394
+ Default::default()
395
+ };
348
396
  let bookmarks = extract_bookmarks(&raw_doc.document);
349
397
 
350
398
  Ok(ExtractSession {
@@ -354,6 +402,8 @@ pub fn extract_session(
354
402
  title: raw_doc.metadata.title,
355
403
  creation_date: raw_doc.metadata.creation_date,
356
404
  modification_date: raw_doc.metadata.modification_date,
405
+ producer: raw_doc.metadata.producer,
406
+ creator: raw_doc.metadata.creator,
357
407
  number_of_pages: pages_map.len() as u32,
358
408
  page_contents,
359
409
  page_info_list,
@@ -389,6 +439,27 @@ impl ExtractSession {
389
439
  }
390
440
  }
391
441
 
442
+ /// Drop the source Image (by XObject index) and inject OCR words as text chunks.
443
+ #[cfg(feature = "image")]
444
+ fn inject_ocr_text_blocks(
445
+ session: &mut ExtractSession,
446
+ page_idx: usize,
447
+ cand: &pdf::inmem_raster::RasterCandidate,
448
+ words: &[pdf::ocr::OcrWord],
449
+ ) {
450
+ if let Some(page) = session.page_contents.get_mut(page_idx) {
451
+ if let Some(idx) = cand.image_index {
452
+ page.retain(|el| match el {
453
+ ContentElement::Image(img) => img.index != Some(idx),
454
+ _ => true,
455
+ });
456
+ }
457
+ for chunk in pdf::inmem_raster::ocr_words_to_text_chunks(cand, words) {
458
+ page.push(ContentElement::TextChunk(chunk));
459
+ }
460
+ }
461
+ }
462
+
392
463
  /// Run the pipeline after injecting OCR-derived tables from session candidates.
393
464
  pub fn assemble(
394
465
  mut session: ExtractSession,
@@ -397,9 +468,11 @@ pub fn assemble(
397
468
  #[cfg(feature = "image")]
398
469
  if session.config.raster_table_ocr_enabled() {
399
470
  use image::GrayImage;
471
+ use pdf::inmem_raster::RasterCandidateKind;
400
472
  use pdf::ocr::{default_engine, OcrEngine};
401
473
 
402
- for cand in &session.candidates {
474
+ let candidates = std::mem::take(&mut session.candidates);
475
+ for cand in &candidates {
403
476
  let words = match &mode {
404
477
  OcrAssembleMode::SyncEngine => {
405
478
  if let Some(gray) =
@@ -423,15 +496,27 @@ pub fn assemble(
423
496
  }
424
497
  }
425
498
  };
426
- if let Some(table) = pdf::inmem_raster::build_table_from_candidate(cand, &words) {
427
- if pdf::inmem_raster::bordered_table_is_plausible(&table) {
428
- let page_idx = cand.page.saturating_sub(1) as usize;
429
- if let Some(page) = session.page_contents.get_mut(page_idx) {
430
- page.push(ContentElement::TableBorder(table));
499
+ let page_idx = cand.page.saturating_sub(1) as usize;
500
+ match cand.kind {
501
+ RasterCandidateKind::Table => {
502
+ let table = pdf::inmem_raster::build_table_from_candidate(cand, &words)
503
+ .filter(pdf::inmem_raster::bordered_table_is_plausible);
504
+ if let Some(table) = table {
505
+ if let Some(page) = session.page_contents.get_mut(page_idx) {
506
+ page.push(ContentElement::TableBorder(table));
507
+ }
508
+ } else if !words.is_empty() {
509
+ // OCR found glyphs but no lattice — assemble as text,
510
+ // not discard (right assembly from evidence).
511
+ inject_ocr_text_blocks(&mut session, page_idx, cand, &words);
431
512
  }
432
513
  }
514
+ RasterCandidateKind::TextBlocks => {
515
+ inject_ocr_text_blocks(&mut session, page_idx, cand, &words);
516
+ }
433
517
  }
434
518
  }
519
+ session.candidates = candidates;
435
520
  }
436
521
 
437
522
  #[cfg(not(feature = "image"))]
@@ -452,6 +537,8 @@ pub fn assemble(
452
537
  doc.title = session.title;
453
538
  doc.creation_date = session.creation_date;
454
539
  doc.modification_date = session.modification_date;
540
+ doc.producer = session.producer;
541
+ doc.creator = session.creator;
455
542
 
456
543
  for page in pipeline_state.pages {
457
544
  doc.kids.extend(page);
@@ -776,4 +863,105 @@ mod tests {
776
863
  let _ = count_table_borders(&doc);
777
864
  }
778
865
  }
866
+
867
+ /// Pattern-painted page raster (Skia/Penpot style) must emit an OCR
868
+ /// candidate, skip page-edge lines, and not render artifact metadata titles.
869
+ #[cfg(feature = "image")]
870
+ #[test]
871
+ fn pattern_form_fixture_emits_text_blocks_candidate() {
872
+ let pdf =
873
+ std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("testdata/pattern_form.pdf");
874
+ assert!(pdf.exists(), "missing fixture {}", pdf.display());
875
+ let data = std::fs::read(&pdf).unwrap();
876
+ let config = ProcessingConfig::default();
877
+ let session = extract_session(&data, "pattern_form.pdf", &config, None).unwrap();
878
+ assert!(
879
+ !session.candidates.is_empty(),
880
+ "expected ≥1 OCR candidate from pattern-painted image"
881
+ );
882
+ assert!(
883
+ session
884
+ .candidates
885
+ .iter()
886
+ .any(|c| c.kind == pdf::inmem_raster::RasterCandidateKind::TextBlocks),
887
+ "expected TextBlocks candidate for page-sized raster"
888
+ );
889
+
890
+ // No OCR words → still assemble; markdown must skip "Penpot - Render".
891
+ let doc = assemble(session, OcrAssembleMode::Provided).unwrap();
892
+ assert_eq!(doc.producer.as_deref(), Some("Skia/PDF m151"));
893
+ let md = output::markdown::to_markdown(&doc).unwrap();
894
+ assert!(
895
+ !md.contains("# Penpot - Render"),
896
+ "artifact metadata title must not become H1, got:\n{md}"
897
+ );
898
+ // Native text from the fixture should still appear.
899
+ assert!(
900
+ md.contains("NAME VALUE") || md.contains("ACME"),
901
+ "expected native text in markdown, got:\n{md}"
902
+ );
903
+ // Reading order: title (higher on page) before value text.
904
+ if let (Some(a), Some(b)) = (md.find("ACME"), md.find("NAME VALUE")) {
905
+ assert!(a < b, "expected ACME before NAME VALUE in:\n{md}");
906
+ }
907
+
908
+ // Page-edge lines should not dominate kids.
909
+ let edge_lines = doc
910
+ .kids
911
+ .iter()
912
+ .filter(|e| {
913
+ matches!(
914
+ e,
915
+ models::content::ContentElement::Line(l)
916
+ if l.bbox.width() < 2.0 || l.bbox.height() < 2.0
917
+ )
918
+ })
919
+ .count();
920
+ assert_eq!(edge_lines, 0, "pattern fill must not emit page-edge lines");
921
+ assert!(
922
+ !doc.kids.iter().any(|e| matches!(
923
+ e,
924
+ models::content::ContentElement::Image(_)
925
+ | models::content::ContentElement::Figure(_)
926
+ )),
927
+ "page-sized TextBlocks source image should be dropped from layout"
928
+ );
929
+ }
930
+
931
+ #[test]
932
+ fn artifact_metadata_title_skipped_in_markdown() {
933
+ let mut doc = PdfDocument::new("x.pdf".into());
934
+ doc.title = Some("Penpot - Render".into());
935
+ doc.producer = Some("Skia/PDF m151".into());
936
+ doc.creator = Some("Chromium".into());
937
+ doc.number_of_pages = 1;
938
+ // Body text present but does not corroborate Info.Title → skip H1.
939
+ doc.kids.push(models::content::ContentElement::TextChunk(
940
+ models::chunks::TextChunk {
941
+ value: "MANSUY RAPHAEL".into(),
942
+ bbox: models::bbox::BoundingBox::new(Some(1), 10.0, 100.0, 200.0, 120.0),
943
+ font_name: "Helvetica".into(),
944
+ font_size: 12.0,
945
+ font_weight: 400.0,
946
+ italic_angle: 0.0,
947
+ font_color: "#000000".into(),
948
+ contrast_ratio: 21.0,
949
+ symbol_ends: vec![200.0],
950
+ text_format: models::enums::TextFormat::Normal,
951
+ text_type: models::enums::TextType::Regular,
952
+ pdf_layer: models::enums::PdfLayer::Main,
953
+ ocg_visible: true,
954
+ index: Some(0),
955
+ page_number: Some(1),
956
+ level: None,
957
+ mcid: None,
958
+ },
959
+ ));
960
+ let md = output::markdown::to_markdown(&doc).unwrap();
961
+ assert!(
962
+ !md.contains("# Penpot - Render"),
963
+ "uncorroborated metadata title must be skipped when body text exists, got:\n{md}"
964
+ );
965
+ assert!(md.contains("MANSUY RAPHAEL"), "got:\n{md}");
966
+ }
779
967
  }