docvortex 0.2.1__py3-none-any.whl
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.
- docvortex/__init__.py +21 -0
- docvortex/analyzers/__init__.py +3 -0
- docvortex/analyzers/native/__init__.py +37 -0
- docvortex/analyzers/native/_shared/__init__.py +3 -0
- docvortex/analyzers/native/_shared/hyperlink.py +16 -0
- docvortex/analyzers/native/_shared/image.py +8 -0
- docvortex/analyzers/native/_shared/markup/__init__.py +47 -0
- docvortex/analyzers/native/_shared/markup/anchors.py +30 -0
- docvortex/analyzers/native/_shared/markup/formula.py +37 -0
- docvortex/analyzers/native/_shared/markup/projector.py +57 -0
- docvortex/analyzers/native/_shared/markup/styles.py +19 -0
- docvortex/analyzers/native/_shared/mathml.py +17 -0
- docvortex/analyzers/native/_shared/names.py +7 -0
- docvortex/analyzers/native/_shared/xycut.py +414 -0
- docvortex/analyzers/native/contracts.py +44 -0
- docvortex/analyzers/native/csv.py +351 -0
- docvortex/analyzers/native/epub/__init__.py +18 -0
- docvortex/analyzers/native/epub/constants.py +45 -0
- docvortex/analyzers/native/epub/converter.py +80 -0
- docvortex/analyzers/native/epub/errors.py +20 -0
- docvortex/analyzers/native/epub/metadata.py +31 -0
- docvortex/analyzers/native/epub/package.py +649 -0
- docvortex/analyzers/native/epub/xhtml.py +306 -0
- docvortex/analyzers/native/html/__init__.py +6 -0
- docvortex/analyzers/native/html/anchors.py +271 -0
- docvortex/analyzers/native/html/constants.py +25 -0
- docvortex/analyzers/native/html/contracts.py +7 -0
- docvortex/analyzers/native/html/converter.py +117 -0
- docvortex/analyzers/native/html/document.py +389 -0
- docvortex/analyzers/native/html/errors.py +12 -0
- docvortex/analyzers/native/html/resources.py +365 -0
- docvortex/analyzers/native/html/selector.py +421 -0
- docvortex/analyzers/native/models.py +209 -0
- docvortex/analyzers/native/ofd/__init__.py +14 -0
- docvortex/analyzers/native/ofd/constants.py +59 -0
- docvortex/analyzers/native/ofd/converter.py +31 -0
- docvortex/analyzers/native/ofd/errors.py +16 -0
- docvortex/analyzers/native/ofd/geometry.py +215 -0
- docvortex/analyzers/native/ofd/images.py +124 -0
- docvortex/analyzers/native/ofd/metadata.py +53 -0
- docvortex/analyzers/native/ofd/models.py +170 -0
- docvortex/analyzers/native/ofd/package.py +346 -0
- docvortex/analyzers/native/ofd/path.py +201 -0
- docvortex/analyzers/native/ofd/reading_order.py +293 -0
- docvortex/analyzers/native/ofd/resources.py +122 -0
- docvortex/analyzers/native/ofd/scene.py +347 -0
- docvortex/analyzers/native/ofd/table.py +240 -0
- docvortex/analyzers/native/ofd/text.py +521 -0
- docvortex/analyzers/native/office/__init__.py +3 -0
- docvortex/analyzers/native/office/doc/__init__.py +3 -0
- docvortex/analyzers/native/office/doc/bookmarks.py +81 -0
- docvortex/analyzers/native/office/doc/doc_converter.py +573 -0
- docvortex/analyzers/native/office/doc/fib.py +261 -0
- docvortex/analyzers/native/office/doc/fields.py +101 -0
- docvortex/analyzers/native/office/doc/formatting.py +171 -0
- docvortex/analyzers/native/office/doc/images.py +162 -0
- docvortex/analyzers/native/office/doc/lists.py +356 -0
- docvortex/analyzers/native/office/doc/models.py +164 -0
- docvortex/analyzers/native/office/doc/parser.py +851 -0
- docvortex/analyzers/native/office/doc/pieces.py +256 -0
- docvortex/analyzers/native/office/doc/records.py +40 -0
- docvortex/analyzers/native/office/doc/sprm.py +269 -0
- docvortex/analyzers/native/office/doc/styles.py +215 -0
- docvortex/analyzers/native/office/docx/__init__.py +3 -0
- docvortex/analyzers/native/office/docx/context.py +71 -0
- docvortex/analyzers/native/office/docx/docx_converter.py +614 -0
- docvortex/analyzers/native/office/docx/equationxml.py +119 -0
- docvortex/analyzers/native/office/docx/fields.py +816 -0
- docvortex/analyzers/native/office/docx/formatting_types.py +23 -0
- docvortex/analyzers/native/office/docx/main.py +50 -0
- docvortex/analyzers/native/office/docx/numbering.py +491 -0
- docvortex/analyzers/native/office/docx/office_xml.py +57 -0
- docvortex/analyzers/native/office/docx/package_normalizer.py +248 -0
- docvortex/analyzers/native/office/docx/resources.py +526 -0
- docvortex/analyzers/native/office/docx/styles.py +693 -0
- docvortex/analyzers/native/office/docx/tables.py +515 -0
- docvortex/analyzers/native/office/equation/__init__.py +3 -0
- docvortex/analyzers/native/office/equation/image.py +470 -0
- docvortex/analyzers/native/office/equation/latex_dict.py +324 -0
- docvortex/analyzers/native/office/equation/mtef.py +885 -0
- docvortex/analyzers/native/office/equation/mtef_v5.py +941 -0
- docvortex/analyzers/native/office/equation/omml.py +561 -0
- docvortex/analyzers/native/office/equation/ooxml.py +62 -0
- docvortex/analyzers/native/office/errors.py +33 -0
- docvortex/analyzers/native/office/image.py +307 -0
- docvortex/analyzers/native/office/legacy/__init__.py +3 -0
- docvortex/analyzers/native/office/legacy/binary.py +43 -0
- docvortex/analyzers/native/office/legacy/officeart.py +362 -0
- docvortex/analyzers/native/office/legacy/ole.py +110 -0
- docvortex/analyzers/native/office/limits.py +12 -0
- docvortex/analyzers/native/office/odf/__init__.py +3 -0
- docvortex/analyzers/native/office/odf/chart.py +76 -0
- docvortex/analyzers/native/office/odf/constants.py +70 -0
- docvortex/analyzers/native/office/odf/converters.py +403 -0
- docvortex/analyzers/native/office/odf/errors.py +18 -0
- docvortex/analyzers/native/office/odf/metadata.py +91 -0
- docvortex/analyzers/native/office/odf/models.py +176 -0
- docvortex/analyzers/native/office/odf/package.py +270 -0
- docvortex/analyzers/native/office/odf/styles.py +329 -0
- docvortex/analyzers/native/office/odf/table.py +469 -0
- docvortex/analyzers/native/office/odf/text.py +1002 -0
- docvortex/analyzers/native/office/ooxml_chart.py +1016 -0
- docvortex/analyzers/native/office/opc.py +38 -0
- docvortex/analyzers/native/office/ppt/__init__.py +3 -0
- docvortex/analyzers/native/office/ppt/models.py +118 -0
- docvortex/analyzers/native/office/ppt/parser.py +1895 -0
- docvortex/analyzers/native/office/ppt/ppt_converter.py +292 -0
- docvortex/analyzers/native/office/ppt/records.py +131 -0
- docvortex/analyzers/native/office/ppt/style_text.py +247 -0
- docvortex/analyzers/native/office/pptx/__init__.py +3 -0
- docvortex/analyzers/native/office/pptx/context.py +113 -0
- docvortex/analyzers/native/office/pptx/lists.py +558 -0
- docvortex/analyzers/native/office/pptx/main.py +20 -0
- docvortex/analyzers/native/office/pptx/package_normalizer.py +321 -0
- docvortex/analyzers/native/office/pptx/pptx_converter.py +323 -0
- docvortex/analyzers/native/office/pptx/resources.py +329 -0
- docvortex/analyzers/native/office/pptx/shapes.py +393 -0
- docvortex/analyzers/native/office/pptx/text_styles.py +625 -0
- docvortex/analyzers/native/office/pptx/titles.py +178 -0
- docvortex/analyzers/native/office/rich_text.py +420 -0
- docvortex/analyzers/native/office/rtf/__init__.py +3 -0
- docvortex/analyzers/native/office/rtf/converter.py +708 -0
- docvortex/analyzers/native/office/rtf/lexer.py +213 -0
- docvortex/analyzers/native/office/rtf/math.py +339 -0
- docvortex/analyzers/native/office/rtf/models.py +185 -0
- docvortex/analyzers/native/office/rtf/parser.py +1553 -0
- docvortex/analyzers/native/office/spreadsheet/__init__.py +3 -0
- docvortex/analyzers/native/office/spreadsheet/html.py +81 -0
- docvortex/analyzers/native/office/spreadsheet/models.py +79 -0
- docvortex/analyzers/native/office/spreadsheet/projector.py +928 -0
- docvortex/analyzers/native/office/streams.py +18 -0
- docvortex/analyzers/native/office/xls/__init__.py +3 -0
- docvortex/analyzers/native/office/xls/chart.py +132 -0
- docvortex/analyzers/native/office/xls/embedded_chart.py +299 -0
- docvortex/analyzers/native/office/xls/models.py +109 -0
- docvortex/analyzers/native/office/xls/number_format.py +521 -0
- docvortex/analyzers/native/office/xls/parser.py +1145 -0
- docvortex/analyzers/native/office/xls/records.py +201 -0
- docvortex/analyzers/native/office/xls/strings.py +205 -0
- docvortex/analyzers/native/office/xls/xls_converter.py +354 -0
- docvortex/analyzers/native/office/xlsx/__init__.py +3 -0
- docvortex/analyzers/native/office/xlsx/main.py +20 -0
- docvortex/analyzers/native/office/xlsx/ooxml_ole.py +522 -0
- docvortex/analyzers/native/office/xlsx/package_normalizer.py +310 -0
- docvortex/analyzers/native/office/xlsx/xlsx_converter.py +716 -0
- docvortex/analyzers/native/pdf/__init__.py +3 -0
- docvortex/analyzers/native/pdf/auxiliary_text.py +1671 -0
- docvortex/analyzers/native/pdf/char_geometry.py +1662 -0
- docvortex/analyzers/native/pdf/code_blocks.py +535 -0
- docvortex/analyzers/native/pdf/formulas.py +1985 -0
- docvortex/analyzers/native/pdf/geometry.py +281 -0
- docvortex/analyzers/native/pdf/graphics.py +1501 -0
- docvortex/analyzers/native/pdf/index_blocks.py +268 -0
- docvortex/analyzers/native/pdf/inline/__init__.py +3 -0
- docvortex/analyzers/native/pdf/inline/common.py +112 -0
- docvortex/analyzers/native/pdf/inline/detection.py +590 -0
- docvortex/analyzers/native/pdf/inline/matching.py +1185 -0
- docvortex/analyzers/native/pdf/inline/materialize.py +457 -0
- docvortex/analyzers/native/pdf/inline/scripts.py +975 -0
- docvortex/analyzers/native/pdf/inline/types.py +385 -0
- docvortex/analyzers/native/pdf/line_layout.py +1106 -0
- docvortex/analyzers/native/pdf/line_merging.py +1223 -0
- docvortex/analyzers/native/pdf/models.py +246 -0
- docvortex/analyzers/native/pdf/native_text.py +1004 -0
- docvortex/analyzers/native/pdf/pipeline.py +1390 -0
- docvortex/analyzers/native/pdf/script_geometry.py +636 -0
- docvortex/analyzers/native/pdf/shared.py +30 -0
- docvortex/analyzers/native/pdf/spatial_text.py +383 -0
- docvortex/analyzers/native/pdf/table_annotations.py +446 -0
- docvortex/analyzers/native/pdf/table_constants.py +48 -0
- docvortex/analyzers/native/pdf/table_detection.py +227 -0
- docvortex/analyzers/native/pdf/table_filled_grid.py +218 -0
- docvortex/analyzers/native/pdf/table_geometry.py +40 -0
- docvortex/analyzers/native/pdf/table_materialization.py +444 -0
- docvortex/analyzers/native/pdf/table_recovery/__init__.py +15 -0
- docvortex/analyzers/native/pdf/table_recovery/candidate.py +356 -0
- docvortex/analyzers/native/pdf/table_recovery/contracts.py +154 -0
- docvortex/analyzers/native/pdf/table_recovery/engine.py +609 -0
- docvortex/analyzers/native/pdf/table_recovery/geometry.py +164 -0
- docvortex/analyzers/native/pdf/table_recovery/sparse_common.py +85 -0
- docvortex/analyzers/native/pdf/table_recovery/sparse_hybrid.py +804 -0
- docvortex/analyzers/native/pdf/table_recovery/sparse_multiline.py +1122 -0
- docvortex/analyzers/native/pdf/table_recovery/text.py +414 -0
- docvortex/analyzers/native/pdf/table_recovery/text_grid.py +618 -0
- docvortex/analyzers/native/pdf/table_recovery/vector.py +1931 -0
- docvortex/analyzers/native/pdf/table_rows.py +34 -0
- docvortex/analyzers/native/pdf/table_rules.py +1129 -0
- docvortex/analyzers/native/pdf/table_text_styles.py +283 -0
- docvortex/analyzers/native/pdf/tables.py +147 -0
- docvortex/analyzers/native/pdf/text_assembly/__init__.py +3 -0
- docvortex/analyzers/native/pdf/text_assembly/annotations.py +581 -0
- docvortex/analyzers/native/pdf/text_assembly/assembly.py +292 -0
- docvortex/analyzers/native/pdf/text_assembly/common.py +477 -0
- docvortex/analyzers/native/pdf/text_assembly/footnotes.py +394 -0
- docvortex/analyzers/native/pdf/text_assembly/merging.py +1274 -0
- docvortex/analyzers/native/pdf/text_assembly/rows.py +692 -0
- docvortex/analyzers/native/pdf/text_blocks.py +82 -0
- docvortex/analyzers/native/pdf/text_styles.py +55 -0
- docvortex/analyzers/native/pdf/title_analysis/__init__.py +3 -0
- docvortex/analyzers/native/pdf/title_analysis/body_profile.py +215 -0
- docvortex/analyzers/native/pdf/title_analysis/common.py +117 -0
- docvortex/analyzers/native/pdf/title_analysis/document_profile.py +164 -0
- docvortex/analyzers/native/pdf/title_analysis/lane_titles.py +758 -0
- docvortex/analyzers/native/pdf/title_analysis/page_titles.py +1024 -0
- docvortex/analyzers/native/pdf/title_analysis/prototype.py +194 -0
- docvortex/analyzers/native/pdf/title_analysis/structural.py +1081 -0
- docvortex/analyzers/native/pdf/titles.py +75 -0
- docvortex/analyzers/native/pdf/typography.py +19 -0
- docvortex/analyzers/native/pdf/visual_annotations.py +1262 -0
- docvortex/api.py +180 -0
- docvortex/assets/__init__.py +5 -0
- docvortex/assets/store.py +51 -0
- docvortex/cli.py +54 -0
- docvortex/codecs/__init__.py +3 -0
- docvortex/codecs/html/__init__.py +22 -0
- docvortex/codecs/html/contracts.py +236 -0
- docvortex/codecs/html/materializer.py +331 -0
- docvortex/codecs/html/parser.py +763 -0
- docvortex/codecs/html/resources.py +34 -0
- docvortex/codecs/json.py +17 -0
- docvortex/content/__init__.py +5 -0
- docvortex/content/inline.py +248 -0
- docvortex/content/markup/__init__.py +44 -0
- docvortex/content/markup/anchors.py +188 -0
- docvortex/content/markup/formula.py +280 -0
- docvortex/content/markup/projector.py +1237 -0
- docvortex/content/markup/styles.py +327 -0
- docvortex/content/mathml.py +167 -0
- docvortex/content/normalization.py +188 -0
- docvortex/content/spans.py +183 -0
- docvortex/content/table/__init__.py +18 -0
- docvortex/content/table/blocks.py +152 -0
- docvortex/content/table/content.py +425 -0
- docvortex/content/table/document.py +104 -0
- docvortex/content/table/html.py +399 -0
- docvortex/content/table/models.py +76 -0
- docvortex/content/table/rules.py +42 -0
- docvortex/content/table/structure.py +221 -0
- docvortex/content/tree.py +5 -0
- docvortex/document/__init__.py +3 -0
- docvortex/document/contracts.py +22 -0
- docvortex/document/detection.py +389 -0
- docvortex/document/filetypes.py +175 -0
- docvortex/document/page_range.py +167 -0
- docvortex/document/pdf/__init__.py +19 -0
- docvortex/document/pdf/classify.py +1138 -0
- docvortex/document/pdf/constants.py +132 -0
- docvortex/document/pdf/diagnostics.py +350 -0
- docvortex/document/pdf/document.py +582 -0
- docvortex/document/pdf/font_runtime.py +335 -0
- docvortex/document/pdf/geometry.py +31 -0
- docvortex/document/pdf/images.py +654 -0
- docvortex/document/pdf/native_annotations.py +367 -0
- docvortex/document/pdf/native_contracts.py +169 -0
- docvortex/document/pdf/native_coordinates.py +216 -0
- docvortex/document/pdf/native_lifecycle.py +16 -0
- docvortex/document/pdf/native_objects.py +902 -0
- docvortex/document/pdf/native_text_geometry.py +315 -0
- docvortex/document/pdf/pdfium.py +325 -0
- docvortex/document/pdf/raster.py +46 -0
- docvortex/document/pdf/text/__init__.py +62 -0
- docvortex/document/pdf/text/contracts.py +211 -0
- docvortex/document/pdf/text/extract.py +165 -0
- docvortex/document/pdf/text/geometry.py +16 -0
- docvortex/document/pdf/text/groups.py +162 -0
- docvortex/document/pdf/visual_geometry.py +201 -0
- docvortex/document/pdf/visuals.py +343 -0
- docvortex/document/source.py +92 -0
- docvortex/errors.py +21 -0
- docvortex/export/__init__.py +3 -0
- docvortex/export/bundle.py +98 -0
- docvortex/export/files.py +66 -0
- docvortex/export/middle.py +208 -0
- docvortex/foundation/__init__.py +3 -0
- docvortex/foundation/geometry.py +125 -0
- docvortex/foundation/hyperlink.py +65 -0
- docvortex/foundation/image.py +48 -0
- docvortex/foundation/image_encoding.py +30 -0
- docvortex/foundation/image_payload.py +280 -0
- docvortex/foundation/language.py +92 -0
- docvortex/foundation/platform.py +38 -0
- docvortex/foundation/text.py +153 -0
- docvortex/foundation/type_identity.py +20 -0
- docvortex/foundation/xml_names.py +20 -0
- docvortex/options.py +30 -0
- docvortex/postprocess/__init__.py +3 -0
- docvortex/postprocess/content.py +53 -0
- docvortex/postprocess/document.py +19 -0
- docvortex/postprocess/lists.py +236 -0
- docvortex/postprocess/page_blocks.py +214 -0
- docvortex/postprocess/pages.py +95 -0
- docvortex/postprocess/paragraphs.py +580 -0
- docvortex/postprocess/visual.py +715 -0
- docvortex/render/__init__.py +48 -0
- docvortex/render/_internal/__init__.py +3 -0
- docvortex/render/_internal/common/__init__.py +3 -0
- docvortex/render/_internal/common/context.py +43 -0
- docvortex/render/_internal/common/html_table.py +178 -0
- docvortex/render/_internal/common/index.py +33 -0
- docvortex/render/_internal/common/list_items.py +158 -0
- docvortex/render/_internal/common/planner.py +140 -0
- docvortex/render/_internal/docx/__init__.py +3 -0
- docvortex/render/_internal/docx/assets.py +202 -0
- docvortex/render/_internal/docx/inline.py +434 -0
- docvortex/render/_internal/docx/math.py +220 -0
- docvortex/render/_internal/docx/renderer.py +905 -0
- docvortex/render/_internal/docx/styles.py +195 -0
- docvortex/render/_internal/docx/table.py +442 -0
- docvortex/render/_internal/epub/__init__.py +5 -0
- docvortex/render/_internal/epub/assets.py +173 -0
- docvortex/render/_internal/epub/package.py +249 -0
- docvortex/render/_internal/epub/renderer.py +1156 -0
- docvortex/render/_internal/html/__init__.py +3 -0
- docvortex/render/_internal/html/inline.py +346 -0
- docvortex/render/_internal/html/renderer.py +1041 -0
- docvortex/render/_internal/html/sanitizer.py +478 -0
- docvortex/render/_internal/html/table.py +121 -0
- docvortex/render/_internal/latex/__init__.py +1 -0
- docvortex/render/_internal/latex/assets.py +85 -0
- docvortex/render/_internal/latex/inline.py +145 -0
- docvortex/render/_internal/latex/renderer.py +506 -0
- docvortex/render/_internal/latex/table.py +347 -0
- docvortex/render/_internal/markdown/__init__.py +3 -0
- docvortex/render/_internal/markdown/assets.py +78 -0
- docvortex/render/_internal/markdown/blocks.py +635 -0
- docvortex/render/_internal/markdown/escaping.py +51 -0
- docvortex/render/_internal/markdown/inline.py +260 -0
- docvortex/render/_internal/markdown/renderer.py +93 -0
- docvortex/render/_internal/markdown/table.py +281 -0
- docvortex/render/_internal/pdf/__init__.py +3 -0
- docvortex/render/_internal/pdf/assets.py +197 -0
- docvortex/render/_internal/pdf/formula.py +417 -0
- docvortex/render/_internal/pdf/inline.py +343 -0
- docvortex/render/_internal/pdf/renderer.py +734 -0
- docvortex/render/_internal/pdf/styles.py +206 -0
- docvortex/render/_internal/pdf/table.py +272 -0
- docvortex/render/_internal/structured_content/__init__.py +3 -0
- docvortex/render/_internal/structured_content/renderer.py +193 -0
- docvortex/render/api.py +199 -0
- docvortex/render/contracts.py +205 -0
- docvortex/render/docx.py +38 -0
- docvortex/render/epub.py +35 -0
- docvortex/render/fragments.py +70 -0
- docvortex/render/html.py +29 -0
- docvortex/render/latex.py +24 -0
- docvortex/render/markdown.py +50 -0
- docvortex/render/pdf.py +25 -0
- docvortex/render/structured_content.py +23 -0
- docvortex/resources/epub/docvortex.css +91 -0
- docvortex/resources/fasttext-langdetect/lid.176.ftz +0 -0
- docvortex/resources/fonts/DroidSansFallbackFull.ttf +0 -0
- docvortex/resources/fonts/NOTICE +190 -0
- docvortex/resources/fonts/manifest.json +9 -0
- docvortex/resources/html/docvortex.css +601 -0
- docvortex/resources/html/docvortex.min.css +1 -0
- docvortex/result.py +91 -0
- docvortex/schema.py +1141 -0
- docvortex/version.py +3 -0
- docvortex-0.2.1.dist-info/METADATA +193 -0
- docvortex-0.2.1.dist-info/RECORD +364 -0
- docvortex-0.2.1.dist-info/WHEEL +5 -0
- docvortex-0.2.1.dist-info/entry_points.txt +2 -0
- docvortex-0.2.1.dist-info/licenses/LICENSE.md +21 -0
- docvortex-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""PDF 表格与注释输出物化;保留原有认领顺序与判定规则。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Any
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from ....document.pdf.text.contracts import Char
|
|
7
|
+
from .table_recovery import (
|
|
8
|
+
NativeTableInput,
|
|
9
|
+
NativeTableRectangle,
|
|
10
|
+
NativeTableRule,
|
|
11
|
+
coerce_native_table_rectangles,
|
|
12
|
+
coerce_native_table_rules,
|
|
13
|
+
recover_native_pdf_table,
|
|
14
|
+
)
|
|
15
|
+
from .table_text_styles import render_native_table_html_with_scripts
|
|
16
|
+
from ....foundation.text import merge_text_line_contents
|
|
17
|
+
from .spatial_text import project_pdf_table_text
|
|
18
|
+
from ....schema import BBox
|
|
19
|
+
from ....foundation.language import detect_lang
|
|
20
|
+
from .models import _LineItem, _PageSource, _TableAnnotation, _TableCandidate
|
|
21
|
+
from .geometry import (
|
|
22
|
+
_bbox_axis_overlap_ratio,
|
|
23
|
+
_bbox_center_x,
|
|
24
|
+
_bbox_center_y,
|
|
25
|
+
_bbox_overlap_in_smaller,
|
|
26
|
+
_bbox_union,
|
|
27
|
+
_bbox_union_many,
|
|
28
|
+
_point_in_bbox,
|
|
29
|
+
_rotate_bbox_to_upright,
|
|
30
|
+
)
|
|
31
|
+
from .line_layout import _font_signatures_share_family, _line_effective_height, _line_tight_output_bbox
|
|
32
|
+
from .line_merging import _same_baseline_geometry
|
|
33
|
+
from .native_text import _normalize_native_run_text
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _recover_native_table_html(
|
|
37
|
+
source: _PageSource,
|
|
38
|
+
table_bbox: BBox,
|
|
39
|
+
angle: int,
|
|
40
|
+
chars: tuple[Char, ...] | None = None,
|
|
41
|
+
drawing_lines: tuple[NativeTableRule, ...] | None = None,
|
|
42
|
+
rectangles: tuple[NativeTableRectangle, ...] | None = None,
|
|
43
|
+
tight_bboxes: dict[int, BBox] | None = None,
|
|
44
|
+
origins: dict[int, tuple[float, float]] | None = None,
|
|
45
|
+
) -> str:
|
|
46
|
+
"""使用共享原生字符与绘图原语恢复高置信表格 HTML。"""
|
|
47
|
+
|
|
48
|
+
table_input = NativeTableInput(
|
|
49
|
+
table_bbox=table_bbox,
|
|
50
|
+
page_size=source.page_size,
|
|
51
|
+
angle=angle,
|
|
52
|
+
chars=chars if chars is not None else tuple(source.chars),
|
|
53
|
+
drawing_lines=(drawing_lines if drawing_lines is not None else coerce_native_table_rules(source.drawing_lines)),
|
|
54
|
+
rectangles=(rectangles if rectangles is not None else coerce_native_table_rectangles(source.path_infos)),
|
|
55
|
+
)
|
|
56
|
+
result = recover_native_pdf_table(table_input)
|
|
57
|
+
if result is None:
|
|
58
|
+
return ""
|
|
59
|
+
return render_native_table_html_with_scripts(
|
|
60
|
+
result,
|
|
61
|
+
table_input,
|
|
62
|
+
tight_bboxes or {},
|
|
63
|
+
origins or {},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _materialize_table_blocks(
|
|
68
|
+
source: _PageSource,
|
|
69
|
+
candidates: list[_TableCandidate],
|
|
70
|
+
*,
|
|
71
|
+
tight_bboxes: dict[int, BBox] | None = None,
|
|
72
|
+
origins: dict[int, tuple[float, float]] | None = None,
|
|
73
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], set[int]]:
|
|
74
|
+
"""原子物化表体及其独立注释,仅认领整组成功输出的文本行。"""
|
|
75
|
+
|
|
76
|
+
table_blocks: list[dict[str, Any]] = []
|
|
77
|
+
annotation_blocks: list[dict[str, Any]] = []
|
|
78
|
+
accepted_candidate_bboxes: list[BBox] = []
|
|
79
|
+
claimed: set[int] = set()
|
|
80
|
+
native_chars = tuple(source.chars)
|
|
81
|
+
native_rules = coerce_native_table_rules(source.drawing_lines)
|
|
82
|
+
native_rectangles = coerce_native_table_rectangles(source.path_infos)
|
|
83
|
+
for candidate in sorted(candidates, key=lambda item: item.score, reverse=True):
|
|
84
|
+
# 候选去重仍使用包含注释的完整框,不能因输出表体收缩而放行重复表格。
|
|
85
|
+
if any(_bbox_overlap_in_smaller(candidate.bbox, bbox) >= 0.5 for bbox in accepted_candidate_bboxes):
|
|
86
|
+
continue
|
|
87
|
+
output_angle = candidate.angle
|
|
88
|
+
candidate_annotation_blocks, externalized_line_indices, failed_annotations = _materialize_table_annotations(
|
|
89
|
+
source, candidate
|
|
90
|
+
)
|
|
91
|
+
projection_line_indices = _candidate_projection_line_indices(source, candidate)
|
|
92
|
+
for annotation in candidate.annotations:
|
|
93
|
+
projection_line_indices.update(annotation.line_indices)
|
|
94
|
+
projection_line_indices.difference_update(externalized_line_indices)
|
|
95
|
+
body_bbox = _table_body_materialization_bbox(
|
|
96
|
+
candidate,
|
|
97
|
+
failed_annotations,
|
|
98
|
+
)
|
|
99
|
+
content = ""
|
|
100
|
+
try:
|
|
101
|
+
content = _recover_native_table_html(
|
|
102
|
+
source,
|
|
103
|
+
body_bbox,
|
|
104
|
+
candidate.angle,
|
|
105
|
+
native_chars,
|
|
106
|
+
native_rules,
|
|
107
|
+
native_rectangles,
|
|
108
|
+
tight_bboxes,
|
|
109
|
+
origins,
|
|
110
|
+
)
|
|
111
|
+
except Exception as exc:
|
|
112
|
+
logger.warning(
|
|
113
|
+
f"Flash native table recovery failed and fell back to projection: bbox={candidate.bbox}, error={exc}"
|
|
114
|
+
)
|
|
115
|
+
if content:
|
|
116
|
+
logger.debug(f"Flash native table recovery accepted: bbox={body_bbox}, angle={candidate.angle}")
|
|
117
|
+
try:
|
|
118
|
+
if not content:
|
|
119
|
+
# 使用完整原始字符流保留 PDF 物理换行;行索引仅负责表体与注释的所有权认领。
|
|
120
|
+
content = project_pdf_table_text(
|
|
121
|
+
source.chars,
|
|
122
|
+
body_bbox,
|
|
123
|
+
angle=candidate.angle,
|
|
124
|
+
)
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
# 表体投影异常时同时撤销预构造注释,保持整组不输出、不认领。
|
|
127
|
+
logger.warning(f"Flash table projection failed and rolled back: bbox={candidate.bbox}, error={exc}")
|
|
128
|
+
continue
|
|
129
|
+
if not content or not content.strip():
|
|
130
|
+
continue
|
|
131
|
+
table_blocks.append(
|
|
132
|
+
{
|
|
133
|
+
"type": "table",
|
|
134
|
+
"bbox": body_bbox,
|
|
135
|
+
"angle": output_angle,
|
|
136
|
+
"content": content,
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
annotation_blocks.extend(candidate_annotation_blocks)
|
|
140
|
+
accepted_candidate_bboxes.append(candidate.bbox)
|
|
141
|
+
# 表体和成功外置的注释行在同一事务中各认领一次。
|
|
142
|
+
claimed.update(projection_line_indices | externalized_line_indices)
|
|
143
|
+
table_blocks.sort(key=lambda block: (block["bbox"][1], block["bbox"][0]))
|
|
144
|
+
annotation_blocks.sort(key=lambda block: (block["bbox"][1], block["bbox"][0]))
|
|
145
|
+
return table_blocks, annotation_blocks, claimed
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _materialize_table_annotations(
|
|
149
|
+
source: _PageSource,
|
|
150
|
+
candidate: _TableCandidate,
|
|
151
|
+
) -> tuple[list[dict[str, Any]], set[int], list[_TableAnnotation]]:
|
|
152
|
+
"""构造候选注释块,并返回成功外置行与需回退到表体的注释记录。"""
|
|
153
|
+
|
|
154
|
+
blocks: list[dict[str, Any]] = []
|
|
155
|
+
externalized_line_indices: set[int] = set()
|
|
156
|
+
failed_annotations: list[_TableAnnotation] = []
|
|
157
|
+
annotations = sorted(
|
|
158
|
+
candidate.annotations,
|
|
159
|
+
key=lambda annotation: (
|
|
160
|
+
_rotate_bbox_to_upright(
|
|
161
|
+
annotation.bbox,
|
|
162
|
+
source.page_size,
|
|
163
|
+
candidate.angle,
|
|
164
|
+
)[1],
|
|
165
|
+
_rotate_bbox_to_upright(
|
|
166
|
+
annotation.bbox,
|
|
167
|
+
source.page_size,
|
|
168
|
+
candidate.angle,
|
|
169
|
+
)[0],
|
|
170
|
+
annotation.kind,
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
for annotation in annotations:
|
|
174
|
+
annotation_blocks = _build_table_annotation_blocks(
|
|
175
|
+
source,
|
|
176
|
+
candidate,
|
|
177
|
+
annotation,
|
|
178
|
+
)
|
|
179
|
+
if not annotation_blocks:
|
|
180
|
+
failed_annotations.append(annotation)
|
|
181
|
+
continue
|
|
182
|
+
blocks.extend(annotation_blocks)
|
|
183
|
+
externalized_line_indices.update(annotation.line_indices)
|
|
184
|
+
return blocks, externalized_line_indices, failed_annotations
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _split_table_annotation_visual_groups(
|
|
188
|
+
line_geometry: list[tuple[_LineItem, BBox]],
|
|
189
|
+
) -> list[list[tuple[_LineItem, BBox]]]:
|
|
190
|
+
"""用短尾后的字体或字号重启拆分独立表格注释段。"""
|
|
191
|
+
|
|
192
|
+
if len(line_geometry) < 2:
|
|
193
|
+
return [line_geometry] if line_geometry else []
|
|
194
|
+
maximum_width = max(bbox[2] - bbox[0] for _line, bbox in line_geometry)
|
|
195
|
+
group_left = min(bbox[0] for _line, bbox in line_geometry)
|
|
196
|
+
groups = [[line_geometry[0]]]
|
|
197
|
+
for previous, current in zip(line_geometry, line_geometry[1:]):
|
|
198
|
+
previous_line, previous_bbox = previous
|
|
199
|
+
current_line, current_bbox = current
|
|
200
|
+
previous_width = previous_bbox[2] - previous_bbox[0]
|
|
201
|
+
current_width = current_bbox[2] - current_bbox[0]
|
|
202
|
+
pair_height = max(
|
|
203
|
+
_line_effective_height(*previous),
|
|
204
|
+
_line_effective_height(*current),
|
|
205
|
+
)
|
|
206
|
+
font_switch = (
|
|
207
|
+
previous_line.font_signature is not None
|
|
208
|
+
and current_line.font_signature is not None
|
|
209
|
+
and previous_line.font_coverage >= 0.6
|
|
210
|
+
and current_line.font_coverage >= 0.75
|
|
211
|
+
and not _font_signatures_share_family(
|
|
212
|
+
previous_line.font_signature,
|
|
213
|
+
current_line.font_signature,
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
scale_ratio = max(
|
|
217
|
+
previous_line.em_height or _line_effective_height(*previous),
|
|
218
|
+
current_line.em_height or _line_effective_height(*current),
|
|
219
|
+
) / max(
|
|
220
|
+
0.1,
|
|
221
|
+
min(
|
|
222
|
+
previous_line.em_height or _line_effective_height(*previous),
|
|
223
|
+
current_line.em_height or _line_effective_height(*current),
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
vertical_gap = current_bbox[1] - (previous_bbox[1] + _line_effective_height(*previous))
|
|
227
|
+
should_split = (
|
|
228
|
+
previous_width <= 0.45 * maximum_width
|
|
229
|
+
and current_width >= 0.75 * maximum_width
|
|
230
|
+
and current_bbox[0] - group_left <= 3.0 * pair_height
|
|
231
|
+
and -0.25 * pair_height <= vertical_gap <= 0.75 * pair_height
|
|
232
|
+
and (font_switch or scale_ratio >= 1.25)
|
|
233
|
+
)
|
|
234
|
+
if should_split:
|
|
235
|
+
groups.append([current])
|
|
236
|
+
else:
|
|
237
|
+
groups[-1].append(current)
|
|
238
|
+
return groups
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _build_table_annotation_blocks(
|
|
242
|
+
source: _PageSource,
|
|
243
|
+
candidate: _TableCandidate,
|
|
244
|
+
annotation: _TableAnnotation,
|
|
245
|
+
) -> list[dict[str, Any]]:
|
|
246
|
+
"""按视觉重启切分原生行,并生成保留排版元数据的独立注释块。"""
|
|
247
|
+
|
|
248
|
+
line_geometry = [
|
|
249
|
+
(
|
|
250
|
+
line,
|
|
251
|
+
_rotate_bbox_to_upright(
|
|
252
|
+
line.bbox,
|
|
253
|
+
source.page_size,
|
|
254
|
+
candidate.angle,
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
for line in source.lines
|
|
258
|
+
if line.source_index in annotation.line_indices
|
|
259
|
+
]
|
|
260
|
+
# 原生来源顺序可抵抗旋转文字同一基线上的字形顶边抖动。
|
|
261
|
+
line_geometry.sort(key=lambda item: item[0].source_index)
|
|
262
|
+
blocks = []
|
|
263
|
+
for group in _split_table_annotation_visual_groups(line_geometry):
|
|
264
|
+
content = _merge_table_annotation_content(
|
|
265
|
+
[line.text for line, _local_bbox in group],
|
|
266
|
+
)
|
|
267
|
+
if not content:
|
|
268
|
+
continue
|
|
269
|
+
local_output_line_bboxes = []
|
|
270
|
+
output_bbox_repaired = False
|
|
271
|
+
for line, local_bbox in group:
|
|
272
|
+
tight_candidate = _line_tight_output_bbox(
|
|
273
|
+
line,
|
|
274
|
+
source.page_size,
|
|
275
|
+
)
|
|
276
|
+
if tight_candidate is None:
|
|
277
|
+
local_output_line_bboxes.append(local_bbox)
|
|
278
|
+
else:
|
|
279
|
+
local_output_line_bboxes.append(
|
|
280
|
+
_rotate_bbox_to_upright(
|
|
281
|
+
tight_candidate,
|
|
282
|
+
source.page_size,
|
|
283
|
+
candidate.angle,
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
output_bbox_repaired = True
|
|
287
|
+
blocks.append(
|
|
288
|
+
{
|
|
289
|
+
"type": annotation.kind,
|
|
290
|
+
"bbox": _bbox_union_many(
|
|
291
|
+
[
|
|
292
|
+
annotation.line_bboxes.get(
|
|
293
|
+
line.source_index,
|
|
294
|
+
line.bbox,
|
|
295
|
+
)
|
|
296
|
+
for line, _local_bbox in group
|
|
297
|
+
]
|
|
298
|
+
),
|
|
299
|
+
"angle": candidate.angle,
|
|
300
|
+
"content": content,
|
|
301
|
+
"_local_line_bboxes": [bbox for _line, bbox in group],
|
|
302
|
+
"_local_output_line_bboxes": local_output_line_bboxes,
|
|
303
|
+
"_output_bbox_repaired": output_bbox_repaired,
|
|
304
|
+
"_line_heights": [_line_effective_height(line, bbox) for line, bbox in group],
|
|
305
|
+
"_font_signatures": {
|
|
306
|
+
line.font_signature
|
|
307
|
+
for line, _bbox in group
|
|
308
|
+
if line.font_signature is not None and line.font_coverage >= 0.5
|
|
309
|
+
},
|
|
310
|
+
# 已由表格检测器给出完整边界,禁止通用表注规则继续向下扩张。
|
|
311
|
+
"_table_annotation_complete": True,
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
return blocks
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _merge_table_annotation_content(line_texts: list[str]) -> str:
|
|
318
|
+
"""按正文一致的语言与行末断词规则折叠表格注释原生行。"""
|
|
319
|
+
|
|
320
|
+
normalized_lines = [normalized for text in line_texts if (normalized := _normalize_native_run_text(str(text or "")))]
|
|
321
|
+
if not normalized_lines:
|
|
322
|
+
return ""
|
|
323
|
+
try:
|
|
324
|
+
block_language = detect_lang("".join(normalized_lines))
|
|
325
|
+
except Exception:
|
|
326
|
+
block_language = ""
|
|
327
|
+
return merge_text_line_contents(
|
|
328
|
+
normalized_lines,
|
|
329
|
+
block_language=block_language,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _table_body_materialization_bbox(
|
|
334
|
+
candidate: _TableCandidate,
|
|
335
|
+
failed_annotations: list[_TableAnnotation],
|
|
336
|
+
) -> BBox:
|
|
337
|
+
"""返回排除有效注释后的表体框,并把无效注释边界保守并回表体。"""
|
|
338
|
+
|
|
339
|
+
if not candidate.annotations or len(failed_annotations) == len(candidate.annotations):
|
|
340
|
+
return candidate.bbox
|
|
341
|
+
body_bbox = candidate.core_bbox or candidate.bbox
|
|
342
|
+
for annotation in failed_annotations:
|
|
343
|
+
body_bbox = _bbox_union(body_bbox, annotation.bbox)
|
|
344
|
+
return body_bbox
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _candidate_projection_line_indices(
|
|
348
|
+
source: _PageSource,
|
|
349
|
+
candidate: _TableCandidate,
|
|
350
|
+
) -> set[int]:
|
|
351
|
+
"""合并核心成员、同基线续段及非零角度表格的表头文本。"""
|
|
352
|
+
|
|
353
|
+
line_indices = set(candidate.line_indices)
|
|
354
|
+
if candidate.core_bbox is not None:
|
|
355
|
+
for line in source.lines:
|
|
356
|
+
if _point_in_bbox(
|
|
357
|
+
(_bbox_center_x(line.bbox), _bbox_center_y(line.bbox)),
|
|
358
|
+
candidate.core_bbox,
|
|
359
|
+
):
|
|
360
|
+
line_indices.add(line.source_index)
|
|
361
|
+
if candidate.angle == 0:
|
|
362
|
+
_expand_candidate_same_baseline_members(source, candidate, line_indices)
|
|
363
|
+
return line_indices
|
|
364
|
+
if candidate.core_bbox is None:
|
|
365
|
+
return line_indices
|
|
366
|
+
|
|
367
|
+
candidate_local_bbox = _rotate_bbox_to_upright(
|
|
368
|
+
candidate.bbox,
|
|
369
|
+
source.page_size,
|
|
370
|
+
candidate.angle,
|
|
371
|
+
)
|
|
372
|
+
core_local_bbox = _rotate_bbox_to_upright(
|
|
373
|
+
candidate.core_bbox,
|
|
374
|
+
source.page_size,
|
|
375
|
+
candidate.angle,
|
|
376
|
+
)
|
|
377
|
+
for line in source.lines:
|
|
378
|
+
if line.angle != candidate.angle:
|
|
379
|
+
continue
|
|
380
|
+
page_center = (_bbox_center_x(line.bbox), _bbox_center_y(line.bbox))
|
|
381
|
+
if not _point_in_bbox(page_center, candidate.bbox):
|
|
382
|
+
continue
|
|
383
|
+
local_bbox = _rotate_bbox_to_upright(
|
|
384
|
+
line.bbox,
|
|
385
|
+
source.page_size,
|
|
386
|
+
candidate.angle,
|
|
387
|
+
)
|
|
388
|
+
local_center_y = _bbox_center_y(local_bbox)
|
|
389
|
+
if not candidate_local_bbox[1] <= local_center_y <= candidate_local_bbox[3]:
|
|
390
|
+
continue
|
|
391
|
+
if _bbox_axis_overlap_ratio(local_bbox, core_local_bbox, axis="x") < 0.05:
|
|
392
|
+
continue
|
|
393
|
+
line_indices.add(line.source_index)
|
|
394
|
+
return line_indices
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _expand_candidate_same_baseline_members(
|
|
398
|
+
source: _PageSource,
|
|
399
|
+
candidate: _TableCandidate,
|
|
400
|
+
line_indices: set[int],
|
|
401
|
+
) -> None:
|
|
402
|
+
"""迭代吸收完整候选框内与已认领成员同基线相邻的 angle=0 续段。"""
|
|
403
|
+
|
|
404
|
+
local_bboxes = {
|
|
405
|
+
line.source_index: _rotate_bbox_to_upright(
|
|
406
|
+
line.bbox,
|
|
407
|
+
source.page_size,
|
|
408
|
+
candidate.angle,
|
|
409
|
+
)
|
|
410
|
+
for line in source.lines
|
|
411
|
+
if line.angle == candidate.angle
|
|
412
|
+
}
|
|
413
|
+
changed = True
|
|
414
|
+
while changed:
|
|
415
|
+
changed = False
|
|
416
|
+
selected_lines = [line for line in source.lines if line.angle == candidate.angle and line.source_index in line_indices]
|
|
417
|
+
for line in source.lines:
|
|
418
|
+
if line.angle != candidate.angle or line.source_index in line_indices:
|
|
419
|
+
continue
|
|
420
|
+
if not _point_in_bbox(
|
|
421
|
+
(_bbox_center_x(line.bbox), _bbox_center_y(line.bbox)),
|
|
422
|
+
candidate.bbox,
|
|
423
|
+
):
|
|
424
|
+
continue
|
|
425
|
+
line_bbox = local_bboxes[line.source_index]
|
|
426
|
+
line_height = _line_effective_height(line, line_bbox)
|
|
427
|
+
for selected in selected_lines:
|
|
428
|
+
if (
|
|
429
|
+
line.font_signature is not None
|
|
430
|
+
and selected.font_signature is not None
|
|
431
|
+
and line.font_signature != selected.font_signature
|
|
432
|
+
):
|
|
433
|
+
continue
|
|
434
|
+
selected_bbox = local_bboxes[selected.source_index]
|
|
435
|
+
if not _same_baseline_geometry(
|
|
436
|
+
line_bbox,
|
|
437
|
+
line_height,
|
|
438
|
+
selected_bbox,
|
|
439
|
+
_line_effective_height(selected, selected_bbox),
|
|
440
|
+
):
|
|
441
|
+
continue
|
|
442
|
+
line_indices.add(line.source_index)
|
|
443
|
+
changed = True
|
|
444
|
+
break
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""面向已有 table bbox 的 Native PDF 表格结构恢复公共内部入口。"""
|
|
2
|
+
|
|
3
|
+
from .contracts import NativeTableCell, NativeTableInput, NativeTableRectangle, NativeTableResult, NativeTableRule
|
|
4
|
+
from .engine import coerce_native_table_rectangles, coerce_native_table_rules, recover_native_pdf_table
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"NativeTableCell",
|
|
8
|
+
"NativeTableInput",
|
|
9
|
+
"NativeTableRectangle",
|
|
10
|
+
"NativeTableResult",
|
|
11
|
+
"NativeTableRule",
|
|
12
|
+
"coerce_native_table_rectangles",
|
|
13
|
+
"coerce_native_table_rules",
|
|
14
|
+
"recover_native_pdf_table",
|
|
15
|
+
]
|