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,521 @@
1
+ """Excel 数值格式解析与稳定显示文本生成。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import datetime, timedelta
7
+ from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
8
+ from fractions import Fraction
9
+ import math
10
+ import re
11
+
12
+
13
+ BUILTIN_NUMBER_FORMATS: dict[int, str] = {
14
+ 1: "0",
15
+ 2: "0.00",
16
+ 3: "#,##0",
17
+ 4: "#,##0.00",
18
+ 9: "0%",
19
+ 10: "0.00%",
20
+ 11: "0.00E+00",
21
+ 12: "# ?/?",
22
+ 13: "# ??/??",
23
+ 14: "mm-dd-yy",
24
+ 15: "d-mmm-yy",
25
+ 16: "d-mmm",
26
+ 17: "mmm-yy",
27
+ 18: "h:mm AM/PM",
28
+ 19: "h:mm:ss AM/PM",
29
+ 20: "h:mm",
30
+ 21: "h:mm:ss",
31
+ 22: "m/d/yy h:mm",
32
+ 37: "#,##0 ;(#,##0)",
33
+ 38: "#,##0 ;[Red](#,##0)",
34
+ 39: "#,##0.00;(#,##0.00)",
35
+ 40: "#,##0.00;[Red](#,##0.00)",
36
+ 45: "mm:ss",
37
+ 46: "[h]:mm:ss",
38
+ 47: "mmss.0",
39
+ 48: "##0.0E+0",
40
+ 49: "@",
41
+ }
42
+
43
+ _COLOR_NAMES = {
44
+ "black",
45
+ "blue",
46
+ "cyan",
47
+ "green",
48
+ "magenta",
49
+ "red",
50
+ "white",
51
+ "yellow",
52
+ }
53
+ _CONDITION_RE = re.compile(r"^(<=|>=|<>|=|<|>)([-+]?(?:\d+(?:\.\d*)?|\.\d+))$")
54
+
55
+
56
+ @dataclass(frozen=True, slots=True)
57
+ class _Condition:
58
+ """一个数值格式 section 的比较条件。"""
59
+
60
+ operator: str
61
+ operand: float
62
+
63
+ def matches(self, value: float) -> bool:
64
+ """判断数值是否满足当前比较条件。"""
65
+
66
+ return {
67
+ "<": value < self.operand,
68
+ "<=": value <= self.operand,
69
+ ">": value > self.operand,
70
+ ">=": value >= self.operand,
71
+ "=": value == self.operand,
72
+ "<>": value != self.operand,
73
+ }[self.operator]
74
+
75
+
76
+ @dataclass(frozen=True, slots=True)
77
+ class _Section:
78
+ """一个已拆分但仍保留 Excel 格式语法的 section。"""
79
+
80
+ pattern: str
81
+ condition: _Condition | None
82
+
83
+
84
+ def builtin_number_format(format_id: int) -> str | None:
85
+ """返回确定的内建格式代码,地区相关格式不做猜测。"""
86
+
87
+ return BUILTIN_NUMBER_FORMATS.get(int(format_id))
88
+
89
+
90
+ def format_general(value: float) -> str:
91
+ """按 Excel 15 位有效数字输出无格式浮点数。"""
92
+
93
+ if not math.isfinite(value):
94
+ return str(value)
95
+ if value == 0:
96
+ return "0"
97
+ rounded = float(f"{value:.15g}")
98
+ if rounded.is_integer() and abs(rounded) < 1e15:
99
+ return str(int(rounded))
100
+ return format(rounded, ".15g")
101
+
102
+
103
+ def _split_sections(code: str) -> list[str] | None:
104
+ """在不切开引号、转义和方括号的前提下拆分分号 sections。"""
105
+
106
+ sections: list[str] = []
107
+ current: list[str] = []
108
+ quote = False
109
+ bracket_depth = 0
110
+ escaped = False
111
+ for char in code:
112
+ if escaped:
113
+ current.append(char)
114
+ escaped = False
115
+ continue
116
+ if char == "\\" and not quote:
117
+ current.append(char)
118
+ escaped = True
119
+ continue
120
+ if char == '"':
121
+ current.append(char)
122
+ quote = not quote
123
+ continue
124
+ if not quote and char == "[":
125
+ bracket_depth += 1
126
+ elif not quote and char == "]":
127
+ bracket_depth -= 1
128
+ if bracket_depth < 0:
129
+ return None
130
+ if char == ";" and not quote and bracket_depth == 0:
131
+ sections.append("".join(current))
132
+ current = []
133
+ else:
134
+ current.append(char)
135
+ if quote or bracket_depth != 0 or escaped:
136
+ return None
137
+ sections.append("".join(current))
138
+ return sections if len(sections) <= 4 else None
139
+
140
+
141
+ def _strip_brackets(pattern: str) -> tuple[str, _Condition | None, bool]:
142
+ """移除颜色/条件/地区标记,同时识别 elapsed 时间格式。"""
143
+
144
+ output: list[str] = []
145
+ condition: _Condition | None = None
146
+ elapsed = False
147
+ cursor = 0
148
+ while cursor < len(pattern):
149
+ if pattern[cursor] != "[":
150
+ output.append(pattern[cursor])
151
+ cursor += 1
152
+ continue
153
+ end = pattern.find("]", cursor + 1)
154
+ if end < 0:
155
+ return pattern, condition, elapsed
156
+ inner = pattern[cursor + 1 : end]
157
+ match = _CONDITION_RE.fullmatch(inner)
158
+ if match and condition is None:
159
+ condition = _Condition(match.group(1), float(match.group(2)))
160
+ elif inner.casefold() in _COLOR_NAMES or re.fullmatch(r"Color\d+", inner, re.I):
161
+ pass
162
+ elif inner.casefold() in {"h", "hh", "m", "mm", "s", "ss"}:
163
+ elapsed = True
164
+ output.append(f"[{inner}]")
165
+ elif inner.startswith("$"):
166
+ currency = inner[1:].split("-", 1)[0]
167
+ output.append(currency)
168
+ else:
169
+ output.append(f"[{inner}]")
170
+ cursor = end + 1
171
+ return "".join(output), condition, elapsed
172
+
173
+
174
+ def _parse_sections(code: str) -> list[_Section] | None:
175
+ """拆分格式并提取每个 section 的可选条件。"""
176
+
177
+ raw_sections = _split_sections(code)
178
+ if raw_sections is None or not raw_sections:
179
+ return None
180
+ sections: list[_Section] = []
181
+ for raw in raw_sections:
182
+ pattern, condition, _ = _strip_brackets(raw)
183
+ sections.append(_Section(pattern=pattern, condition=condition))
184
+ if sum(section.condition is not None for section in sections) > 2:
185
+ return None
186
+ return sections
187
+
188
+
189
+ def _choose_numeric_section(
190
+ sections: list[_Section],
191
+ value: float,
192
+ ) -> tuple[_Section, float, bool] | None:
193
+ """按条件或正负零位置选择用于渲染的数值 section。"""
194
+
195
+ numeric = sections[:-1] if len(sections) == 4 else sections
196
+ if not numeric:
197
+ return None
198
+ if any(section.condition is not None for section in numeric):
199
+ for section in numeric:
200
+ if section.condition is None or section.condition.matches(value):
201
+ return section, value, True
202
+ return None
203
+ if len(numeric) == 1:
204
+ return numeric[0], value, True
205
+ if len(numeric) == 2:
206
+ return (numeric[1], abs(value), False) if value < 0 else (numeric[0], value, False)
207
+ if value > 0:
208
+ return numeric[0], value, False
209
+ if value < 0:
210
+ return numeric[1], abs(value), False
211
+ return numeric[2], value, False
212
+
213
+
214
+ def _literalize(pattern: str) -> str:
215
+ """把引号、转义、下划线和填充语法还原为显示字面量。"""
216
+
217
+ output: list[str] = []
218
+ cursor = 0
219
+ quote = False
220
+ while cursor < len(pattern):
221
+ char = pattern[cursor]
222
+ if char == '"':
223
+ quote = not quote
224
+ cursor += 1
225
+ continue
226
+ if not quote and char == "\\" and cursor + 1 < len(pattern):
227
+ output.append(pattern[cursor + 1])
228
+ cursor += 2
229
+ continue
230
+ if not quote and char == "_" and cursor + 1 < len(pattern):
231
+ output.append(" ")
232
+ cursor += 2
233
+ continue
234
+ if not quote and char == "*" and cursor + 1 < len(pattern):
235
+ cursor += 2
236
+ continue
237
+ output.append(char)
238
+ cursor += 1
239
+ return "".join(output)
240
+
241
+
242
+ def _syntax_view(pattern: str) -> str:
243
+ """移除字面量后返回仅供格式类型判定的语法视图。"""
244
+
245
+ output: list[str] = []
246
+ cursor = 0
247
+ quote = False
248
+ while cursor < len(pattern):
249
+ char = pattern[cursor]
250
+ if char == '"':
251
+ quote = not quote
252
+ cursor += 1
253
+ continue
254
+ if not quote and char in {"\\", "_", "*"} and cursor + 1 < len(pattern):
255
+ cursor += 2
256
+ continue
257
+ if not quote:
258
+ output.append(char)
259
+ cursor += 1
260
+ return "".join(output)
261
+
262
+
263
+ def _date_parts(pattern: str) -> tuple[bool, bool, bool] | None:
264
+ """判断格式是否表示日期、时间或 elapsed 时长。"""
265
+
266
+ syntax = _syntax_view(pattern).casefold()
267
+ elapsed = bool(re.search(r"\[(?:h+|m+|s+)\]", syntax))
268
+ syntax_without_elapsed = re.sub(r"\[(?:h+|m+|s+)\]", "", syntax)
269
+ has_year_or_day = bool(re.search(r"y|d", syntax_without_elapsed))
270
+ has_hour_or_second = bool(re.search(r"h|s|am/pm|a/p", syntax_without_elapsed))
271
+ has_month_or_minute = "m" in syntax_without_elapsed
272
+ if not (elapsed or has_year_or_day or has_hour_or_second or has_month_or_minute):
273
+ return None
274
+ if not re.search(r"[ydhms]", syntax_without_elapsed) and not elapsed:
275
+ return None
276
+ if has_month_or_minute:
277
+ minute_context = has_hour_or_second or bool(re.search(r"m+\s*:\s*s+|h+\s*:\s*m+", syntax))
278
+ has_date = has_year_or_day or not minute_context
279
+ has_time = has_hour_or_second or minute_context
280
+ else:
281
+ has_date = has_year_or_day
282
+ has_time = has_hour_or_second
283
+ if elapsed:
284
+ has_time = True
285
+ if not has_year_or_day:
286
+ has_date = False
287
+ return has_date, has_time, elapsed
288
+
289
+
290
+ def _render_serial(value: float, *, date1904: bool, parts: tuple[bool, bool, bool]) -> str:
291
+ """把 Excel serial 按确定的 ISO 日期/时间策略输出。"""
292
+
293
+ has_date, has_time, elapsed = parts
294
+ if elapsed:
295
+ total_seconds = int(round(abs(value) * 86_400))
296
+ sign = "-" if value < 0 else ""
297
+ hours, remainder = divmod(total_seconds, 3600)
298
+ minutes, seconds = divmod(remainder, 60)
299
+ return f"{sign}{hours}:{minutes:02}:{seconds:02}"
300
+ if not has_date:
301
+ total_seconds = int(round(abs(value % 1) * 86_400)) % 86_400
302
+ hours, remainder = divmod(total_seconds, 3600)
303
+ minutes, seconds = divmod(remainder, 60)
304
+ return f"{hours:02}:{minutes:02}:{seconds:02}"
305
+ days = math.floor(value)
306
+ if not date1904 and days == 60:
307
+ return format_general(value)
308
+ try:
309
+ if date1904:
310
+ resolved = datetime(1904, 1, 1) + timedelta(days=value)
311
+ else:
312
+ corrected = value - 1 if value >= 60 else value
313
+ resolved = datetime(1899, 12, 31) + timedelta(days=corrected)
314
+ except (OverflowError, ValueError):
315
+ return format_general(value)
316
+ output = resolved.strftime("%Y-%m-%d")
317
+ if has_time:
318
+ output += resolved.strftime(" %H:%M:%S")
319
+ return output
320
+
321
+
322
+ def _decimal_quantize(value: float, places: int) -> Decimal:
323
+ """以 Excel 接近的 half-up 规则按指定位数舍入。"""
324
+
325
+ decimal_value = Decimal(format(value, ".15g"))
326
+ quantum = Decimal(1).scaleb(-places)
327
+ return decimal_value.quantize(quantum, rounding=ROUND_HALF_UP)
328
+
329
+
330
+ def _extract_number_span(pattern: str) -> tuple[str, str, str]:
331
+ """拆出数字占位主体及其前后字面量。"""
332
+
333
+ literalized = _literalize(pattern)
334
+ general_match = re.search("general", literalized, re.I)
335
+ if general_match is not None:
336
+ return (
337
+ literalized[: general_match.start()],
338
+ literalized[general_match.start() : general_match.end()],
339
+ literalized[general_match.end() :],
340
+ )
341
+ positions = [index for index, char in enumerate(literalized) if char in "0#?@"]
342
+ if not positions:
343
+ return literalized, "", ""
344
+ start, end = min(positions), max(positions) + 1
345
+ numeric_syntax = set("0123456789#?.,%Ee+-/")
346
+ while start > 0 and literalized[start - 1] in numeric_syntax:
347
+ start -= 1
348
+ while end < len(literalized) and literalized[end] in numeric_syntax:
349
+ end += 1
350
+ return literalized[:start], literalized[start:end], literalized[end:]
351
+
352
+
353
+ def _render_scientific(value: float, pattern: str) -> str | None:
354
+ """按 Excel 科学计数占位符输出 mantissa 与 exponent。"""
355
+
356
+ match = re.search(r"([0#?]+)(?:\.([0#?]+))?[Ee]([+-])([0#?]+)", pattern)
357
+ if match is None:
358
+ return None
359
+ int_places = len(match.group(1))
360
+ frac_places = len(match.group(2) or "")
361
+ exponent_places = len(match.group(4))
362
+ if value == 0:
363
+ exponent = 0
364
+ mantissa = 0.0
365
+ else:
366
+ raw_exponent = math.floor(math.log10(abs(value)))
367
+ exponent = math.floor(raw_exponent / max(int_places, 1)) * max(int_places, 1)
368
+ mantissa = value / (10**exponent)
369
+ rounded = _decimal_quantize(mantissa, frac_places)
370
+ if abs(rounded) >= 10**int_places:
371
+ exponent += max(int_places, 1)
372
+ rounded /= Decimal(10 ** max(int_places, 1))
373
+ mantissa_text = f"{rounded:.{frac_places}f}"
374
+ exponent_sign = "+" if exponent >= 0 else "-"
375
+ if match.group(3) == "-" and exponent >= 0:
376
+ exponent_sign = ""
377
+ exponent_text = str(abs(exponent)).rjust(exponent_places, "0")
378
+ return f"{mantissa_text}E{exponent_sign}{exponent_text}"
379
+
380
+
381
+ def _render_fraction(value: float, pattern: str) -> str | None:
382
+ """按固定或占位 denominator 输出最接近的分数。"""
383
+
384
+ match = re.search(r"([#0?]*)\s*([0#?]+)\/([0-9#?]+)", pattern)
385
+ if match is None:
386
+ return None
387
+ integer_pattern, numerator_pattern, denominator_pattern = match.groups()
388
+ whole = math.floor(abs(value)) if integer_pattern else 0
389
+ fraction_value = abs(value) - whole
390
+ fixed_denominator = int(denominator_pattern) if denominator_pattern.isdigit() else None
391
+ if fixed_denominator:
392
+ denominator = fixed_denominator
393
+ numerator = int(Decimal(format(fraction_value * denominator, ".15g")).quantize(Decimal(1), rounding=ROUND_HALF_UP))
394
+ else:
395
+ max_denominator = max(1, 10 ** len(denominator_pattern) - 1)
396
+ fraction = Fraction(fraction_value).limit_denominator(max_denominator)
397
+ numerator, denominator = fraction.numerator, fraction.denominator
398
+ if numerator == 0:
399
+ return str(whole) if whole else ""
400
+ if numerator >= denominator and integer_pattern:
401
+ whole += numerator // denominator
402
+ numerator %= denominator
403
+ numerator_text = str(numerator).rjust(len(numerator_pattern), " " if "?" in numerator_pattern else "0")
404
+ denominator_text = str(denominator).rjust(
405
+ len(denominator_pattern),
406
+ " " if "?" in denominator_pattern else "0",
407
+ )
408
+ fraction_text = f"{numerator_text}/{denominator_text}"
409
+ return f"{whole} {fraction_text}" if whole else fraction_text
410
+
411
+
412
+ def _render_decimal(value: float, pattern: str) -> str:
413
+ """按整数、小数、分组、缩放与百分号占位符输出数值。"""
414
+
415
+ percent_count = pattern.count("%")
416
+ core = pattern.replace("%", "")
417
+ placeholder_positions = [index for index, char in enumerate(core) if char in "0#?"]
418
+ trailing_commas = 0
419
+ if placeholder_positions:
420
+ suffix = core[max(placeholder_positions) + 1 :]
421
+ trailing_commas = suffix.count(",") if not suffix.strip(", ") else 0
422
+ if trailing_commas:
423
+ remove_from = max(placeholder_positions) + 1
424
+ core = core[:remove_from] + core[remove_from:].replace(",", "")
425
+ decimal_pos = core.find(".")
426
+ integer_pattern = core if decimal_pos < 0 else core[:decimal_pos]
427
+ fractional_pattern = "" if decimal_pos < 0 else core[decimal_pos + 1 :]
428
+ scaled = value * (100**percent_count) / (1000**trailing_commas)
429
+ integer_placeholders = "".join(char for char in integer_pattern if char in "0#?")
430
+ fractional_placeholders = "".join(char for char in fractional_pattern if char in "0#?")
431
+ decimal_places = len(fractional_placeholders)
432
+ rounded = _decimal_quantize(abs(scaled), decimal_places)
433
+ absolute_text = f"{rounded:.{decimal_places}f}"
434
+ integer_text, _, fraction_text = absolute_text.partition(".")
435
+ required_integer = integer_placeholders.count("0")
436
+ if len(integer_text) < required_integer:
437
+ integer_text = integer_text.rjust(required_integer, "0")
438
+ if rounded == 0 and not required_integer and integer_placeholders:
439
+ integer_text = ""
440
+ if "," in integer_pattern and integer_text:
441
+ integer_text = f"{int(integer_text):,}"
442
+ if integer_placeholders.startswith("?") and integer_text:
443
+ integer_text = integer_text.rjust(len(integer_placeholders), " ")
444
+ visible_fraction = list(fraction_text)
445
+ for index in range(len(fractional_placeholders) - 1, -1, -1):
446
+ placeholder = fractional_placeholders[index]
447
+ if placeholder == "0":
448
+ break
449
+ if index >= len(visible_fraction) or visible_fraction[index] != "0":
450
+ break
451
+ visible_fraction[index] = " " if placeholder == "?" else ""
452
+ rendered_fraction = "".join(visible_fraction)
453
+ show_decimal = decimal_pos >= 0 and (rendered_fraction != "" or not fractional_placeholders)
454
+ result = integer_text
455
+ if show_decimal:
456
+ result += "." + rendered_fraction
457
+ if scaled < 0:
458
+ result = "-" + result
459
+ return result + ("%" * percent_count)
460
+
461
+
462
+ def _render_pattern(pattern: str, value: float, auto_minus: bool) -> str:
463
+ """渲染一个已经选定的数值 section。"""
464
+
465
+ if not pattern:
466
+ return ""
467
+ prefix, core, suffix = _extract_number_span(pattern)
468
+ if core.casefold() == "general":
469
+ body = format_general(abs(value) if not auto_minus else value)
470
+ if value < 0 and not auto_minus:
471
+ body = format_general(abs(value))
472
+ return prefix + body + suffix
473
+ if not any(char in core for char in "0#?"):
474
+ return prefix + suffix
475
+ if re.search(r"[Ee][+-][0#?]", core):
476
+ body = _render_scientific(abs(value), core) or format_general(abs(value))
477
+ elif "/" in core:
478
+ body = _render_fraction(abs(value), core) or format_general(abs(value))
479
+ else:
480
+ body = _render_decimal(abs(value), core)
481
+ if auto_minus and value < 0:
482
+ body = "-" + body
483
+ return prefix + body + suffix
484
+
485
+
486
+ def format_number(value: float, format_code: str | None, *, date1904: bool) -> str:
487
+ """使用 Excel format code 渲染数值,无法解析时退回 General。"""
488
+
489
+ if format_code is None or not math.isfinite(value):
490
+ return format_general(value)
491
+ sections = _parse_sections(format_code)
492
+ if sections is None:
493
+ return format_general(value)
494
+ chosen = _choose_numeric_section(sections, value)
495
+ if chosen is None:
496
+ return format_general(value)
497
+ section, selected_value, auto_minus = chosen
498
+ pattern, _, _ = _strip_brackets(section.pattern)
499
+ parts = _date_parts(pattern)
500
+ if parts is not None:
501
+ return _render_serial(selected_value, date1904=date1904, parts=parts)
502
+ try:
503
+ return _render_pattern(pattern, selected_value, auto_minus)
504
+ except (InvalidOperation, OverflowError, ValueError, ZeroDivisionError):
505
+ return format_general(value)
506
+
507
+
508
+ def format_text(text: str, format_code: str | None) -> str:
509
+ """应用第四个文本 section;没有文本 section 时保持原文。"""
510
+
511
+ if format_code is None:
512
+ return text
513
+ sections = _parse_sections(format_code)
514
+ if sections is None:
515
+ return text
516
+ if len(sections) == 4:
517
+ pattern = _literalize(sections[3].pattern)
518
+ return pattern.replace("@", text)
519
+ if len(sections) == 1 and "@" in sections[0].pattern:
520
+ return _literalize(sections[0].pattern).replace("@", text)
521
+ return text