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,522 @@
|
|
|
1
|
+
"""读取 XLSX worksheet 中的 MathType/Equation OLE 对象、anchor 与预览。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import posixpath
|
|
7
|
+
import re
|
|
8
|
+
from zipfile import ZipFile
|
|
9
|
+
import xml.etree.ElementTree as ET
|
|
10
|
+
|
|
11
|
+
from loguru import logger
|
|
12
|
+
|
|
13
|
+
from ..errors import LegacyOfficeResourceLimitError
|
|
14
|
+
from ..limits import MAX_ENTRY_BYTES
|
|
15
|
+
from ..image import serialize_office_image
|
|
16
|
+
from ..equation.image import OfficeImageEquationDecoder
|
|
17
|
+
from ..equation.ooxml import OoxmlEquationDecoder, is_mathtype_equation_prog_id
|
|
18
|
+
|
|
19
|
+
WORKBOOK_PART = "xl/workbook.xml"
|
|
20
|
+
RELATIONSHIP_NS_SUFFIX = "/relationships"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class XlsxOleEquationArtifact:
|
|
25
|
+
"""一个已绑定 worksheet anchor 的公式 OLE 结果或预览。"""
|
|
26
|
+
|
|
27
|
+
row: int | None
|
|
28
|
+
col: int | None
|
|
29
|
+
latex: str | None
|
|
30
|
+
preview_base64: str | None
|
|
31
|
+
order: int
|
|
32
|
+
shape_id: str | None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class XlsxImageArtifact:
|
|
37
|
+
"""一个从原始 worksheet drawing 读取的图片和 cell anchor。"""
|
|
38
|
+
|
|
39
|
+
row: int | None
|
|
40
|
+
col: int | None
|
|
41
|
+
payload: bytes
|
|
42
|
+
part_name: str
|
|
43
|
+
order: int
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class _Relationship:
|
|
48
|
+
"""一个经过安全路径规范化的 OPC relationship。"""
|
|
49
|
+
|
|
50
|
+
target: str | None
|
|
51
|
+
reltype: str
|
|
52
|
+
external: bool
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _local_name(value: object) -> str:
|
|
56
|
+
"""返回 XML QName 或普通属性名的本地名称。"""
|
|
57
|
+
|
|
58
|
+
text = str(value)
|
|
59
|
+
return text.rsplit("}", 1)[-1].rsplit(":", 1)[-1]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _relationship_id(element: ET.Element) -> str | None:
|
|
63
|
+
"""读取 r:id,避免把普通 shape id 误当 relationship。"""
|
|
64
|
+
|
|
65
|
+
for attribute, value in element.attrib.items():
|
|
66
|
+
if not attribute.endswith("}id"):
|
|
67
|
+
continue
|
|
68
|
+
namespace = attribute[1:].split("}", 1)[0] if attribute.startswith("{") else ""
|
|
69
|
+
if namespace.rstrip("/").endswith(RELATIONSHIP_NS_SUFFIX):
|
|
70
|
+
return value
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _relationship_embed_id(element: ET.Element) -> str | None:
|
|
75
|
+
"""读取 DrawingML a:blip 的 r:embed relationship id。"""
|
|
76
|
+
|
|
77
|
+
for attribute, value in element.attrib.items():
|
|
78
|
+
if not attribute.endswith("}embed"):
|
|
79
|
+
continue
|
|
80
|
+
namespace = attribute[1:].split("}", 1)[0] if attribute.startswith("{") else ""
|
|
81
|
+
if namespace.rstrip("/").endswith(RELATIONSHIP_NS_SUFFIX):
|
|
82
|
+
return value
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _relationship_part_name(source_part: str) -> str:
|
|
87
|
+
"""由源 part 生成同目录的 .rels part 名称。"""
|
|
88
|
+
|
|
89
|
+
directory, basename = posixpath.split(source_part)
|
|
90
|
+
return posixpath.join(directory, "_rels", f"{basename}.rels")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _resolve_internal_target(source_part: str, target: str) -> str | None:
|
|
94
|
+
"""解析 OPC 内部 target,并拒绝反斜杠和越界路径。"""
|
|
95
|
+
|
|
96
|
+
candidate = target.strip()
|
|
97
|
+
if not candidate or "\\" in candidate:
|
|
98
|
+
return None
|
|
99
|
+
if candidate.startswith("/"):
|
|
100
|
+
normalized = posixpath.normpath(candidate.lstrip("/"))
|
|
101
|
+
else:
|
|
102
|
+
normalized = posixpath.normpath(posixpath.join(posixpath.dirname(source_part), candidate))
|
|
103
|
+
if normalized in {"", "."} or normalized == ".." or normalized.startswith("../"):
|
|
104
|
+
return None
|
|
105
|
+
return normalized
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _read_xml(source: ZipFile, part_name: str) -> ET.Element | None:
|
|
109
|
+
"""尽力读取一个 XML part,坏可选 part 返回空。"""
|
|
110
|
+
|
|
111
|
+
if part_name not in source.namelist():
|
|
112
|
+
return None
|
|
113
|
+
try:
|
|
114
|
+
return ET.fromstring(source.read(part_name))
|
|
115
|
+
except (ET.ParseError, KeyError, OSError, RuntimeError):
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _relationships(source: ZipFile, source_part: str) -> dict[str, _Relationship]:
|
|
120
|
+
"""读取源 part 的 relationships,并规范化所有内部 target。"""
|
|
121
|
+
|
|
122
|
+
root = _read_xml(source, _relationship_part_name(source_part))
|
|
123
|
+
if root is None:
|
|
124
|
+
return {}
|
|
125
|
+
result: dict[str, _Relationship] = {}
|
|
126
|
+
for element in root.iter():
|
|
127
|
+
if _local_name(element.tag) != "Relationship":
|
|
128
|
+
continue
|
|
129
|
+
relationship_id = element.get("Id")
|
|
130
|
+
if not relationship_id:
|
|
131
|
+
continue
|
|
132
|
+
external = (element.get("TargetMode") or "").casefold() == "external"
|
|
133
|
+
target = None
|
|
134
|
+
if not external:
|
|
135
|
+
target = _resolve_internal_target(
|
|
136
|
+
source_part,
|
|
137
|
+
element.get("Target") or "",
|
|
138
|
+
)
|
|
139
|
+
result[relationship_id] = _Relationship(
|
|
140
|
+
target=target,
|
|
141
|
+
reltype=element.get("Type") or "",
|
|
142
|
+
external=external,
|
|
143
|
+
)
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _read_member_bounded(source: ZipFile, part_name: str | None) -> bytes | None:
|
|
148
|
+
"""按共享单 part 上限读取 XLSX ZIP 成员。"""
|
|
149
|
+
|
|
150
|
+
if part_name is None:
|
|
151
|
+
return None
|
|
152
|
+
try:
|
|
153
|
+
info = source.getinfo(part_name)
|
|
154
|
+
except KeyError:
|
|
155
|
+
return None
|
|
156
|
+
if info.file_size > MAX_ENTRY_BYTES:
|
|
157
|
+
raise LegacyOfficeResourceLimitError(f"XLSX embedded part exceeds max_entry_bytes={MAX_ENTRY_BYTES}: {part_name}")
|
|
158
|
+
payload = source.read(info)
|
|
159
|
+
if len(payload) > MAX_ENTRY_BYTES:
|
|
160
|
+
raise LegacyOfficeResourceLimitError(f"XLSX embedded part exceeds max_entry_bytes={MAX_ENTRY_BYTES}: {part_name}")
|
|
161
|
+
return payload
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def workbook_sheet_parts(source: ZipFile) -> dict[str, str]:
|
|
165
|
+
"""按 workbook sheet 名称建立 worksheet part 映射。"""
|
|
166
|
+
|
|
167
|
+
workbook = _read_xml(source, WORKBOOK_PART)
|
|
168
|
+
if workbook is None:
|
|
169
|
+
return {}
|
|
170
|
+
relationships = _relationships(source, WORKBOOK_PART)
|
|
171
|
+
result: dict[str, str] = {}
|
|
172
|
+
for sheet in workbook.iter():
|
|
173
|
+
if _local_name(sheet.tag) != "sheet":
|
|
174
|
+
continue
|
|
175
|
+
name = sheet.get("name")
|
|
176
|
+
relationship_id = _relationship_id(sheet)
|
|
177
|
+
relationship = relationships.get(relationship_id or "")
|
|
178
|
+
if (
|
|
179
|
+
not name
|
|
180
|
+
or relationship is None
|
|
181
|
+
or relationship.external
|
|
182
|
+
or relationship.target is None
|
|
183
|
+
or not relationship.reltype.rstrip("/").casefold().endswith("/worksheet")
|
|
184
|
+
):
|
|
185
|
+
continue
|
|
186
|
+
result[name] = relationship.target
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def package_has_sheet_ole_objects(
|
|
191
|
+
source: ZipFile,
|
|
192
|
+
worksheet_parts: dict[str, str],
|
|
193
|
+
) -> bool:
|
|
194
|
+
"""判断任一 worksheet 是否包含 openpyxl 无法安全忽略的 oleObjects。"""
|
|
195
|
+
|
|
196
|
+
for part_name in worksheet_parts.values():
|
|
197
|
+
root = _read_xml(source, part_name)
|
|
198
|
+
if root is None:
|
|
199
|
+
continue
|
|
200
|
+
if any(_local_name(node.tag) == "oleObjects" for node in root):
|
|
201
|
+
return True
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _child_int(element: ET.Element, name: str) -> int | None:
|
|
206
|
+
"""读取指定本地名称的首个整数子元素。"""
|
|
207
|
+
|
|
208
|
+
for child in element.iter():
|
|
209
|
+
if _local_name(child.tag) != name or child.text is None:
|
|
210
|
+
continue
|
|
211
|
+
try:
|
|
212
|
+
return int(child.text.strip())
|
|
213
|
+
except ValueError:
|
|
214
|
+
return None
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _anchor_from_object_properties(ole_object: ET.Element) -> tuple[int, int] | None:
|
|
219
|
+
"""优先从 objectPr/anchor/from 读取零基 row/col。"""
|
|
220
|
+
|
|
221
|
+
for object_properties in ole_object:
|
|
222
|
+
if _local_name(object_properties.tag) != "objectPr":
|
|
223
|
+
continue
|
|
224
|
+
for anchor in object_properties:
|
|
225
|
+
if _local_name(anchor.tag) != "anchor":
|
|
226
|
+
continue
|
|
227
|
+
from_marker = next(
|
|
228
|
+
(child for child in anchor if _local_name(child.tag) == "from"),
|
|
229
|
+
None,
|
|
230
|
+
)
|
|
231
|
+
if from_marker is None:
|
|
232
|
+
continue
|
|
233
|
+
row = _child_int(from_marker, "row")
|
|
234
|
+
col = _child_int(from_marker, "col")
|
|
235
|
+
if row is not None and col is not None and row >= 0 and col >= 0:
|
|
236
|
+
return row, col
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _object_preview_relationship_id(ole_object: ET.Element) -> str | None:
|
|
241
|
+
"""读取 objectPr 指向缓存预览图片的 relationship id。"""
|
|
242
|
+
|
|
243
|
+
for child in ole_object:
|
|
244
|
+
if _local_name(child.tag) == "objectPr":
|
|
245
|
+
return _relationship_id(child)
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _shape_id_matches(raw_id: str | None, shape_id: str) -> bool:
|
|
250
|
+
"""兼容 DrawingML 数字 id 和 VML `_x0000_sNNN` 写法。"""
|
|
251
|
+
|
|
252
|
+
if raw_id is None:
|
|
253
|
+
return False
|
|
254
|
+
if raw_id == shape_id:
|
|
255
|
+
return True
|
|
256
|
+
match = re.search(r"(?:^|[_s])(\d+)$", raw_id)
|
|
257
|
+
return bool(match and match.group(1) == shape_id)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _drawing_shape_info(
|
|
261
|
+
source: ZipFile,
|
|
262
|
+
drawing_part: str,
|
|
263
|
+
shape_id: str,
|
|
264
|
+
) -> tuple[tuple[int, int] | None, str | None]:
|
|
265
|
+
"""从 DrawingML anchor 按 cNvPr id 查找坐标和预览 part。"""
|
|
266
|
+
|
|
267
|
+
root = _read_xml(source, drawing_part)
|
|
268
|
+
if root is None:
|
|
269
|
+
return None, None
|
|
270
|
+
relationships = _relationships(source, drawing_part)
|
|
271
|
+
for anchor in root:
|
|
272
|
+
if _local_name(anchor.tag) not in {"oneCellAnchor", "twoCellAnchor", "absoluteAnchor"}:
|
|
273
|
+
continue
|
|
274
|
+
if not any(_local_name(node.tag) == "cNvPr" and _shape_id_matches(node.get("id"), shape_id) for node in anchor.iter()):
|
|
275
|
+
continue
|
|
276
|
+
from_marker = next(
|
|
277
|
+
(child for child in anchor if _local_name(child.tag) == "from"),
|
|
278
|
+
None,
|
|
279
|
+
)
|
|
280
|
+
coordinate = None
|
|
281
|
+
if from_marker is not None:
|
|
282
|
+
row = _child_int(from_marker, "row")
|
|
283
|
+
col = _child_int(from_marker, "col")
|
|
284
|
+
if row is not None and col is not None and row >= 0 and col >= 0:
|
|
285
|
+
coordinate = (row, col)
|
|
286
|
+
for node in anchor.iter():
|
|
287
|
+
if _local_name(node.tag) != "blip":
|
|
288
|
+
continue
|
|
289
|
+
relationship = relationships.get(_relationship_embed_id(node) or "")
|
|
290
|
+
if relationship is not None and not relationship.external:
|
|
291
|
+
return coordinate, relationship.target
|
|
292
|
+
return coordinate, None
|
|
293
|
+
return None, None
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def read_sheet_image_artifacts(
|
|
297
|
+
source: ZipFile,
|
|
298
|
+
worksheet_part: str,
|
|
299
|
+
) -> list[XlsxImageArtifact]:
|
|
300
|
+
"""从原始 worksheet DrawingML 读取未经过 openpyxl 转码的图片。"""
|
|
301
|
+
|
|
302
|
+
worksheet_relationships = _relationships(source, worksheet_part)
|
|
303
|
+
drawing_parts = [
|
|
304
|
+
relationship.target
|
|
305
|
+
for relationship in worksheet_relationships.values()
|
|
306
|
+
if not relationship.external
|
|
307
|
+
and relationship.target is not None
|
|
308
|
+
and relationship.reltype.rstrip("/").casefold().endswith("/drawing")
|
|
309
|
+
]
|
|
310
|
+
artifacts: list[XlsxImageArtifact] = []
|
|
311
|
+
order = 0
|
|
312
|
+
for drawing_part in drawing_parts:
|
|
313
|
+
root = _read_xml(source, drawing_part)
|
|
314
|
+
if root is None:
|
|
315
|
+
continue
|
|
316
|
+
relationships = _relationships(source, drawing_part)
|
|
317
|
+
for anchor in root:
|
|
318
|
+
if _local_name(anchor.tag) not in {
|
|
319
|
+
"oneCellAnchor",
|
|
320
|
+
"twoCellAnchor",
|
|
321
|
+
"absoluteAnchor",
|
|
322
|
+
}:
|
|
323
|
+
continue
|
|
324
|
+
from_marker = next(
|
|
325
|
+
(child for child in anchor if _local_name(child.tag) == "from"),
|
|
326
|
+
None,
|
|
327
|
+
)
|
|
328
|
+
coordinate = None
|
|
329
|
+
if from_marker is not None:
|
|
330
|
+
row = _child_int(from_marker, "row")
|
|
331
|
+
col = _child_int(from_marker, "col")
|
|
332
|
+
if row is not None and col is not None and row >= 0 and col >= 0:
|
|
333
|
+
coordinate = (row, col)
|
|
334
|
+
for node in anchor.iter():
|
|
335
|
+
if _local_name(node.tag) != "blip":
|
|
336
|
+
continue
|
|
337
|
+
relationship = relationships.get(_relationship_embed_id(node) or "")
|
|
338
|
+
if relationship is None or relationship.external or relationship.target is None:
|
|
339
|
+
continue
|
|
340
|
+
payload = _read_member_bounded(source, relationship.target)
|
|
341
|
+
if payload is None:
|
|
342
|
+
continue
|
|
343
|
+
artifacts.append(
|
|
344
|
+
XlsxImageArtifact(
|
|
345
|
+
row=coordinate[0] if coordinate is not None else None,
|
|
346
|
+
col=coordinate[1] if coordinate is not None else None,
|
|
347
|
+
payload=payload,
|
|
348
|
+
part_name=relationship.target,
|
|
349
|
+
order=order,
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
order += 1
|
|
353
|
+
return artifacts
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _vml_shape_info(
|
|
357
|
+
source: ZipFile,
|
|
358
|
+
drawing_part: str,
|
|
359
|
+
shape_id: str,
|
|
360
|
+
) -> tuple[tuple[int, int] | None, str | None]:
|
|
361
|
+
"""从 VML ClientData/Anchor 按 shape id 查找坐标和预览 part。"""
|
|
362
|
+
|
|
363
|
+
root = _read_xml(source, drawing_part)
|
|
364
|
+
if root is None:
|
|
365
|
+
return None, None
|
|
366
|
+
relationships = _relationships(source, drawing_part)
|
|
367
|
+
for shape in root.iter():
|
|
368
|
+
if _local_name(shape.tag) != "shape" or not _shape_id_matches(
|
|
369
|
+
shape.get("id"),
|
|
370
|
+
shape_id,
|
|
371
|
+
):
|
|
372
|
+
continue
|
|
373
|
+
coordinate = None
|
|
374
|
+
client_data = next(
|
|
375
|
+
(node for node in shape.iter() if _local_name(node.tag) == "ClientData"),
|
|
376
|
+
None,
|
|
377
|
+
)
|
|
378
|
+
if client_data is not None:
|
|
379
|
+
anchor_text = next(
|
|
380
|
+
(node.text for node in client_data if _local_name(node.tag) == "Anchor" and node.text),
|
|
381
|
+
None,
|
|
382
|
+
)
|
|
383
|
+
if anchor_text:
|
|
384
|
+
try:
|
|
385
|
+
values = [int(value.strip()) for value in anchor_text.split(",")]
|
|
386
|
+
except ValueError:
|
|
387
|
+
values = []
|
|
388
|
+
if len(values) >= 4 and values[0] >= 0 and values[2] >= 0:
|
|
389
|
+
coordinate = (values[2], values[0])
|
|
390
|
+
if coordinate is None:
|
|
391
|
+
row = _child_int(client_data, "Row")
|
|
392
|
+
col = _child_int(client_data, "Column")
|
|
393
|
+
if row is not None and col is not None and row >= 0 and col >= 0:
|
|
394
|
+
coordinate = (row, col)
|
|
395
|
+
for node in shape.iter():
|
|
396
|
+
if _local_name(node.tag) != "imagedata":
|
|
397
|
+
continue
|
|
398
|
+
relationship = relationships.get(_relationship_id(node) or "")
|
|
399
|
+
if relationship is not None and not relationship.external:
|
|
400
|
+
return coordinate, relationship.target
|
|
401
|
+
return coordinate, None
|
|
402
|
+
return None, None
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _shape_anchor_and_preview(
|
|
406
|
+
source: ZipFile,
|
|
407
|
+
worksheet_relationships: dict[str, _Relationship],
|
|
408
|
+
shape_id: str,
|
|
409
|
+
) -> tuple[tuple[int, int] | None, str | None]:
|
|
410
|
+
"""按 DrawingML 后 VML 的顺序恢复 shape anchor 与预览。"""
|
|
411
|
+
|
|
412
|
+
candidates = [
|
|
413
|
+
relationship
|
|
414
|
+
for relationship in worksheet_relationships.values()
|
|
415
|
+
if not relationship.external and relationship.target is not None
|
|
416
|
+
]
|
|
417
|
+
for relationship in candidates:
|
|
418
|
+
reltype = relationship.reltype.rstrip("/").casefold()
|
|
419
|
+
if reltype.endswith("/drawing"):
|
|
420
|
+
coordinate, preview = _drawing_shape_info(
|
|
421
|
+
source,
|
|
422
|
+
relationship.target or "",
|
|
423
|
+
shape_id,
|
|
424
|
+
)
|
|
425
|
+
if coordinate is not None or preview is not None:
|
|
426
|
+
return coordinate, preview
|
|
427
|
+
for relationship in candidates:
|
|
428
|
+
reltype = relationship.reltype.rstrip("/").casefold()
|
|
429
|
+
if reltype.endswith("/vmldrawing"):
|
|
430
|
+
coordinate, preview = _vml_shape_info(
|
|
431
|
+
source,
|
|
432
|
+
relationship.target or "",
|
|
433
|
+
shape_id,
|
|
434
|
+
)
|
|
435
|
+
if coordinate is not None or preview is not None:
|
|
436
|
+
return coordinate, preview
|
|
437
|
+
return None, None
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def read_sheet_equation_artifacts(
|
|
441
|
+
source: ZipFile,
|
|
442
|
+
worksheet_part: str,
|
|
443
|
+
decoder: OoxmlEquationDecoder,
|
|
444
|
+
image_decoder: OfficeImageEquationDecoder,
|
|
445
|
+
) -> list[XlsxOleEquationArtifact]:
|
|
446
|
+
"""读取一个 worksheet 的公式 OLE 对象并绑定公式或图片回退。"""
|
|
447
|
+
|
|
448
|
+
worksheet = _read_xml(source, worksheet_part)
|
|
449
|
+
if worksheet is None:
|
|
450
|
+
return []
|
|
451
|
+
relationships = _relationships(source, worksheet_part)
|
|
452
|
+
artifacts: list[XlsxOleEquationArtifact] = []
|
|
453
|
+
for order, ole_object in enumerate(node for node in worksheet.iter() if _local_name(node.tag) == "oleObject"):
|
|
454
|
+
prog_id = ole_object.get("progId") or ole_object.get("ProgID")
|
|
455
|
+
if not is_mathtype_equation_prog_id(prog_id):
|
|
456
|
+
continue
|
|
457
|
+
relationship = relationships.get(_relationship_id(ole_object) or "")
|
|
458
|
+
is_linked = bool(ole_object.get("link")) or (relationship is not None and relationship.external)
|
|
459
|
+
draw_aspect = (ole_object.get("dvAspect") or "").casefold()
|
|
460
|
+
show_as_icon = "icon" in draw_aspect
|
|
461
|
+
|
|
462
|
+
latex = None
|
|
463
|
+
if (
|
|
464
|
+
not is_linked
|
|
465
|
+
and relationship is not None
|
|
466
|
+
and relationship.target is not None
|
|
467
|
+
and relationship.reltype.rstrip("/").casefold().endswith("/oleobject")
|
|
468
|
+
):
|
|
469
|
+
blob = _read_member_bounded(source, relationship.target)
|
|
470
|
+
latex = decoder.decode(
|
|
471
|
+
blob,
|
|
472
|
+
prog_id=prog_id,
|
|
473
|
+
show_as_icon=show_as_icon,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
coordinate = _anchor_from_object_properties(ole_object)
|
|
477
|
+
preview_relationship = relationships.get(_object_preview_relationship_id(ole_object) or "")
|
|
478
|
+
preview_part = (
|
|
479
|
+
preview_relationship.target if preview_relationship is not None and not preview_relationship.external else None
|
|
480
|
+
)
|
|
481
|
+
shape_coordinate, shape_preview = _shape_anchor_and_preview(
|
|
482
|
+
source,
|
|
483
|
+
relationships,
|
|
484
|
+
ole_object.get("shapeId") or "",
|
|
485
|
+
)
|
|
486
|
+
coordinate = coordinate or shape_coordinate
|
|
487
|
+
preview_part = preview_part or shape_preview
|
|
488
|
+
preview_payload = _read_member_bounded(source, preview_part)
|
|
489
|
+
if latex is None and not show_as_icon and preview_payload is not None:
|
|
490
|
+
latex = image_decoder.decode(
|
|
491
|
+
preview_payload,
|
|
492
|
+
part_name=preview_part,
|
|
493
|
+
)
|
|
494
|
+
if latex is None and not show_as_icon and not is_linked:
|
|
495
|
+
logger.warning(
|
|
496
|
+
"XLSX_MTEF_FALLBACK: worksheet={!r}, relationship={!r} has no recoverable native or image-comment equation",
|
|
497
|
+
worksheet_part,
|
|
498
|
+
_relationship_id(ole_object),
|
|
499
|
+
)
|
|
500
|
+
preview = (
|
|
501
|
+
serialize_office_image(
|
|
502
|
+
preview_payload,
|
|
503
|
+
part_name=preview_part,
|
|
504
|
+
content_type=None,
|
|
505
|
+
)
|
|
506
|
+
if preview_payload is not None
|
|
507
|
+
else None
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
if latex is None and preview is None:
|
|
511
|
+
continue
|
|
512
|
+
artifacts.append(
|
|
513
|
+
XlsxOleEquationArtifact(
|
|
514
|
+
row=coordinate[0] if coordinate is not None else None,
|
|
515
|
+
col=coordinate[1] if coordinate is not None else None,
|
|
516
|
+
latex=latex,
|
|
517
|
+
preview_base64=preview,
|
|
518
|
+
order=order,
|
|
519
|
+
shape_id=ole_object.get("shapeId"),
|
|
520
|
+
)
|
|
521
|
+
)
|
|
522
|
+
return artifacts
|