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,446 @@
|
|
|
1
|
+
"""PDF 表题和表注认领;保留原有认领顺序与判定规则。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import statistics
|
|
5
|
+
import unicodedata
|
|
6
|
+
from typing import Literal
|
|
7
|
+
from ....schema import BBox
|
|
8
|
+
from .models import _LineItem, _TableAnnotation, _TableCandidate, _VisualRow
|
|
9
|
+
from .geometry import (
|
|
10
|
+
_bbox_axis_overlap_ratio,
|
|
11
|
+
_bbox_center_y,
|
|
12
|
+
_bbox_union,
|
|
13
|
+
_bbox_union_many,
|
|
14
|
+
_coerce_bbox,
|
|
15
|
+
_rotate_bbox_to_upright,
|
|
16
|
+
)
|
|
17
|
+
from .line_layout import _line_effective_height
|
|
18
|
+
|
|
19
|
+
from .table_constants import _AUXILIARY_TABLE_NOTE_RE, _TABLE_CAPTION_RE, _TABLE_NOTE_RE, _TABLE_SPLIT_NUMBER_RE
|
|
20
|
+
from .table_rows import _clip_visual_row_to_corridor
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _build_table_annotation(
|
|
24
|
+
kind: Literal["caption", "footnote"],
|
|
25
|
+
rows: list[_VisualRow],
|
|
26
|
+
*,
|
|
27
|
+
excluded_line_indices: set[int] | None = None,
|
|
28
|
+
excluded_local_bbox: BBox | None = None,
|
|
29
|
+
) -> _TableAnnotation | None:
|
|
30
|
+
"""把已确认视觉行压缩成一个带精确来源行集合的表格注释记录。"""
|
|
31
|
+
|
|
32
|
+
excluded_line_indices = excluded_line_indices or set()
|
|
33
|
+
fragments = [
|
|
34
|
+
fragment
|
|
35
|
+
for row in rows
|
|
36
|
+
for fragment in row.fragments
|
|
37
|
+
if fragment.line_index not in excluded_line_indices
|
|
38
|
+
and not (
|
|
39
|
+
excluded_local_bbox is not None
|
|
40
|
+
and fragment.local_bbox[3] > excluded_local_bbox[1]
|
|
41
|
+
and _bbox_axis_overlap_ratio(
|
|
42
|
+
fragment.local_bbox,
|
|
43
|
+
excluded_local_bbox,
|
|
44
|
+
axis="x",
|
|
45
|
+
)
|
|
46
|
+
>= 0.05
|
|
47
|
+
)
|
|
48
|
+
]
|
|
49
|
+
if not fragments:
|
|
50
|
+
return None
|
|
51
|
+
line_bboxes: dict[int, BBox] = {}
|
|
52
|
+
for fragment in fragments:
|
|
53
|
+
existing_bbox = line_bboxes.get(fragment.line_index)
|
|
54
|
+
line_bboxes[fragment.line_index] = fragment.bbox if existing_bbox is None else _bbox_union(existing_bbox, fragment.bbox)
|
|
55
|
+
return _TableAnnotation(
|
|
56
|
+
kind=kind,
|
|
57
|
+
bbox=_bbox_union_many(list(line_bboxes.values())),
|
|
58
|
+
line_indices=set(line_bboxes),
|
|
59
|
+
line_bboxes=line_bboxes,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _collect_caption_rows(
|
|
64
|
+
rows: list[_VisualRow],
|
|
65
|
+
caption_line: _LineItem | None,
|
|
66
|
+
rule_bbox: BBox,
|
|
67
|
+
median_height: float,
|
|
68
|
+
) -> list[_VisualRow]:
|
|
69
|
+
"""收集显式标题所在行及其到表格上边界之间的连续换行。"""
|
|
70
|
+
|
|
71
|
+
if caption_line is None:
|
|
72
|
+
return []
|
|
73
|
+
caption_row_index = next(
|
|
74
|
+
(
|
|
75
|
+
index
|
|
76
|
+
for index, row in enumerate(rows)
|
|
77
|
+
if any(fragment.line_index == caption_line.source_index for fragment in row.fragments)
|
|
78
|
+
),
|
|
79
|
+
None,
|
|
80
|
+
)
|
|
81
|
+
if caption_row_index is None:
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
output: list[_VisualRow] = []
|
|
85
|
+
previous_bbox: BBox | None = None
|
|
86
|
+
margin = 2.0 * median_height
|
|
87
|
+
for index, row in enumerate(rows[caption_row_index:], start=caption_row_index):
|
|
88
|
+
clipped_row = _clip_visual_row_to_corridor(row, rule_bbox, margin=margin)
|
|
89
|
+
if clipped_row is None:
|
|
90
|
+
continue
|
|
91
|
+
if index > caption_row_index and clipped_row.center_y >= rule_bbox[1]:
|
|
92
|
+
break
|
|
93
|
+
if previous_bbox is not None and max(0.0, clipped_row.bbox[1] - previous_bbox[3]) > 2.0 * median_height:
|
|
94
|
+
break
|
|
95
|
+
output.append(clipped_row)
|
|
96
|
+
previous_bbox = clipped_row.bbox
|
|
97
|
+
|
|
98
|
+
if not output or rule_bbox[1] - output[-1].bbox[3] > 2.0 * median_height:
|
|
99
|
+
return []
|
|
100
|
+
return output
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _collect_footnote_rows(
|
|
104
|
+
rows: list[_VisualRow],
|
|
105
|
+
lines: list[_LineItem],
|
|
106
|
+
rule_bbox: BBox,
|
|
107
|
+
median_height: float,
|
|
108
|
+
core_line_indices: set[int],
|
|
109
|
+
page_size: tuple[float, float],
|
|
110
|
+
angle: int,
|
|
111
|
+
) -> list[_VisualRow]:
|
|
112
|
+
"""从表格下边界吸收具有表内引用和版面证据的表注连续行。"""
|
|
113
|
+
|
|
114
|
+
output: list[_VisualRow] = []
|
|
115
|
+
bottom = rule_bbox[3]
|
|
116
|
+
note_chain_started = False
|
|
117
|
+
margin = 2.0 * median_height
|
|
118
|
+
selected_line_indices = set(core_line_indices)
|
|
119
|
+
line_by_index = {line.source_index: line for line in lines}
|
|
120
|
+
core_lines = [line for line in lines if line.source_index in core_line_indices]
|
|
121
|
+
body_reference_height = _table_note_body_reference_height(
|
|
122
|
+
lines,
|
|
123
|
+
rule_bbox,
|
|
124
|
+
median_height,
|
|
125
|
+
core_line_indices,
|
|
126
|
+
page_size,
|
|
127
|
+
angle,
|
|
128
|
+
)
|
|
129
|
+
note_left: float | None = None
|
|
130
|
+
note_height: float | None = None
|
|
131
|
+
note_fonts: set[tuple[str, int]] = set()
|
|
132
|
+
for row in rows:
|
|
133
|
+
clipped_row = _clip_visual_row_to_corridor(row, rule_bbox, margin=margin)
|
|
134
|
+
if clipped_row is None or clipped_row.bbox[3] <= bottom:
|
|
135
|
+
continue
|
|
136
|
+
line_indices = {fragment.line_index for fragment in clipped_row.fragments}
|
|
137
|
+
if line_indices.issubset(selected_line_indices):
|
|
138
|
+
bottom = max(bottom, clipped_row.bbox[3])
|
|
139
|
+
continue
|
|
140
|
+
row_gap = max(0.0, clipped_row.bbox[1] - bottom)
|
|
141
|
+
row_lines = [line_by_index[line_index] for line_index in line_indices if line_index in line_by_index]
|
|
142
|
+
row_heights = [
|
|
143
|
+
_line_effective_height(
|
|
144
|
+
line,
|
|
145
|
+
_rotate_bbox_to_upright(line.bbox, page_size, angle),
|
|
146
|
+
)
|
|
147
|
+
for line in row_lines
|
|
148
|
+
]
|
|
149
|
+
row_height = statistics.median(row_heights) if row_heights else clipped_row.bbox[3] - clipped_row.bbox[1]
|
|
150
|
+
row_fonts = {
|
|
151
|
+
line.font_signature for line in row_lines if line.font_signature is not None and line.font_coverage >= 0.75
|
|
152
|
+
}
|
|
153
|
+
if note_chain_started and clipped_row.bbox[3] - rule_bbox[3] > 10.0 * median_height:
|
|
154
|
+
break
|
|
155
|
+
row_text = _visual_row_text(clipped_row)
|
|
156
|
+
explicit_note = _is_table_note_text(row_text)
|
|
157
|
+
auxiliary_marker = _extract_auxiliary_table_note_marker(row_text)
|
|
158
|
+
auxiliary_note = auxiliary_marker is not None and _table_core_references_marker(
|
|
159
|
+
auxiliary_marker,
|
|
160
|
+
core_lines,
|
|
161
|
+
page_size,
|
|
162
|
+
angle,
|
|
163
|
+
)
|
|
164
|
+
first_gap_limit = 0.75 if auxiliary_note and not explicit_note else 1.25
|
|
165
|
+
if row_gap > (first_gap_limit if not note_chain_started else 1.0) * median_height:
|
|
166
|
+
break
|
|
167
|
+
if not note_chain_started:
|
|
168
|
+
if not explicit_note and not auxiliary_note:
|
|
169
|
+
break
|
|
170
|
+
if explicit_note:
|
|
171
|
+
spatially_compatible = (
|
|
172
|
+
_bbox_axis_overlap_ratio(clipped_row.bbox, rule_bbox, axis="x") >= 0.35
|
|
173
|
+
and abs(clipped_row.bbox[0] - rule_bbox[0]) <= 2.0 * median_height
|
|
174
|
+
and row_height <= 1.15 * median_height
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
spatially_compatible = (
|
|
178
|
+
_bbox_axis_overlap_ratio(clipped_row.bbox, rule_bbox, axis="x") >= 0.50
|
|
179
|
+
and abs(clipped_row.bbox[0] - rule_bbox[0]) <= 3.0 * median_height
|
|
180
|
+
and row_height <= 1.05 * median_height
|
|
181
|
+
and row_height <= 0.90 * body_reference_height
|
|
182
|
+
)
|
|
183
|
+
if not spatially_compatible:
|
|
184
|
+
break
|
|
185
|
+
note_left = clipped_row.bbox[0]
|
|
186
|
+
note_height = max(0.1, row_height)
|
|
187
|
+
note_fonts = row_fonts
|
|
188
|
+
elif (
|
|
189
|
+
note_left is None
|
|
190
|
+
or note_height is None
|
|
191
|
+
or abs(clipped_row.bbox[0] - note_left) > 1.5 * median_height
|
|
192
|
+
or not 0.75 <= row_height / note_height <= 1.25
|
|
193
|
+
or (note_fonts and row_fonts and note_fonts.isdisjoint(row_fonts))
|
|
194
|
+
or _bbox_axis_overlap_ratio(clipped_row.bbox, rule_bbox, axis="x") < 0.35
|
|
195
|
+
):
|
|
196
|
+
# 字号、字体或缩进突变表明已进入标题/正文,表注链必须立即终止。
|
|
197
|
+
break
|
|
198
|
+
output.append(clipped_row)
|
|
199
|
+
selected_line_indices.update(line_indices)
|
|
200
|
+
bottom = max(bottom, clipped_row.bbox[3])
|
|
201
|
+
note_chain_started = True
|
|
202
|
+
return output
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _extract_auxiliary_table_note_marker(text: str) -> str | None:
|
|
206
|
+
"""提取行首一至三个通用 Unicode 标记,不解释任何具体标记含义。"""
|
|
207
|
+
|
|
208
|
+
match = _AUXILIARY_TABLE_NOTE_RE.match(str(text or ""))
|
|
209
|
+
if match is None:
|
|
210
|
+
return None
|
|
211
|
+
marker = unicodedata.normalize("NFKC", match.group("marker")).casefold()
|
|
212
|
+
if not marker or not all(unicodedata.category(char)[0] in {"L", "N", "S"} for char in marker):
|
|
213
|
+
return None
|
|
214
|
+
return marker
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _table_core_references_marker(
|
|
218
|
+
marker: str,
|
|
219
|
+
core_lines: list[_LineItem],
|
|
220
|
+
page_size: tuple[float, float],
|
|
221
|
+
angle: int,
|
|
222
|
+
) -> bool:
|
|
223
|
+
"""要求通用短标记在表格核心中具有上标或紧凑单元格引用。"""
|
|
224
|
+
|
|
225
|
+
return any(
|
|
226
|
+
_line_has_superscript_marker(line, marker, page_size, angle) or _line_has_compact_marker_token(line.text, marker)
|
|
227
|
+
for line in core_lines
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _line_has_compact_marker_token(text: str, marker: str) -> bool:
|
|
232
|
+
"""仅在短小单元格文本中确认独立标记 token,避免普通句子偶然命中。"""
|
|
233
|
+
|
|
234
|
+
normalized_text = unicodedata.normalize("NFKC", str(text or "")).casefold()
|
|
235
|
+
if sum(not char.isspace() for char in normalized_text) > 12:
|
|
236
|
+
return False
|
|
237
|
+
tokens: list[str] = []
|
|
238
|
+
current: list[str] = []
|
|
239
|
+
for char in normalized_text:
|
|
240
|
+
if unicodedata.category(char)[0] in {"L", "N", "S"}:
|
|
241
|
+
current.append(char)
|
|
242
|
+
elif current:
|
|
243
|
+
tokens.append("".join(current))
|
|
244
|
+
current = []
|
|
245
|
+
if current:
|
|
246
|
+
tokens.append("".join(current))
|
|
247
|
+
return len(tokens) <= 4 and marker in tokens
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _line_has_superscript_marker(
|
|
251
|
+
line: _LineItem,
|
|
252
|
+
marker: str,
|
|
253
|
+
page_size: tuple[float, float],
|
|
254
|
+
angle: int,
|
|
255
|
+
) -> bool:
|
|
256
|
+
"""在正向局部坐标中检查标记字形是否同时更小并明显上移。"""
|
|
257
|
+
|
|
258
|
+
glyphs: list[tuple[str, BBox]] = []
|
|
259
|
+
for char in line.chars:
|
|
260
|
+
raw_char = str(char.get("char") or "")
|
|
261
|
+
if not raw_char.isprintable() or raw_char.isspace():
|
|
262
|
+
continue
|
|
263
|
+
bbox = _coerce_bbox(char.get("bbox"))
|
|
264
|
+
if bbox is None:
|
|
265
|
+
continue
|
|
266
|
+
local_bbox = _rotate_bbox_to_upright(bbox, page_size, angle)
|
|
267
|
+
glyphs.append((unicodedata.normalize("NFKC", raw_char).casefold(), local_bbox))
|
|
268
|
+
if len(glyphs) < 2:
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
for start_index in range(len(glyphs)):
|
|
272
|
+
combined = ""
|
|
273
|
+
for end_index in range(start_index, len(glyphs)):
|
|
274
|
+
combined += glyphs[end_index][0]
|
|
275
|
+
if not marker.startswith(combined):
|
|
276
|
+
break
|
|
277
|
+
if combined != marker:
|
|
278
|
+
continue
|
|
279
|
+
marker_indices = set(range(start_index, end_index + 1))
|
|
280
|
+
ordinary_bboxes = [bbox for index, (_char, bbox) in enumerate(glyphs) if index not in marker_indices]
|
|
281
|
+
if not ordinary_bboxes:
|
|
282
|
+
continue
|
|
283
|
+
normal_height = statistics.median(bbox[3] - bbox[1] for bbox in ordinary_bboxes)
|
|
284
|
+
if normal_height <= 0:
|
|
285
|
+
continue
|
|
286
|
+
baseline_bboxes = [bbox for bbox in ordinary_bboxes if bbox[3] - bbox[1] >= 0.90 * normal_height]
|
|
287
|
+
marker_bboxes = [glyphs[index][1] for index in marker_indices]
|
|
288
|
+
marker_height = statistics.median(bbox[3] - bbox[1] for bbox in marker_bboxes)
|
|
289
|
+
marker_center = statistics.median(_bbox_center_y(bbox) for bbox in marker_bboxes)
|
|
290
|
+
normal_center = statistics.median(_bbox_center_y(bbox) for bbox in baseline_bboxes)
|
|
291
|
+
if marker_height <= 0.85 * normal_height and normal_center - marker_center >= 0.12 * normal_height:
|
|
292
|
+
return True
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _table_note_body_reference_height(
|
|
297
|
+
lines: list[_LineItem],
|
|
298
|
+
rule_bbox: BBox,
|
|
299
|
+
median_height: float,
|
|
300
|
+
core_line_indices: set[int],
|
|
301
|
+
page_size: tuple[float, float],
|
|
302
|
+
angle: int,
|
|
303
|
+
) -> float:
|
|
304
|
+
"""以同方向非表格行的最高四分位估计正文高度,样本不足时稳健回退。"""
|
|
305
|
+
|
|
306
|
+
exclusion_top = rule_bbox[1] - 3.0 * median_height
|
|
307
|
+
exclusion_bottom = rule_bbox[3] + 10.0 * median_height
|
|
308
|
+
heights: list[float] = []
|
|
309
|
+
for line in lines:
|
|
310
|
+
if line.angle != angle or line.source_index in core_line_indices:
|
|
311
|
+
continue
|
|
312
|
+
local_bbox = _rotate_bbox_to_upright(line.bbox, page_size, angle)
|
|
313
|
+
if exclusion_top <= _bbox_center_y(local_bbox) <= exclusion_bottom:
|
|
314
|
+
continue
|
|
315
|
+
heights.append(_line_effective_height(line, local_bbox))
|
|
316
|
+
if len(heights) < 4:
|
|
317
|
+
return 1.25 * median_height
|
|
318
|
+
heights.sort()
|
|
319
|
+
upper_quartile_count = max(1, (len(heights) + 3) // 4)
|
|
320
|
+
return statistics.median(heights[-upper_quartile_count:])
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _visual_row_text(row: _VisualRow) -> str:
|
|
324
|
+
"""按局部 x 顺序拼接视觉行文本,供拆分脚注标记判断。"""
|
|
325
|
+
|
|
326
|
+
return " ".join(fragment.text.strip() for fragment in row.fragments if fragment.text.strip())
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _is_table_note_text(text: str) -> bool:
|
|
330
|
+
"""判断表后首行是否具有明确的注释、来源或脚注标记。"""
|
|
331
|
+
|
|
332
|
+
return bool(_TABLE_NOTE_RE.match(str(text or "").strip()))
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _find_table_caption(
|
|
336
|
+
lines: list[_LineItem],
|
|
337
|
+
core_bbox: BBox,
|
|
338
|
+
page_size: tuple[float, float],
|
|
339
|
+
angle: int,
|
|
340
|
+
median_height: float,
|
|
341
|
+
) -> _LineItem | None:
|
|
342
|
+
"""在核心表格上方最多十二倍行高内查找显式 Table/表标题。"""
|
|
343
|
+
|
|
344
|
+
candidates: list[tuple[float, _LineItem]] = []
|
|
345
|
+
for line in lines:
|
|
346
|
+
text = line.text.strip()
|
|
347
|
+
caption_match = _TABLE_CAPTION_RE.match(text)
|
|
348
|
+
is_split_label = text.lower().rstrip(".") in {"table", "tab", "表", "表格"}
|
|
349
|
+
if caption_match is None and not is_split_label:
|
|
350
|
+
continue
|
|
351
|
+
if caption_match is not None:
|
|
352
|
+
suffix = caption_match.group("suffix").strip(" .:–—-")
|
|
353
|
+
# 小写连续句通常是“Table 5 also ...”这类正文,不应作为标题。
|
|
354
|
+
if suffix and suffix[0].islower():
|
|
355
|
+
continue
|
|
356
|
+
local_bbox = _rotate_bbox_to_upright(line.bbox, page_size, angle)
|
|
357
|
+
if _bbox_axis_overlap_ratio(local_bbox, core_bbox, axis="x") < 0.05:
|
|
358
|
+
continue
|
|
359
|
+
if is_split_label:
|
|
360
|
+
has_number_peer = bool(
|
|
361
|
+
_find_caption_number_peers(
|
|
362
|
+
line,
|
|
363
|
+
lines,
|
|
364
|
+
page_size,
|
|
365
|
+
angle,
|
|
366
|
+
median_height,
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
if not has_number_peer:
|
|
370
|
+
continue
|
|
371
|
+
gap = core_bbox[1] - local_bbox[3]
|
|
372
|
+
if -median_height <= gap <= 12.0 * median_height:
|
|
373
|
+
candidates.append((abs(gap), line))
|
|
374
|
+
if not candidates:
|
|
375
|
+
return None
|
|
376
|
+
return min(candidates, key=lambda item: item[0])[1]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _find_caption_number_peers(
|
|
380
|
+
caption_line: _LineItem,
|
|
381
|
+
lines: list[_LineItem],
|
|
382
|
+
page_size: tuple[float, float],
|
|
383
|
+
angle: int,
|
|
384
|
+
median_height: float,
|
|
385
|
+
) -> list[_LineItem]:
|
|
386
|
+
"""查找与拆分 Table/表 标签同一视觉行的编号文本。"""
|
|
387
|
+
|
|
388
|
+
caption_local_bbox = _rotate_bbox_to_upright(caption_line.bbox, page_size, angle)
|
|
389
|
+
peers: list[_LineItem] = []
|
|
390
|
+
for peer in lines:
|
|
391
|
+
if peer.source_index == caption_line.source_index:
|
|
392
|
+
continue
|
|
393
|
+
if not _TABLE_SPLIT_NUMBER_RE.match(peer.text.strip()):
|
|
394
|
+
continue
|
|
395
|
+
peer_local_bbox = _rotate_bbox_to_upright(peer.bbox, page_size, angle)
|
|
396
|
+
gap = peer_local_bbox[0] - caption_local_bbox[2]
|
|
397
|
+
if _bbox_axis_overlap_ratio(caption_local_bbox, peer_local_bbox, axis="y") >= 0.5 and 0.0 <= gap <= 4.0 * median_height:
|
|
398
|
+
peers.append(peer)
|
|
399
|
+
return sorted(
|
|
400
|
+
peers,
|
|
401
|
+
key=lambda peer: _rotate_bbox_to_upright(peer.bbox, page_size, angle)[0],
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _merge_table_candidate_annotations(
|
|
406
|
+
target: _TableCandidate,
|
|
407
|
+
candidate: _TableCandidate,
|
|
408
|
+
) -> None:
|
|
409
|
+
"""按类型合并重复候选注释,并以表体优先消解来源行角色冲突。"""
|
|
410
|
+
|
|
411
|
+
for annotation in candidate.annotations:
|
|
412
|
+
existing = next(
|
|
413
|
+
(item for item in target.annotations if item.kind == annotation.kind),
|
|
414
|
+
None,
|
|
415
|
+
)
|
|
416
|
+
if existing is None:
|
|
417
|
+
target.annotations.append(
|
|
418
|
+
_TableAnnotation(
|
|
419
|
+
kind=annotation.kind,
|
|
420
|
+
bbox=annotation.bbox,
|
|
421
|
+
line_indices=set(annotation.line_indices),
|
|
422
|
+
line_bboxes=dict(annotation.line_bboxes),
|
|
423
|
+
)
|
|
424
|
+
)
|
|
425
|
+
continue
|
|
426
|
+
existing.bbox = _bbox_union(existing.bbox, annotation.bbox)
|
|
427
|
+
existing.line_indices.update(annotation.line_indices)
|
|
428
|
+
for line_index, bbox in annotation.line_bboxes.items():
|
|
429
|
+
existing_bbox = existing.line_bboxes.get(line_index)
|
|
430
|
+
existing.line_bboxes[line_index] = bbox if existing_bbox is None else _bbox_union(existing_bbox, bbox)
|
|
431
|
+
|
|
432
|
+
# 重复候选发生角色冲突时以任一候选确认的表体成员为准,避免表头被并入 caption。
|
|
433
|
+
retained_annotations: list[_TableAnnotation] = []
|
|
434
|
+
for annotation in target.annotations:
|
|
435
|
+
annotation.line_indices.difference_update(target.line_indices)
|
|
436
|
+
annotation.line_bboxes = {
|
|
437
|
+
line_index: bbox for line_index, bbox in annotation.line_bboxes.items() if line_index in annotation.line_indices
|
|
438
|
+
}
|
|
439
|
+
if not annotation.line_indices:
|
|
440
|
+
continue
|
|
441
|
+
if annotation.line_bboxes:
|
|
442
|
+
annotation.bbox = _bbox_union_many(
|
|
443
|
+
list(annotation.line_bboxes.values()),
|
|
444
|
+
)
|
|
445
|
+
retained_annotations.append(annotation)
|
|
446
|
+
target.annotations = retained_annotations
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""PDF 固定阈值与模式;保留原有认领顺序与判定规则。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_TABLE_CAPTION_RE = re.compile(
|
|
8
|
+
r"^(?:table|tab\.?|表格?)[\s:.–—-]*(?:\d+|[ivxlcdm]+|[一二三四五六七八九十]+)\b(?P<suffix>.*)$",
|
|
9
|
+
re.IGNORECASE,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_TABLE_CONTINUATION_RE = re.compile(
|
|
14
|
+
r"^(?:续\s*表(?:\s*[0-90-9ivxlcdm一二三四五六七八九十]+)?"
|
|
15
|
+
r"|(?:table|tab\.?)\s*(?:[0-9ivxlcdm]+\s*)?(?:continued|cont\.?))$",
|
|
16
|
+
re.IGNORECASE,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_TABLE_NOTE_RE = re.compile(
|
|
21
|
+
r"^(?:notes?|sources?)\b|^(?:注释?|说明)\s*[::]?|^for\s+[*†‡]"
|
|
22
|
+
r"|^[*†‡]\s*\S",
|
|
23
|
+
re.IGNORECASE,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_AUXILIARY_TABLE_NOTE_RE = re.compile(
|
|
28
|
+
r"^\s*[([{([【]?(?P<marker>[^\s)\]})]】.::、]{1,3})"
|
|
29
|
+
r"[)\]})]】]?[.::、)]*\s+(?P<body>\S.*)$"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_TABLE_SPLIT_NUMBER_RE = re.compile(
|
|
34
|
+
r"^(?:\d+|[ivxlcdm]+|[一二三四五六七八九十]+)[.::]?$",
|
|
35
|
+
re.IGNORECASE,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_FILLED_GRID_MIN_PAGE_AREA_RATIO = 0.005
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_FILLED_GRID_MAX_PAGE_AREA_RATIO = 0.25
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_FILLED_GRID_MIN_PAGE_WIDTH_RATIO = 0.12
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_FILLED_GRID_MIN_PAGE_HEIGHT_RATIO = 0.03
|