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,18 @@
|
|
|
1
|
+
"""Flash Office 转换器复用的调用方二进制流读取能力。"""
|
|
2
|
+
|
|
3
|
+
from typing import BinaryIO
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def rewind_stream(file_stream: BinaryIO) -> bool:
|
|
7
|
+
"""将可复位的二进制流移动到起点;不可复位时返回 False。"""
|
|
8
|
+
try:
|
|
9
|
+
file_stream.seek(0)
|
|
10
|
+
except (AttributeError, OSError, ValueError):
|
|
11
|
+
return False
|
|
12
|
+
return True
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def read_stream_bytes_from_start(file_stream: BinaryIO) -> bytes:
|
|
16
|
+
"""从流起点读取完整字节;不可复位的流则从当前位置读取剩余字节。"""
|
|
17
|
+
rewind_stream(file_stream)
|
|
18
|
+
return file_stream.read()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""解析 BIFF chart BRAI 中的简单单元格引用。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import struct
|
|
7
|
+
|
|
8
|
+
from .records import BiffRecord
|
|
9
|
+
|
|
10
|
+
BRAI = 0x1051
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True, slots=True)
|
|
14
|
+
class ChartSourceSelection:
|
|
15
|
+
"""一个 chart 引用到的唯一源工作表及行列集合。"""
|
|
16
|
+
|
|
17
|
+
sheet_index: int
|
|
18
|
+
rows: tuple[int, ...]
|
|
19
|
+
cols: tuple[int, ...]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _extern_sheet_index(
|
|
23
|
+
extern_sheets: list[int | None],
|
|
24
|
+
extern_index: int,
|
|
25
|
+
) -> int | None:
|
|
26
|
+
"""把 ixti 解析为内部工作表索引。"""
|
|
27
|
+
|
|
28
|
+
if 0 <= extern_index < len(extern_sheets):
|
|
29
|
+
return extern_sheets[extern_index]
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _reference_from_tokens(
|
|
34
|
+
tokens: bytes,
|
|
35
|
+
*,
|
|
36
|
+
current_sheet_index: int,
|
|
37
|
+
extern_sheets: list[int | None],
|
|
38
|
+
) -> tuple[int, list[int], list[int]] | None:
|
|
39
|
+
"""解析单一 PtgRef/PtgArea 及其 3D 变体和源工作表。"""
|
|
40
|
+
|
|
41
|
+
if not tokens:
|
|
42
|
+
return None
|
|
43
|
+
token = tokens[0] & 0x1F
|
|
44
|
+
if token == 0x04 and len(tokens) >= 5:
|
|
45
|
+
row, column_flags = struct.unpack_from("<HH", tokens, 1)
|
|
46
|
+
return current_sheet_index, [int(row)], [int(column_flags & 0x00FF)]
|
|
47
|
+
if token == 0x05 and len(tokens) >= 9:
|
|
48
|
+
row_first, row_last, col_first, col_last = struct.unpack_from("<4H", tokens, 1)
|
|
49
|
+
return (
|
|
50
|
+
current_sheet_index,
|
|
51
|
+
list(range(min(row_first, row_last), max(row_first, row_last) + 1)),
|
|
52
|
+
list(range((col_first & 0x00FF), (col_last & 0x00FF) + 1)),
|
|
53
|
+
)
|
|
54
|
+
if token == 0x1A and len(tokens) >= 7:
|
|
55
|
+
extern_index, row, column_flags = struct.unpack_from("<3H", tokens, 1)
|
|
56
|
+
sheet_index = _extern_sheet_index(extern_sheets, int(extern_index))
|
|
57
|
+
if sheet_index is None:
|
|
58
|
+
return None
|
|
59
|
+
return sheet_index, [int(row)], [int(column_flags & 0x00FF)]
|
|
60
|
+
if token == 0x1B and len(tokens) >= 11:
|
|
61
|
+
extern_index, row_first, row_last, col_first, col_last = struct.unpack_from("<5H", tokens, 1)
|
|
62
|
+
sheet_index = _extern_sheet_index(extern_sheets, int(extern_index))
|
|
63
|
+
if sheet_index is None:
|
|
64
|
+
return None
|
|
65
|
+
first_col = col_first & 0x00FF
|
|
66
|
+
last_col = col_last & 0x00FF
|
|
67
|
+
return (
|
|
68
|
+
sheet_index,
|
|
69
|
+
list(range(min(row_first, row_last), max(row_first, row_last) + 1)),
|
|
70
|
+
list(range(min(first_col, last_col), max(first_col, last_col) + 1)),
|
|
71
|
+
)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def chart_source_selection(
|
|
76
|
+
records: list[BiffRecord],
|
|
77
|
+
*,
|
|
78
|
+
current_sheet_index: int,
|
|
79
|
+
extern_sheets: list[int | None],
|
|
80
|
+
) -> ChartSourceSelection | None:
|
|
81
|
+
"""合并 chart 名称、分类、数值与气泡引用并要求唯一源工作表。"""
|
|
82
|
+
|
|
83
|
+
rows: set[int] = set()
|
|
84
|
+
cols: set[int] = set()
|
|
85
|
+
sheet_indices: set[int] = set()
|
|
86
|
+
formulas_found = False
|
|
87
|
+
for record in records:
|
|
88
|
+
if record.record_type != BRAI or len(record.payload) < 8:
|
|
89
|
+
continue
|
|
90
|
+
formula_length = int(struct.unpack_from("<H", record.payload, 6)[0])
|
|
91
|
+
if formula_length <= 0:
|
|
92
|
+
continue
|
|
93
|
+
formulas_found = True
|
|
94
|
+
tokens = record.payload[8 : 8 + formula_length]
|
|
95
|
+
if len(tokens) != formula_length:
|
|
96
|
+
return None
|
|
97
|
+
reference = _reference_from_tokens(
|
|
98
|
+
tokens,
|
|
99
|
+
current_sheet_index=current_sheet_index,
|
|
100
|
+
extern_sheets=extern_sheets,
|
|
101
|
+
)
|
|
102
|
+
if reference is None:
|
|
103
|
+
return None
|
|
104
|
+
sheet_index, reference_rows, reference_cols = reference
|
|
105
|
+
sheet_indices.add(sheet_index)
|
|
106
|
+
rows.update(reference_rows)
|
|
107
|
+
cols.update(reference_cols)
|
|
108
|
+
if not formulas_found or not rows or not cols or len(sheet_indices) != 1:
|
|
109
|
+
return None
|
|
110
|
+
return ChartSourceSelection(
|
|
111
|
+
sheet_index=next(iter(sheet_indices)),
|
|
112
|
+
rows=tuple(sorted(rows)),
|
|
113
|
+
cols=tuple(sorted(cols)),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def chart_source_axes(
|
|
118
|
+
records: list[BiffRecord],
|
|
119
|
+
*,
|
|
120
|
+
current_sheet_index: int,
|
|
121
|
+
extern_sheets: list[int | None],
|
|
122
|
+
) -> tuple[list[int], list[int]] | None:
|
|
123
|
+
"""兼容 worksheet 内嵌 chart,仅接受引用当前工作表的数据。"""
|
|
124
|
+
|
|
125
|
+
selection = chart_source_selection(
|
|
126
|
+
records,
|
|
127
|
+
current_sheet_index=current_sheet_index,
|
|
128
|
+
extern_sheets=extern_sheets,
|
|
129
|
+
)
|
|
130
|
+
if selection is None or selection.sheet_index != current_sheet_index:
|
|
131
|
+
return None
|
|
132
|
+
return list(selection.rows), list(selection.cols)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""从 Excel.Chart 或 MSGraph.Chart OLE 对象恢复 HTML 数据表。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import struct
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from ..legacy.ole import BoundedOleReader
|
|
11
|
+
from ..legacy.binary import get_f64, get_u16
|
|
12
|
+
from ..errors import LegacyOfficeMalformedError, LegacyOfficeResourceLimitError
|
|
13
|
+
from ..limits import MAX_GRID_SLOTS
|
|
14
|
+
from .models import XlsCell, XlsRichText, XlsSheet, XlsWorkbook
|
|
15
|
+
from .number_format import builtin_number_format, format_number
|
|
16
|
+
from .parser import parse_xls_workbook
|
|
17
|
+
from .records import BOF, EOF, iter_records, record_at
|
|
18
|
+
from .strings import clean_text
|
|
19
|
+
from .xls_converter import render_xls_chart_html
|
|
20
|
+
|
|
21
|
+
GRAPH_VERSION = 0x0680
|
|
22
|
+
GRAPH_WORKBOOK_SUBSTREAM = 0x0005
|
|
23
|
+
GRAPH_CHART_SUBSTREAM = 0x8000
|
|
24
|
+
GRAPH_BLANK = 0x0001
|
|
25
|
+
GRAPH_NUMBER = 0x0003
|
|
26
|
+
GRAPH_LABEL = 0x0204
|
|
27
|
+
GRAPH_DATE1904 = 0x0022
|
|
28
|
+
GRAPH_FORMAT = 0x041E
|
|
29
|
+
GRAPH_BOF_DATASHEET = 0x1052
|
|
30
|
+
GRAPH_EXCLUDE_ROWS = 0x1053
|
|
31
|
+
GRAPH_EXCLUDE_COLUMNS = 0x1054
|
|
32
|
+
GRAPH_MAX_ROWS = 4_000
|
|
33
|
+
GRAPH_MAX_COLS = 256
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class EmbeddedChartData:
|
|
38
|
+
"""嵌入式 chart 已解析出的工作簿和唯一表格选择。"""
|
|
39
|
+
|
|
40
|
+
workbook: XlsWorkbook
|
|
41
|
+
sheet_name: str
|
|
42
|
+
rows: tuple[int, ...]
|
|
43
|
+
cols: tuple[int, ...]
|
|
44
|
+
source_kind: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _nonempty_selection(sheet: XlsSheet) -> tuple[tuple[int, ...], tuple[int, ...]] | None:
|
|
48
|
+
"""返回工作表非空单元格覆盖的稳定行列集合。"""
|
|
49
|
+
|
|
50
|
+
if not sheet.cells:
|
|
51
|
+
return None
|
|
52
|
+
rows = tuple(sorted({row for row, _col in sheet.cells}))
|
|
53
|
+
cols = tuple(sorted({col for _row, col in sheet.cells}))
|
|
54
|
+
if len(rows) * len(cols) > MAX_GRID_SLOTS:
|
|
55
|
+
raise LegacyOfficeResourceLimitError(f"workbook extent exceeds max_grid_slots={MAX_GRID_SLOTS}")
|
|
56
|
+
return rows, cols
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _excel_chart_data(workbook_stream: bytes) -> EmbeddedChartData | None:
|
|
60
|
+
"""解析 Excel.Chart 工作簿中的活动独立 chart sheet。"""
|
|
61
|
+
|
|
62
|
+
workbook = parse_xls_workbook(workbook_stream)
|
|
63
|
+
visible_charts = [chart for chart in workbook.chart_sheets if chart.visible]
|
|
64
|
+
selected = next(
|
|
65
|
+
(
|
|
66
|
+
chart
|
|
67
|
+
for chart in visible_charts
|
|
68
|
+
if workbook.active_sheet_index is not None and chart.order == workbook.active_sheet_index
|
|
69
|
+
),
|
|
70
|
+
None,
|
|
71
|
+
)
|
|
72
|
+
if selected is None and len(visible_charts) == 1:
|
|
73
|
+
selected = visible_charts[0]
|
|
74
|
+
if selected is not None and selected.source_sheet_name:
|
|
75
|
+
if selected.source_rows and selected.source_cols:
|
|
76
|
+
return EmbeddedChartData(
|
|
77
|
+
workbook=workbook,
|
|
78
|
+
sheet_name=selected.source_sheet_name,
|
|
79
|
+
rows=selected.source_rows,
|
|
80
|
+
cols=selected.source_cols,
|
|
81
|
+
source_kind="excel",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
visible_sheets = [sheet for sheet in workbook.sheets if sheet.visible and sheet.cells]
|
|
85
|
+
if len(visible_sheets) != 1:
|
|
86
|
+
return None
|
|
87
|
+
fallback = _nonempty_selection(visible_sheets[0])
|
|
88
|
+
if fallback is None:
|
|
89
|
+
return None
|
|
90
|
+
rows, cols = fallback
|
|
91
|
+
return EmbeddedChartData(
|
|
92
|
+
workbook=workbook,
|
|
93
|
+
sheet_name=visible_sheets[0].name,
|
|
94
|
+
rows=rows,
|
|
95
|
+
cols=cols,
|
|
96
|
+
source_kind="excel",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _decode_short_unicode(payload: bytes, offset: int) -> str | None:
|
|
101
|
+
"""读取 MS-OGRAPH ShortXLUnicodeString。"""
|
|
102
|
+
|
|
103
|
+
if offset < 0 or offset + 2 > len(payload):
|
|
104
|
+
return None
|
|
105
|
+
count = payload[offset]
|
|
106
|
+
flags = payload[offset + 1]
|
|
107
|
+
if flags & 0xFE:
|
|
108
|
+
return None
|
|
109
|
+
width = 2 if flags & 0x01 else 1
|
|
110
|
+
start = offset + 2
|
|
111
|
+
end = start + count * width
|
|
112
|
+
if end > len(payload):
|
|
113
|
+
return None
|
|
114
|
+
encoding = "utf-16le" if width == 2 else "cp1252"
|
|
115
|
+
try:
|
|
116
|
+
return clean_text(payload[start:end].decode(encoding, errors="strict"))
|
|
117
|
+
except UnicodeDecodeError:
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _decode_unicode_min2(payload: bytes, offset: int) -> str | None:
|
|
122
|
+
"""读取带 u16 长度的 MS-OGRAPH XLUnicodeStringMin2。"""
|
|
123
|
+
|
|
124
|
+
count = get_u16(payload, offset)
|
|
125
|
+
if count is None or offset + 3 > len(payload):
|
|
126
|
+
return None
|
|
127
|
+
flags = payload[offset + 2]
|
|
128
|
+
if flags & 0xFE:
|
|
129
|
+
return None
|
|
130
|
+
width = 2 if flags & 0x01 else 1
|
|
131
|
+
start = offset + 3
|
|
132
|
+
end = start + int(count) * width
|
|
133
|
+
if end > len(payload):
|
|
134
|
+
return None
|
|
135
|
+
encoding = "utf-16le" if width == 2 else "cp1252"
|
|
136
|
+
try:
|
|
137
|
+
return clean_text(payload[start:end].decode(encoding, errors="strict"))
|
|
138
|
+
except UnicodeDecodeError:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _graph_included_indices(
|
|
143
|
+
candidates: tuple[int, ...],
|
|
144
|
+
payload: bytes | None,
|
|
145
|
+
) -> tuple[int, ...]:
|
|
146
|
+
"""按 MS-OGRAPH 交替边界筛选 chart 实际包含的行或列。"""
|
|
147
|
+
|
|
148
|
+
if not payload:
|
|
149
|
+
return candidates
|
|
150
|
+
if len(payload) % 2:
|
|
151
|
+
return candidates
|
|
152
|
+
boundaries = [int(struct.unpack_from("<H", payload, offset)[0]) for offset in range(0, len(payload), 2)]
|
|
153
|
+
if len(boundaries) % 2:
|
|
154
|
+
return candidates
|
|
155
|
+
included = tuple(
|
|
156
|
+
value for value in candidates if any(start <= value < end for start, end in zip(boundaries[::2], boundaries[1::2]))
|
|
157
|
+
)
|
|
158
|
+
return included or candidates
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _graph_chart_data(workbook_stream: bytes) -> EmbeddedChartData | None:
|
|
162
|
+
"""解析 MS-OGRAPH chart sheet 内嵌的 datasheet。"""
|
|
163
|
+
|
|
164
|
+
records = list(iter_records(workbook_stream))
|
|
165
|
+
bof_records = [record for record in records if record.record_type == BOF]
|
|
166
|
+
if len(bof_records) != 2:
|
|
167
|
+
return None
|
|
168
|
+
first, second = bof_records
|
|
169
|
+
if (
|
|
170
|
+
get_u16(first.payload, 0) != GRAPH_VERSION
|
|
171
|
+
or get_u16(first.payload, 2) != GRAPH_WORKBOOK_SUBSTREAM
|
|
172
|
+
or get_u16(second.payload, 0) != GRAPH_VERSION
|
|
173
|
+
or get_u16(second.payload, 2) != GRAPH_CHART_SUBSTREAM
|
|
174
|
+
):
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
date1904 = False
|
|
178
|
+
custom_formats: dict[int, str] = {}
|
|
179
|
+
in_chart = False
|
|
180
|
+
in_datasheet = False
|
|
181
|
+
cells: dict[tuple[int, int], XlsCell] = {}
|
|
182
|
+
excluded_rows: bytes | None = None
|
|
183
|
+
excluded_cols: bytes | None = None
|
|
184
|
+
for record in records:
|
|
185
|
+
if record is second:
|
|
186
|
+
in_chart = True
|
|
187
|
+
continue
|
|
188
|
+
if not in_chart:
|
|
189
|
+
if record.record_type == GRAPH_DATE1904:
|
|
190
|
+
date1904 = get_u16(record.payload, 0) == 1
|
|
191
|
+
elif record.record_type == GRAPH_FORMAT:
|
|
192
|
+
format_id = get_u16(record.payload, 0)
|
|
193
|
+
format_code = _decode_unicode_min2(record.payload, 2)
|
|
194
|
+
if format_id is not None and format_code:
|
|
195
|
+
custom_formats[int(format_id)] = format_code
|
|
196
|
+
continue
|
|
197
|
+
if record.record_type == EOF:
|
|
198
|
+
break
|
|
199
|
+
if record.record_type == GRAPH_BOF_DATASHEET:
|
|
200
|
+
in_datasheet = True
|
|
201
|
+
continue
|
|
202
|
+
if record.record_type == GRAPH_EXCLUDE_ROWS:
|
|
203
|
+
excluded_rows = record.payload
|
|
204
|
+
continue
|
|
205
|
+
if record.record_type == GRAPH_EXCLUDE_COLUMNS:
|
|
206
|
+
excluded_cols = record.payload
|
|
207
|
+
continue
|
|
208
|
+
if not in_datasheet or len(record.payload) < 7:
|
|
209
|
+
continue
|
|
210
|
+
row = get_u16(record.payload, 0)
|
|
211
|
+
col = get_u16(record.payload, 2)
|
|
212
|
+
format_id = get_u16(record.payload, 5)
|
|
213
|
+
if row is None or col is None or row >= GRAPH_MAX_ROWS or col >= GRAPH_MAX_COLS:
|
|
214
|
+
continue
|
|
215
|
+
text: str | None = None
|
|
216
|
+
if record.record_type == GRAPH_LABEL:
|
|
217
|
+
text = _decode_short_unicode(record.payload, 7)
|
|
218
|
+
elif record.record_type == GRAPH_NUMBER:
|
|
219
|
+
value = get_f64(record.payload, 7)
|
|
220
|
+
if value is not None:
|
|
221
|
+
format_code = custom_formats.get(int(format_id or 0)) or builtin_number_format(int(format_id or 0))
|
|
222
|
+
text = format_number(value, format_code, date1904=date1904)
|
|
223
|
+
elif record.record_type == GRAPH_BLANK:
|
|
224
|
+
text = ""
|
|
225
|
+
if text is None:
|
|
226
|
+
continue
|
|
227
|
+
cells[(int(row), int(col))] = XlsCell(
|
|
228
|
+
row=int(row),
|
|
229
|
+
col=int(col),
|
|
230
|
+
value=XlsRichText(text),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
sheet = XlsSheet(name="Data", visible=True, order=0, cells=cells)
|
|
234
|
+
selection = _nonempty_selection(sheet)
|
|
235
|
+
if selection is None:
|
|
236
|
+
return None
|
|
237
|
+
rows, cols = selection
|
|
238
|
+
rows = _graph_included_indices(rows, excluded_rows)
|
|
239
|
+
cols = _graph_included_indices(cols, excluded_cols)
|
|
240
|
+
if not rows or not cols or len(rows) * len(cols) > MAX_GRID_SLOTS:
|
|
241
|
+
return None
|
|
242
|
+
return EmbeddedChartData(
|
|
243
|
+
workbook=XlsWorkbook(sheets=[sheet]),
|
|
244
|
+
sheet_name=sheet.name,
|
|
245
|
+
rows=rows,
|
|
246
|
+
cols=cols,
|
|
247
|
+
source_kind="graph",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def extract_embedded_chart_data(workbook_stream: bytes) -> EmbeddedChartData | None:
|
|
252
|
+
"""按 BOF 版本选择 Excel BIFF 或 MS-OGRAPH chart 解析器。"""
|
|
253
|
+
|
|
254
|
+
first = record_at(workbook_stream, 0)
|
|
255
|
+
if first is None or first.record_type != BOF:
|
|
256
|
+
return None
|
|
257
|
+
version = get_u16(first.payload, 0)
|
|
258
|
+
try:
|
|
259
|
+
if version in {0x0500, 0x0600}:
|
|
260
|
+
return _excel_chart_data(workbook_stream)
|
|
261
|
+
if version == GRAPH_VERSION:
|
|
262
|
+
return _graph_chart_data(workbook_stream)
|
|
263
|
+
except LegacyOfficeResourceLimitError:
|
|
264
|
+
raise
|
|
265
|
+
except (LegacyOfficeMalformedError, ValueError, struct.error) as exc:
|
|
266
|
+
logger.warning("LEGACY_CHART_DATA_FALLBACK: {}", exc)
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def extract_embedded_chart_html(workbook_stream: bytes) -> str | None:
|
|
271
|
+
"""从 Workbook/Book stream 恢复一个可渲染的 HTML 数据表。"""
|
|
272
|
+
|
|
273
|
+
chart = extract_embedded_chart_data(workbook_stream)
|
|
274
|
+
if chart is None:
|
|
275
|
+
return None
|
|
276
|
+
return render_xls_chart_html(
|
|
277
|
+
chart.workbook,
|
|
278
|
+
chart.sheet_name,
|
|
279
|
+
chart.rows,
|
|
280
|
+
chart.cols,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def extract_embedded_chart_html_from_storage(storage: bytes) -> str | None:
|
|
285
|
+
"""从独立 OLE CFB 对象中读取 Workbook/Book 并恢复数据表。"""
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
with BoundedOleReader(storage) as ole:
|
|
289
|
+
if ole.has_stream("Workbook"):
|
|
290
|
+
workbook_stream = ole.read_stream("Workbook")
|
|
291
|
+
elif ole.has_stream("Book"):
|
|
292
|
+
workbook_stream = ole.read_stream("Book")
|
|
293
|
+
else:
|
|
294
|
+
return None
|
|
295
|
+
except LegacyOfficeResourceLimitError:
|
|
296
|
+
raise
|
|
297
|
+
except ValueError:
|
|
298
|
+
return None
|
|
299
|
+
return extract_embedded_chart_html(workbook_stream)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""旧版 Excel 解析阶段使用的内部语义模型。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True, slots=True)
|
|
9
|
+
class XlsFontStyle:
|
|
10
|
+
"""一个 BIFF FONT 记录中可表达的行内文字样式。"""
|
|
11
|
+
|
|
12
|
+
bold: bool = False
|
|
13
|
+
italic: bool = False
|
|
14
|
+
underline: bool = False
|
|
15
|
+
strike: bool = False
|
|
16
|
+
superscript: bool = False
|
|
17
|
+
subscript: bool = False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class XlsRichRun:
|
|
22
|
+
"""以 Python 字符索引表示的富文本区间。"""
|
|
23
|
+
|
|
24
|
+
start: int
|
|
25
|
+
end: int
|
|
26
|
+
style: XlsFontStyle
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class XlsRichText:
|
|
31
|
+
"""单元格或 drawing 文本及其富文本区间。"""
|
|
32
|
+
|
|
33
|
+
text: str
|
|
34
|
+
runs: tuple[XlsRichRun, ...] = ()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(slots=True)
|
|
38
|
+
class XlsCell:
|
|
39
|
+
"""工作表中的一个可见语义单元格。"""
|
|
40
|
+
|
|
41
|
+
row: int
|
|
42
|
+
col: int
|
|
43
|
+
value: XlsRichText
|
|
44
|
+
hyperlink: str | None = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True, slots=True)
|
|
48
|
+
class XlsImage:
|
|
49
|
+
"""绑定到工作表 cell anchor 的已序列化图片。"""
|
|
50
|
+
|
|
51
|
+
row: int
|
|
52
|
+
col: int
|
|
53
|
+
image_base64: str
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, slots=True)
|
|
57
|
+
class XlsEquation:
|
|
58
|
+
"""绑定到工作表 cell anchor 的原生或图片 comment 公式。"""
|
|
59
|
+
|
|
60
|
+
row: int
|
|
61
|
+
col: int
|
|
62
|
+
latex: str
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class XlsChart:
|
|
67
|
+
"""由嵌入 chart 引用恢复出的源数据坐标。"""
|
|
68
|
+
|
|
69
|
+
row: int
|
|
70
|
+
col: int
|
|
71
|
+
source_rows: tuple[int, ...]
|
|
72
|
+
source_cols: tuple[int, ...]
|
|
73
|
+
image_base64: str | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(slots=True)
|
|
77
|
+
class XlsSheet:
|
|
78
|
+
"""一个 worksheet 的单元格、合并区域与 drawing 资源。"""
|
|
79
|
+
|
|
80
|
+
name: str
|
|
81
|
+
visible: bool
|
|
82
|
+
order: int = -1
|
|
83
|
+
cells: dict[tuple[int, int], XlsCell] = field(default_factory=dict)
|
|
84
|
+
merges: list[tuple[int, int, int, int]] = field(default_factory=list)
|
|
85
|
+
images: list[XlsImage] = field(default_factory=list)
|
|
86
|
+
equations: list[XlsEquation] = field(default_factory=list)
|
|
87
|
+
charts: list[XlsChart] = field(default_factory=list)
|
|
88
|
+
recovered: bool = False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True, slots=True)
|
|
92
|
+
class XlsChartSheet:
|
|
93
|
+
"""一个独立 chart sheet 及其源工作表选择范围。"""
|
|
94
|
+
|
|
95
|
+
name: str
|
|
96
|
+
visible: bool
|
|
97
|
+
order: int
|
|
98
|
+
source_sheet_name: str | None
|
|
99
|
+
source_rows: tuple[int, ...] = ()
|
|
100
|
+
source_cols: tuple[int, ...] = ()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(slots=True)
|
|
104
|
+
class XlsWorkbook:
|
|
105
|
+
"""Excel 97–2003 工作簿的内部分页表示。"""
|
|
106
|
+
|
|
107
|
+
sheets: list[XlsSheet]
|
|
108
|
+
chart_sheets: list[XlsChartSheet] = field(default_factory=list)
|
|
109
|
+
active_sheet_index: int | None = None
|