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,347 @@
1
+ """HTML table 到 TeX Live longtable/tabular 源码的安全物化。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TypeAlias
6
+
7
+ from bs4 import NavigableString, Tag
8
+ from pydantic import ValidationError
9
+
10
+ from ....schema import CodeInlineSpan, EquationInlineSpan, HyperlinkSpan, InlineSpan, InlineStyle, TextSpan, parse_inline_spans
11
+ from ....foundation.hyperlink import OFFICE_EXTERNAL_HYPERLINK_SCHEMES, sanitize_hyperlink_target
12
+ from ..common.html_table import (
13
+ HtmlTableCell,
14
+ HtmlTableError,
15
+ HtmlTableGrid,
16
+ HtmlTableSource,
17
+ MAX_NESTED_TABLE_DEPTH,
18
+ parse_html_tables,
19
+ )
20
+ from .assets import resolve_html_image_path, tex_image_path
21
+ from .inline import LatexAnchorRegistry, escape_latex_text, escape_latex_url, render_inline_spans
22
+
23
+ _BLOCK_TAGS = {"address", "article", "blockquote", "div", "figcaption", "footer", "header", "li", "p", "section"}
24
+ _SKIPPED_TAGS = {"script", "style", "template", "noscript"}
25
+ _HtmlCellToken: TypeAlias = InlineSpan | Tag
26
+
27
+
28
+ class LatexTableError(HtmlTableError):
29
+ """表示 HTML table 无法安全物化为 LaTeX 表格。"""
30
+
31
+
32
+ class LatexTableRenderer:
33
+ """持有图片路径和 anchor 上下文的递归 LaTeX 表格 renderer。"""
34
+
35
+ def __init__(self, *, asset_base_path: str, anchors: LatexAnchorRegistry) -> None:
36
+ """保存表格单元格渲染所需的文档级上下文。"""
37
+ self.asset_base_path = asset_base_path
38
+ self.anchors = anchors
39
+
40
+ def render(self, source: HtmlTableSource) -> str:
41
+ """把一个或多个顶层 HTML table 转换为可分页 LaTeX 表格。"""
42
+ return self._render_source(source, depth=1)
43
+
44
+ def _render_source(self, source: HtmlTableSource, *, depth: int) -> str:
45
+ """递归解析当前层级的 table,并执行深度上限校验。"""
46
+ if depth > MAX_NESTED_TABLE_DEPTH:
47
+ raise LatexTableError(f"Nested table depth exceeds {MAX_NESTED_TABLE_DEPTH}")
48
+ try:
49
+ grids = parse_html_tables(source)
50
+ except HtmlTableError as exc:
51
+ raise LatexTableError(str(exc)) from exc
52
+ return "\n\n".join(self._render_grid(grid, depth=depth) for grid in grids)
53
+
54
+ def _render_grid(self, grid: HtmlTableGrid, *, depth: int) -> str:
55
+ """把严格占位网格转换为 longtable 或嵌套 tabular。"""
56
+ environment = "longtable" if depth == 1 else "tabular"
57
+ column_spec = self._column_spec(grid.column_count)
58
+ occupied = {
59
+ (row, column): placement
60
+ for placement in grid.cells
61
+ for row in range(placement.row, placement.end_row + 1)
62
+ for column in range(placement.column, placement.end_column + 1)
63
+ }
64
+ lines = [rf"\begin{{{environment}}}{{{column_spec}}}", r"\hline"]
65
+ for row_index in range(grid.row_count):
66
+ segments: list[str] = []
67
+ column_index = 0
68
+ while column_index < grid.column_count:
69
+ placement = occupied[(row_index, column_index)]
70
+ if placement.column != column_index:
71
+ column_index += 1
72
+ continue
73
+ if placement.row == row_index:
74
+ segment = self._render_cell(
75
+ placement.tag,
76
+ is_header=placement.is_header,
77
+ rowspan=placement.rowspan,
78
+ colspan=placement.colspan,
79
+ column_count=grid.column_count,
80
+ depth=depth,
81
+ )
82
+ else:
83
+ segment = self._continued_rowspan_placeholder(placement.colspan)
84
+ segments.append(segment)
85
+ column_index += placement.colspan
86
+ lines.append(" & ".join(segments) + r" \\")
87
+ lines.extend(_row_rules(occupied, row_index=row_index, column_count=grid.column_count))
88
+ lines.append(rf"\end{{{environment}}}")
89
+ return "\n".join(lines)
90
+
91
+ def _render_cell(
92
+ self,
93
+ cell: Tag,
94
+ *,
95
+ is_header: bool,
96
+ rowspan: int,
97
+ colspan: int,
98
+ column_count: int,
99
+ depth: int,
100
+ ) -> str:
101
+ """渲染一个 origin cell,并组合 rowspan/colspan 声明。"""
102
+ parts = self._render_cell_parts(cell, depth=depth)
103
+ content = r"\par ".join(part for part in parts if part) or "~"
104
+ if is_header:
105
+ content = rf"\cellcolor{{DocVortexTableHeader}}\textbf{{{content}}}"
106
+ width = _cell_width(colspan, column_count)
107
+ if rowspan > 1:
108
+ content = rf"\multirow{{{rowspan}}}{{*}}{{\parbox[t]{{{width}}}{{{content}}}}}"
109
+ if colspan > 1:
110
+ content = rf"\multicolumn{{{colspan}}}{{|p{{{width}}}|}}{{{content}}}"
111
+ return content
112
+
113
+ def _render_cell_parts(self, cell: Tag, *, depth: int) -> list[str]:
114
+ """按 HTML 来源顺序交替渲染行内内容、图片与嵌套表格。"""
115
+
116
+ parts: list[str] = []
117
+ inline_buffer: list[InlineSpan] = []
118
+
119
+ def flush_inline_buffer() -> None:
120
+ """把当前连续行内 token 校验并渲染为一个 LaTeX 片段。"""
121
+
122
+ if not inline_buffer:
123
+ return
124
+ spans = _parse_cell_inline_spans(inline_buffer)
125
+ rendered = render_inline_spans(spans, self.anchors)
126
+ if rendered:
127
+ parts.append(rendered)
128
+ inline_buffer.clear()
129
+
130
+ for token in _html_cell_tokens(cell):
131
+ if not isinstance(token, Tag):
132
+ inline_buffer.append(token)
133
+ continue
134
+ flush_inline_buffer()
135
+ if token.name == "img":
136
+ parts.append(self._render_image_tag(token))
137
+ elif token.name == "table":
138
+ parts.append(self._render_source(token, depth=depth + 1))
139
+ flush_inline_buffer()
140
+ return parts
141
+
142
+ def _render_image_tag(self, image: Tag) -> str:
143
+ """读取一个单元格图片标签并渲染为 LaTeX 图片或可见回退。"""
144
+
145
+ source = image.get("src", "")
146
+ if isinstance(source, list):
147
+ source = ""
148
+ alt = image.get("alt", "image")
149
+ if isinstance(alt, list):
150
+ alt = " ".join(str(item) for item in alt)
151
+ return self._render_image(str(source), str(alt))
152
+
153
+ def _render_image(self, source: str, alt_text: str) -> str:
154
+ """渲染单元格图片;非本地 sidecar 退化为可见链接或占位。"""
155
+ path = resolve_html_image_path(source, self.asset_base_path)
156
+ if path is not None:
157
+ return rf"\includegraphics[width=.85\linewidth,height=.25\textheight,keepaspectratio]{{{tex_image_path(path)}}}"
158
+ label = rf"\textit{{{escape_latex_text(f'image unavailable: {alt_text}')}}}"
159
+ normalized = source.strip()
160
+ if normalized.startswith(("http://", "https://")):
161
+ return rf"\href{{{escape_latex_url(normalized)}}}{{{label}}}"
162
+ return label
163
+
164
+ @staticmethod
165
+ def _column_spec(column_count: int) -> str:
166
+ """按逻辑列数生成确定性的等宽段落列声明。"""
167
+ if column_count <= 0:
168
+ raise LatexTableError("Table must contain at least one column")
169
+ width = _cell_width(1, column_count)
170
+ column = rf">{{\raggedright\arraybackslash}}p{{{width}}}"
171
+ return "|" + "|".join(column for _ in range(column_count)) + "|"
172
+
173
+ @staticmethod
174
+ def _continued_rowspan_placeholder(colspan: int) -> str:
175
+ """为前序行延续下来的 rowspan 写入等列宽空占位。"""
176
+ return rf"\multicolumn{{{colspan}}}{{|l|}}{{}}" if colspan > 1 else ""
177
+
178
+
179
+ def _cell_width(colspan: int, column_count: int) -> str:
180
+ """按表格总列数计算当前单元格占用的 linewidth 比例。"""
181
+ ratio = 0.94 * colspan / column_count
182
+ return f"{ratio:.6f}\\linewidth"
183
+
184
+
185
+ def _row_rules(
186
+ occupied: dict[tuple[int, int], HtmlTableCell],
187
+ *,
188
+ row_index: int,
189
+ column_count: int,
190
+ ) -> list[str]:
191
+ """只在当前行结束的单元格列下画线,避免横线切穿 rowspan。"""
192
+ ending_columns = [column for column in range(column_count) if occupied[(row_index, column)].end_row == row_index]
193
+ if len(ending_columns) == column_count:
194
+ return [r"\hline"]
195
+ rules: list[str] = []
196
+ range_start: int | None = None
197
+ previous: int | None = None
198
+ for column in ending_columns:
199
+ if range_start is None:
200
+ range_start = column
201
+ elif previous is not None and column != previous + 1:
202
+ rules.append(rf"\cline{{{range_start + 1}-{previous + 1}}}")
203
+ range_start = column
204
+ previous = column
205
+ if range_start is not None and previous is not None:
206
+ rules.append(rf"\cline{{{range_start + 1}-{previous + 1}}}")
207
+ return rules
208
+
209
+
210
+ def _parse_cell_inline_spans(spans: list[InlineSpan]) -> list[InlineSpan]:
211
+ """校验一个不跨图片或嵌套表格的连续单元格行内片段。"""
212
+
213
+ try:
214
+ return parse_inline_spans(spans)
215
+ except (TypeError, ValidationError, ValueError) as exc:
216
+ raise LatexTableError("HTML table cell inline content is invalid") from exc
217
+
218
+
219
+ def _html_cell_tokens(cell: Tag) -> list[_HtmlCellToken]:
220
+ """按来源顺序返回单元格中的行内、图片与嵌套表格 token。"""
221
+
222
+ return [
223
+ token
224
+ for child in cell.children
225
+ for token in _html_node_tokens(
226
+ child,
227
+ styles=(),
228
+ allow_links=True,
229
+ )
230
+ ]
231
+
232
+
233
+ def _html_node_tokens(
234
+ node: object,
235
+ *,
236
+ styles: tuple[InlineStyle, ...],
237
+ allow_links: bool,
238
+ ) -> list[_HtmlCellToken]:
239
+ """递归解析 HTML 节点,同时把图片和嵌套表格保留为顺序屏障。"""
240
+
241
+ if isinstance(node, NavigableString):
242
+ text = str(node)
243
+ return [TextSpan(type="text", content=text, styles=list(styles))] if text else []
244
+ if not isinstance(node, Tag):
245
+ return []
246
+ name = (node.name or "").lower()
247
+ if name in _SKIPPED_TAGS:
248
+ return []
249
+ if name in {"img", "table"}:
250
+ return [node]
251
+ if name == "br":
252
+ return [TextSpan(type="text", content="\n", styles=list(styles))]
253
+ if name == "eq":
254
+ content = node.get_text()
255
+ return [EquationInlineSpan(type="equation_inline", content=content)] if content.strip() else []
256
+ if name == "code":
257
+ content = node.get_text()
258
+ return [CodeInlineSpan(type="code_inline", content=content)] if content else []
259
+ style_name: InlineStyle | None = {
260
+ "b": "bold",
261
+ "strong": "bold",
262
+ "i": "italic",
263
+ "em": "italic",
264
+ "u": "underline",
265
+ "s": "strikethrough",
266
+ "del": "strikethrough",
267
+ "sup": "superscript",
268
+ "sub": "subscript",
269
+ }.get(name)
270
+ child_styles = tuple(dict.fromkeys((*styles, style_name))) if style_name is not None else styles
271
+ if name == "a" and allow_links:
272
+ children = [
273
+ token
274
+ for child in node.children
275
+ for token in _html_node_tokens(
276
+ child,
277
+ styles=child_styles,
278
+ allow_links=False,
279
+ )
280
+ if not isinstance(token, HyperlinkSpan)
281
+ ]
282
+ target = sanitize_hyperlink_target(
283
+ node.get("href"),
284
+ allowed_schemes=OFFICE_EXTERNAL_HYPERLINK_SCHEMES,
285
+ allow_relative=True,
286
+ allow_fragment=True,
287
+ )
288
+ return _wrap_hyperlink_tokens(children, target)
289
+ tokens = [
290
+ token
291
+ for child in node.children
292
+ for token in _html_node_tokens(
293
+ child,
294
+ styles=child_styles,
295
+ allow_links=allow_links,
296
+ )
297
+ ]
298
+ if name in _BLOCK_TAGS and tokens and not _tokens_end_with_newline(tokens):
299
+ tokens.append(TextSpan(type="text", content="\n"))
300
+ return tokens
301
+
302
+
303
+ def _wrap_hyperlink_tokens(
304
+ tokens: list[_HtmlCellToken],
305
+ target: str | None,
306
+ ) -> list[_HtmlCellToken]:
307
+ """只包装链接内连续的 InlineSpan,让图片和嵌套表格保持原位。"""
308
+
309
+ if target is None:
310
+ return tokens
311
+ output: list[_HtmlCellToken] = []
312
+ inline_buffer: list[InlineSpan] = []
313
+
314
+ def flush_inline_buffer() -> None:
315
+ """把当前链接行内片段包装成一个 HyperlinkSpan。"""
316
+
317
+ if not inline_buffer:
318
+ return
319
+ try:
320
+ output.append(
321
+ HyperlinkSpan(
322
+ type="hyperlink",
323
+ url=target,
324
+ content=list(inline_buffer),
325
+ )
326
+ )
327
+ except ValidationError:
328
+ output.extend(inline_buffer)
329
+ inline_buffer.clear()
330
+
331
+ for token in tokens:
332
+ if isinstance(token, Tag):
333
+ flush_inline_buffer()
334
+ output.append(token)
335
+ else:
336
+ inline_buffer.append(token)
337
+ flush_inline_buffer()
338
+ return output
339
+
340
+
341
+ def _tokens_end_with_newline(tokens: list[_HtmlCellToken]) -> bool:
342
+ """判断当前 token 序列是否已经以普通文本换行结束。"""
343
+
344
+ return bool(tokens and isinstance(tokens[-1], TextSpan) and tokens[-1].content.endswith("\n"))
345
+
346
+
347
+ __all__ = ["LatexTableError", "LatexTableRenderer"]
@@ -0,0 +1,3 @@
1
+ """Markdown 与 Content List 共用的内部实现。"""
2
+
3
+ __all__: list[str] = []
@@ -0,0 +1,78 @@
1
+ """Markdown 与 Content List 共用的图片资源解析。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from urllib.parse import quote
7
+
8
+ from ....schema import ImagePayloadBlock
9
+
10
+ _HTML_IMAGE_SRC_RE = re.compile(
11
+ r"(?P<prefix>\bsrc\s*=\s*)(?P<quote>[\"'])(?P<src>.*?)(?P=quote)",
12
+ re.IGNORECASE | re.DOTALL,
13
+ )
14
+
15
+
16
+ def resolve_image_source(block: ImagePayloadBlock, asset_base_url: str = "") -> str | None:
17
+ """按 sidecar、data URI、远程 URL 的稳定优先级解析图片来源。"""
18
+ if block.image_path:
19
+ return join_asset_base_url(asset_base_url, block.image_path)
20
+ if block.image_base64:
21
+ return block.image_base64
22
+ if block.image_url:
23
+ return block.image_url
24
+ return None
25
+
26
+
27
+ def join_asset_base_url(asset_base_url: str, relative_path: str) -> str:
28
+ """使用 POSIX 语义拼接资源根地址与安全相对路径。"""
29
+ if not asset_base_url:
30
+ return relative_path
31
+ return f"{asset_base_url.rstrip('/')}/{relative_path.lstrip('/')}"
32
+
33
+
34
+ def build_markdown_image(source: str, alt: str = "") -> str:
35
+ """构造不会被空格或括号截断的 Markdown 图片语法。"""
36
+ if not source:
37
+ return ""
38
+ safe_alt = alt.replace("[", r"\[").replace("]", r"\]")
39
+ destination = normalize_image_source(source)
40
+ return f"![{safe_alt}]({destination})"
41
+
42
+
43
+ def normalize_image_source(source: str) -> str:
44
+ """把图片来源规范化为 Markdown 与 structured_content 共用的安全地址。"""
45
+ if source.startswith("data:"):
46
+ return source
47
+ return quote(source, safe="/:#?&=%@+~,;!$'*-._")
48
+
49
+
50
+ def prefix_html_image_sources(markup: str, asset_base_url: str = "") -> str:
51
+ """给 HTML 中的相对图片地址添加资源根地址。"""
52
+ if not markup or not asset_base_url:
53
+ return markup
54
+
55
+ def _replace(match: re.Match[str]) -> str:
56
+ """只重写相对 src,保留 data URI、绝对 URL 与根路径。"""
57
+ source = match.group("src")
58
+ if _is_absolute_image_source(source):
59
+ return match.group(0)
60
+ resolved = join_asset_base_url(asset_base_url, source)
61
+ return f"{match.group('prefix')}{match.group('quote')}{resolved}{match.group('quote')}"
62
+
63
+ return _HTML_IMAGE_SRC_RE.sub(_replace, markup)
64
+
65
+
66
+ def _is_absolute_image_source(source: str) -> bool:
67
+ """判断 HTML 图片地址是否已经是绝对或不可前缀化的来源。"""
68
+ normalized = source.strip().lower()
69
+ return normalized.startswith(("data:", "http://", "https://", "//", "/", "#"))
70
+
71
+
72
+ __all__ = [
73
+ "build_markdown_image",
74
+ "join_asset_base_url",
75
+ "normalize_image_source",
76
+ "prefix_html_image_sources",
77
+ "resolve_image_source",
78
+ ]