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.
Files changed (364) hide show
  1. docvortex/__init__.py +21 -0
  2. docvortex/analyzers/__init__.py +3 -0
  3. docvortex/analyzers/native/__init__.py +37 -0
  4. docvortex/analyzers/native/_shared/__init__.py +3 -0
  5. docvortex/analyzers/native/_shared/hyperlink.py +16 -0
  6. docvortex/analyzers/native/_shared/image.py +8 -0
  7. docvortex/analyzers/native/_shared/markup/__init__.py +47 -0
  8. docvortex/analyzers/native/_shared/markup/anchors.py +30 -0
  9. docvortex/analyzers/native/_shared/markup/formula.py +37 -0
  10. docvortex/analyzers/native/_shared/markup/projector.py +57 -0
  11. docvortex/analyzers/native/_shared/markup/styles.py +19 -0
  12. docvortex/analyzers/native/_shared/mathml.py +17 -0
  13. docvortex/analyzers/native/_shared/names.py +7 -0
  14. docvortex/analyzers/native/_shared/xycut.py +414 -0
  15. docvortex/analyzers/native/contracts.py +44 -0
  16. docvortex/analyzers/native/csv.py +351 -0
  17. docvortex/analyzers/native/epub/__init__.py +18 -0
  18. docvortex/analyzers/native/epub/constants.py +45 -0
  19. docvortex/analyzers/native/epub/converter.py +80 -0
  20. docvortex/analyzers/native/epub/errors.py +20 -0
  21. docvortex/analyzers/native/epub/metadata.py +31 -0
  22. docvortex/analyzers/native/epub/package.py +649 -0
  23. docvortex/analyzers/native/epub/xhtml.py +306 -0
  24. docvortex/analyzers/native/html/__init__.py +6 -0
  25. docvortex/analyzers/native/html/anchors.py +271 -0
  26. docvortex/analyzers/native/html/constants.py +25 -0
  27. docvortex/analyzers/native/html/contracts.py +7 -0
  28. docvortex/analyzers/native/html/converter.py +117 -0
  29. docvortex/analyzers/native/html/document.py +389 -0
  30. docvortex/analyzers/native/html/errors.py +12 -0
  31. docvortex/analyzers/native/html/resources.py +365 -0
  32. docvortex/analyzers/native/html/selector.py +421 -0
  33. docvortex/analyzers/native/models.py +209 -0
  34. docvortex/analyzers/native/ofd/__init__.py +14 -0
  35. docvortex/analyzers/native/ofd/constants.py +59 -0
  36. docvortex/analyzers/native/ofd/converter.py +31 -0
  37. docvortex/analyzers/native/ofd/errors.py +16 -0
  38. docvortex/analyzers/native/ofd/geometry.py +215 -0
  39. docvortex/analyzers/native/ofd/images.py +124 -0
  40. docvortex/analyzers/native/ofd/metadata.py +53 -0
  41. docvortex/analyzers/native/ofd/models.py +170 -0
  42. docvortex/analyzers/native/ofd/package.py +346 -0
  43. docvortex/analyzers/native/ofd/path.py +201 -0
  44. docvortex/analyzers/native/ofd/reading_order.py +293 -0
  45. docvortex/analyzers/native/ofd/resources.py +122 -0
  46. docvortex/analyzers/native/ofd/scene.py +347 -0
  47. docvortex/analyzers/native/ofd/table.py +240 -0
  48. docvortex/analyzers/native/ofd/text.py +521 -0
  49. docvortex/analyzers/native/office/__init__.py +3 -0
  50. docvortex/analyzers/native/office/doc/__init__.py +3 -0
  51. docvortex/analyzers/native/office/doc/bookmarks.py +81 -0
  52. docvortex/analyzers/native/office/doc/doc_converter.py +573 -0
  53. docvortex/analyzers/native/office/doc/fib.py +261 -0
  54. docvortex/analyzers/native/office/doc/fields.py +101 -0
  55. docvortex/analyzers/native/office/doc/formatting.py +171 -0
  56. docvortex/analyzers/native/office/doc/images.py +162 -0
  57. docvortex/analyzers/native/office/doc/lists.py +356 -0
  58. docvortex/analyzers/native/office/doc/models.py +164 -0
  59. docvortex/analyzers/native/office/doc/parser.py +851 -0
  60. docvortex/analyzers/native/office/doc/pieces.py +256 -0
  61. docvortex/analyzers/native/office/doc/records.py +40 -0
  62. docvortex/analyzers/native/office/doc/sprm.py +269 -0
  63. docvortex/analyzers/native/office/doc/styles.py +215 -0
  64. docvortex/analyzers/native/office/docx/__init__.py +3 -0
  65. docvortex/analyzers/native/office/docx/context.py +71 -0
  66. docvortex/analyzers/native/office/docx/docx_converter.py +614 -0
  67. docvortex/analyzers/native/office/docx/equationxml.py +119 -0
  68. docvortex/analyzers/native/office/docx/fields.py +816 -0
  69. docvortex/analyzers/native/office/docx/formatting_types.py +23 -0
  70. docvortex/analyzers/native/office/docx/main.py +50 -0
  71. docvortex/analyzers/native/office/docx/numbering.py +491 -0
  72. docvortex/analyzers/native/office/docx/office_xml.py +57 -0
  73. docvortex/analyzers/native/office/docx/package_normalizer.py +248 -0
  74. docvortex/analyzers/native/office/docx/resources.py +526 -0
  75. docvortex/analyzers/native/office/docx/styles.py +693 -0
  76. docvortex/analyzers/native/office/docx/tables.py +515 -0
  77. docvortex/analyzers/native/office/equation/__init__.py +3 -0
  78. docvortex/analyzers/native/office/equation/image.py +470 -0
  79. docvortex/analyzers/native/office/equation/latex_dict.py +324 -0
  80. docvortex/analyzers/native/office/equation/mtef.py +885 -0
  81. docvortex/analyzers/native/office/equation/mtef_v5.py +941 -0
  82. docvortex/analyzers/native/office/equation/omml.py +561 -0
  83. docvortex/analyzers/native/office/equation/ooxml.py +62 -0
  84. docvortex/analyzers/native/office/errors.py +33 -0
  85. docvortex/analyzers/native/office/image.py +307 -0
  86. docvortex/analyzers/native/office/legacy/__init__.py +3 -0
  87. docvortex/analyzers/native/office/legacy/binary.py +43 -0
  88. docvortex/analyzers/native/office/legacy/officeart.py +362 -0
  89. docvortex/analyzers/native/office/legacy/ole.py +110 -0
  90. docvortex/analyzers/native/office/limits.py +12 -0
  91. docvortex/analyzers/native/office/odf/__init__.py +3 -0
  92. docvortex/analyzers/native/office/odf/chart.py +76 -0
  93. docvortex/analyzers/native/office/odf/constants.py +70 -0
  94. docvortex/analyzers/native/office/odf/converters.py +403 -0
  95. docvortex/analyzers/native/office/odf/errors.py +18 -0
  96. docvortex/analyzers/native/office/odf/metadata.py +91 -0
  97. docvortex/analyzers/native/office/odf/models.py +176 -0
  98. docvortex/analyzers/native/office/odf/package.py +270 -0
  99. docvortex/analyzers/native/office/odf/styles.py +329 -0
  100. docvortex/analyzers/native/office/odf/table.py +469 -0
  101. docvortex/analyzers/native/office/odf/text.py +1002 -0
  102. docvortex/analyzers/native/office/ooxml_chart.py +1016 -0
  103. docvortex/analyzers/native/office/opc.py +38 -0
  104. docvortex/analyzers/native/office/ppt/__init__.py +3 -0
  105. docvortex/analyzers/native/office/ppt/models.py +118 -0
  106. docvortex/analyzers/native/office/ppt/parser.py +1895 -0
  107. docvortex/analyzers/native/office/ppt/ppt_converter.py +292 -0
  108. docvortex/analyzers/native/office/ppt/records.py +131 -0
  109. docvortex/analyzers/native/office/ppt/style_text.py +247 -0
  110. docvortex/analyzers/native/office/pptx/__init__.py +3 -0
  111. docvortex/analyzers/native/office/pptx/context.py +113 -0
  112. docvortex/analyzers/native/office/pptx/lists.py +558 -0
  113. docvortex/analyzers/native/office/pptx/main.py +20 -0
  114. docvortex/analyzers/native/office/pptx/package_normalizer.py +321 -0
  115. docvortex/analyzers/native/office/pptx/pptx_converter.py +323 -0
  116. docvortex/analyzers/native/office/pptx/resources.py +329 -0
  117. docvortex/analyzers/native/office/pptx/shapes.py +393 -0
  118. docvortex/analyzers/native/office/pptx/text_styles.py +625 -0
  119. docvortex/analyzers/native/office/pptx/titles.py +178 -0
  120. docvortex/analyzers/native/office/rich_text.py +420 -0
  121. docvortex/analyzers/native/office/rtf/__init__.py +3 -0
  122. docvortex/analyzers/native/office/rtf/converter.py +708 -0
  123. docvortex/analyzers/native/office/rtf/lexer.py +213 -0
  124. docvortex/analyzers/native/office/rtf/math.py +339 -0
  125. docvortex/analyzers/native/office/rtf/models.py +185 -0
  126. docvortex/analyzers/native/office/rtf/parser.py +1553 -0
  127. docvortex/analyzers/native/office/spreadsheet/__init__.py +3 -0
  128. docvortex/analyzers/native/office/spreadsheet/html.py +81 -0
  129. docvortex/analyzers/native/office/spreadsheet/models.py +79 -0
  130. docvortex/analyzers/native/office/spreadsheet/projector.py +928 -0
  131. docvortex/analyzers/native/office/streams.py +18 -0
  132. docvortex/analyzers/native/office/xls/__init__.py +3 -0
  133. docvortex/analyzers/native/office/xls/chart.py +132 -0
  134. docvortex/analyzers/native/office/xls/embedded_chart.py +299 -0
  135. docvortex/analyzers/native/office/xls/models.py +109 -0
  136. docvortex/analyzers/native/office/xls/number_format.py +521 -0
  137. docvortex/analyzers/native/office/xls/parser.py +1145 -0
  138. docvortex/analyzers/native/office/xls/records.py +201 -0
  139. docvortex/analyzers/native/office/xls/strings.py +205 -0
  140. docvortex/analyzers/native/office/xls/xls_converter.py +354 -0
  141. docvortex/analyzers/native/office/xlsx/__init__.py +3 -0
  142. docvortex/analyzers/native/office/xlsx/main.py +20 -0
  143. docvortex/analyzers/native/office/xlsx/ooxml_ole.py +522 -0
  144. docvortex/analyzers/native/office/xlsx/package_normalizer.py +310 -0
  145. docvortex/analyzers/native/office/xlsx/xlsx_converter.py +716 -0
  146. docvortex/analyzers/native/pdf/__init__.py +3 -0
  147. docvortex/analyzers/native/pdf/auxiliary_text.py +1671 -0
  148. docvortex/analyzers/native/pdf/char_geometry.py +1662 -0
  149. docvortex/analyzers/native/pdf/code_blocks.py +535 -0
  150. docvortex/analyzers/native/pdf/formulas.py +1985 -0
  151. docvortex/analyzers/native/pdf/geometry.py +281 -0
  152. docvortex/analyzers/native/pdf/graphics.py +1501 -0
  153. docvortex/analyzers/native/pdf/index_blocks.py +268 -0
  154. docvortex/analyzers/native/pdf/inline/__init__.py +3 -0
  155. docvortex/analyzers/native/pdf/inline/common.py +112 -0
  156. docvortex/analyzers/native/pdf/inline/detection.py +590 -0
  157. docvortex/analyzers/native/pdf/inline/matching.py +1185 -0
  158. docvortex/analyzers/native/pdf/inline/materialize.py +457 -0
  159. docvortex/analyzers/native/pdf/inline/scripts.py +975 -0
  160. docvortex/analyzers/native/pdf/inline/types.py +385 -0
  161. docvortex/analyzers/native/pdf/line_layout.py +1106 -0
  162. docvortex/analyzers/native/pdf/line_merging.py +1223 -0
  163. docvortex/analyzers/native/pdf/models.py +246 -0
  164. docvortex/analyzers/native/pdf/native_text.py +1004 -0
  165. docvortex/analyzers/native/pdf/pipeline.py +1390 -0
  166. docvortex/analyzers/native/pdf/script_geometry.py +636 -0
  167. docvortex/analyzers/native/pdf/shared.py +30 -0
  168. docvortex/analyzers/native/pdf/spatial_text.py +383 -0
  169. docvortex/analyzers/native/pdf/table_annotations.py +446 -0
  170. docvortex/analyzers/native/pdf/table_constants.py +48 -0
  171. docvortex/analyzers/native/pdf/table_detection.py +227 -0
  172. docvortex/analyzers/native/pdf/table_filled_grid.py +218 -0
  173. docvortex/analyzers/native/pdf/table_geometry.py +40 -0
  174. docvortex/analyzers/native/pdf/table_materialization.py +444 -0
  175. docvortex/analyzers/native/pdf/table_recovery/__init__.py +15 -0
  176. docvortex/analyzers/native/pdf/table_recovery/candidate.py +356 -0
  177. docvortex/analyzers/native/pdf/table_recovery/contracts.py +154 -0
  178. docvortex/analyzers/native/pdf/table_recovery/engine.py +609 -0
  179. docvortex/analyzers/native/pdf/table_recovery/geometry.py +164 -0
  180. docvortex/analyzers/native/pdf/table_recovery/sparse_common.py +85 -0
  181. docvortex/analyzers/native/pdf/table_recovery/sparse_hybrid.py +804 -0
  182. docvortex/analyzers/native/pdf/table_recovery/sparse_multiline.py +1122 -0
  183. docvortex/analyzers/native/pdf/table_recovery/text.py +414 -0
  184. docvortex/analyzers/native/pdf/table_recovery/text_grid.py +618 -0
  185. docvortex/analyzers/native/pdf/table_recovery/vector.py +1931 -0
  186. docvortex/analyzers/native/pdf/table_rows.py +34 -0
  187. docvortex/analyzers/native/pdf/table_rules.py +1129 -0
  188. docvortex/analyzers/native/pdf/table_text_styles.py +283 -0
  189. docvortex/analyzers/native/pdf/tables.py +147 -0
  190. docvortex/analyzers/native/pdf/text_assembly/__init__.py +3 -0
  191. docvortex/analyzers/native/pdf/text_assembly/annotations.py +581 -0
  192. docvortex/analyzers/native/pdf/text_assembly/assembly.py +292 -0
  193. docvortex/analyzers/native/pdf/text_assembly/common.py +477 -0
  194. docvortex/analyzers/native/pdf/text_assembly/footnotes.py +394 -0
  195. docvortex/analyzers/native/pdf/text_assembly/merging.py +1274 -0
  196. docvortex/analyzers/native/pdf/text_assembly/rows.py +692 -0
  197. docvortex/analyzers/native/pdf/text_blocks.py +82 -0
  198. docvortex/analyzers/native/pdf/text_styles.py +55 -0
  199. docvortex/analyzers/native/pdf/title_analysis/__init__.py +3 -0
  200. docvortex/analyzers/native/pdf/title_analysis/body_profile.py +215 -0
  201. docvortex/analyzers/native/pdf/title_analysis/common.py +117 -0
  202. docvortex/analyzers/native/pdf/title_analysis/document_profile.py +164 -0
  203. docvortex/analyzers/native/pdf/title_analysis/lane_titles.py +758 -0
  204. docvortex/analyzers/native/pdf/title_analysis/page_titles.py +1024 -0
  205. docvortex/analyzers/native/pdf/title_analysis/prototype.py +194 -0
  206. docvortex/analyzers/native/pdf/title_analysis/structural.py +1081 -0
  207. docvortex/analyzers/native/pdf/titles.py +75 -0
  208. docvortex/analyzers/native/pdf/typography.py +19 -0
  209. docvortex/analyzers/native/pdf/visual_annotations.py +1262 -0
  210. docvortex/api.py +180 -0
  211. docvortex/assets/__init__.py +5 -0
  212. docvortex/assets/store.py +51 -0
  213. docvortex/cli.py +54 -0
  214. docvortex/codecs/__init__.py +3 -0
  215. docvortex/codecs/html/__init__.py +22 -0
  216. docvortex/codecs/html/contracts.py +236 -0
  217. docvortex/codecs/html/materializer.py +331 -0
  218. docvortex/codecs/html/parser.py +763 -0
  219. docvortex/codecs/html/resources.py +34 -0
  220. docvortex/codecs/json.py +17 -0
  221. docvortex/content/__init__.py +5 -0
  222. docvortex/content/inline.py +248 -0
  223. docvortex/content/markup/__init__.py +44 -0
  224. docvortex/content/markup/anchors.py +188 -0
  225. docvortex/content/markup/formula.py +280 -0
  226. docvortex/content/markup/projector.py +1237 -0
  227. docvortex/content/markup/styles.py +327 -0
  228. docvortex/content/mathml.py +167 -0
  229. docvortex/content/normalization.py +188 -0
  230. docvortex/content/spans.py +183 -0
  231. docvortex/content/table/__init__.py +18 -0
  232. docvortex/content/table/blocks.py +152 -0
  233. docvortex/content/table/content.py +425 -0
  234. docvortex/content/table/document.py +104 -0
  235. docvortex/content/table/html.py +399 -0
  236. docvortex/content/table/models.py +76 -0
  237. docvortex/content/table/rules.py +42 -0
  238. docvortex/content/table/structure.py +221 -0
  239. docvortex/content/tree.py +5 -0
  240. docvortex/document/__init__.py +3 -0
  241. docvortex/document/contracts.py +22 -0
  242. docvortex/document/detection.py +389 -0
  243. docvortex/document/filetypes.py +175 -0
  244. docvortex/document/page_range.py +167 -0
  245. docvortex/document/pdf/__init__.py +19 -0
  246. docvortex/document/pdf/classify.py +1138 -0
  247. docvortex/document/pdf/constants.py +132 -0
  248. docvortex/document/pdf/diagnostics.py +350 -0
  249. docvortex/document/pdf/document.py +582 -0
  250. docvortex/document/pdf/font_runtime.py +335 -0
  251. docvortex/document/pdf/geometry.py +31 -0
  252. docvortex/document/pdf/images.py +654 -0
  253. docvortex/document/pdf/native_annotations.py +367 -0
  254. docvortex/document/pdf/native_contracts.py +169 -0
  255. docvortex/document/pdf/native_coordinates.py +216 -0
  256. docvortex/document/pdf/native_lifecycle.py +16 -0
  257. docvortex/document/pdf/native_objects.py +902 -0
  258. docvortex/document/pdf/native_text_geometry.py +315 -0
  259. docvortex/document/pdf/pdfium.py +325 -0
  260. docvortex/document/pdf/raster.py +46 -0
  261. docvortex/document/pdf/text/__init__.py +62 -0
  262. docvortex/document/pdf/text/contracts.py +211 -0
  263. docvortex/document/pdf/text/extract.py +165 -0
  264. docvortex/document/pdf/text/geometry.py +16 -0
  265. docvortex/document/pdf/text/groups.py +162 -0
  266. docvortex/document/pdf/visual_geometry.py +201 -0
  267. docvortex/document/pdf/visuals.py +343 -0
  268. docvortex/document/source.py +92 -0
  269. docvortex/errors.py +21 -0
  270. docvortex/export/__init__.py +3 -0
  271. docvortex/export/bundle.py +98 -0
  272. docvortex/export/files.py +66 -0
  273. docvortex/export/middle.py +208 -0
  274. docvortex/foundation/__init__.py +3 -0
  275. docvortex/foundation/geometry.py +125 -0
  276. docvortex/foundation/hyperlink.py +65 -0
  277. docvortex/foundation/image.py +48 -0
  278. docvortex/foundation/image_encoding.py +30 -0
  279. docvortex/foundation/image_payload.py +280 -0
  280. docvortex/foundation/language.py +92 -0
  281. docvortex/foundation/platform.py +38 -0
  282. docvortex/foundation/text.py +153 -0
  283. docvortex/foundation/type_identity.py +20 -0
  284. docvortex/foundation/xml_names.py +20 -0
  285. docvortex/options.py +30 -0
  286. docvortex/postprocess/__init__.py +3 -0
  287. docvortex/postprocess/content.py +53 -0
  288. docvortex/postprocess/document.py +19 -0
  289. docvortex/postprocess/lists.py +236 -0
  290. docvortex/postprocess/page_blocks.py +214 -0
  291. docvortex/postprocess/pages.py +95 -0
  292. docvortex/postprocess/paragraphs.py +580 -0
  293. docvortex/postprocess/visual.py +715 -0
  294. docvortex/render/__init__.py +48 -0
  295. docvortex/render/_internal/__init__.py +3 -0
  296. docvortex/render/_internal/common/__init__.py +3 -0
  297. docvortex/render/_internal/common/context.py +43 -0
  298. docvortex/render/_internal/common/html_table.py +178 -0
  299. docvortex/render/_internal/common/index.py +33 -0
  300. docvortex/render/_internal/common/list_items.py +158 -0
  301. docvortex/render/_internal/common/planner.py +140 -0
  302. docvortex/render/_internal/docx/__init__.py +3 -0
  303. docvortex/render/_internal/docx/assets.py +202 -0
  304. docvortex/render/_internal/docx/inline.py +434 -0
  305. docvortex/render/_internal/docx/math.py +220 -0
  306. docvortex/render/_internal/docx/renderer.py +905 -0
  307. docvortex/render/_internal/docx/styles.py +195 -0
  308. docvortex/render/_internal/docx/table.py +442 -0
  309. docvortex/render/_internal/epub/__init__.py +5 -0
  310. docvortex/render/_internal/epub/assets.py +173 -0
  311. docvortex/render/_internal/epub/package.py +249 -0
  312. docvortex/render/_internal/epub/renderer.py +1156 -0
  313. docvortex/render/_internal/html/__init__.py +3 -0
  314. docvortex/render/_internal/html/inline.py +346 -0
  315. docvortex/render/_internal/html/renderer.py +1041 -0
  316. docvortex/render/_internal/html/sanitizer.py +478 -0
  317. docvortex/render/_internal/html/table.py +121 -0
  318. docvortex/render/_internal/latex/__init__.py +1 -0
  319. docvortex/render/_internal/latex/assets.py +85 -0
  320. docvortex/render/_internal/latex/inline.py +145 -0
  321. docvortex/render/_internal/latex/renderer.py +506 -0
  322. docvortex/render/_internal/latex/table.py +347 -0
  323. docvortex/render/_internal/markdown/__init__.py +3 -0
  324. docvortex/render/_internal/markdown/assets.py +78 -0
  325. docvortex/render/_internal/markdown/blocks.py +635 -0
  326. docvortex/render/_internal/markdown/escaping.py +51 -0
  327. docvortex/render/_internal/markdown/inline.py +260 -0
  328. docvortex/render/_internal/markdown/renderer.py +93 -0
  329. docvortex/render/_internal/markdown/table.py +281 -0
  330. docvortex/render/_internal/pdf/__init__.py +3 -0
  331. docvortex/render/_internal/pdf/assets.py +197 -0
  332. docvortex/render/_internal/pdf/formula.py +417 -0
  333. docvortex/render/_internal/pdf/inline.py +343 -0
  334. docvortex/render/_internal/pdf/renderer.py +734 -0
  335. docvortex/render/_internal/pdf/styles.py +206 -0
  336. docvortex/render/_internal/pdf/table.py +272 -0
  337. docvortex/render/_internal/structured_content/__init__.py +3 -0
  338. docvortex/render/_internal/structured_content/renderer.py +193 -0
  339. docvortex/render/api.py +199 -0
  340. docvortex/render/contracts.py +205 -0
  341. docvortex/render/docx.py +38 -0
  342. docvortex/render/epub.py +35 -0
  343. docvortex/render/fragments.py +70 -0
  344. docvortex/render/html.py +29 -0
  345. docvortex/render/latex.py +24 -0
  346. docvortex/render/markdown.py +50 -0
  347. docvortex/render/pdf.py +25 -0
  348. docvortex/render/structured_content.py +23 -0
  349. docvortex/resources/epub/docvortex.css +91 -0
  350. docvortex/resources/fasttext-langdetect/lid.176.ftz +0 -0
  351. docvortex/resources/fonts/DroidSansFallbackFull.ttf +0 -0
  352. docvortex/resources/fonts/NOTICE +190 -0
  353. docvortex/resources/fonts/manifest.json +9 -0
  354. docvortex/resources/html/docvortex.css +601 -0
  355. docvortex/resources/html/docvortex.min.css +1 -0
  356. docvortex/result.py +91 -0
  357. docvortex/schema.py +1141 -0
  358. docvortex/version.py +3 -0
  359. docvortex-0.2.1.dist-info/METADATA +193 -0
  360. docvortex-0.2.1.dist-info/RECORD +364 -0
  361. docvortex-0.2.1.dist-info/WHEEL +5 -0
  362. docvortex-0.2.1.dist-info/entry_points.txt +2 -0
  363. docvortex-0.2.1.dist-info/licenses/LICENSE.md +21 -0
  364. 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