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,414 @@
1
+ """Native PDF 表格字符选择、视觉组行和单元格文本重建。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import statistics
6
+ import unicodedata
7
+ from dataclasses import dataclass
8
+
9
+ from .....document.pdf.text.contracts import Char
10
+
11
+ from .....schema import BBox
12
+ from .....foundation.text import resolve_text_line_boundary
13
+ from ..spatial_text import _normalize_table_text
14
+
15
+ from .contracts import NativeTableGlyph, NativeTableInput, NativeTableText, NativeTableTextRow, NativeTableToken
16
+ from .geometry import bbox_center, bbox_intersection, bbox_union, normalize_angle, normalize_bbox, page_bbox_to_table_local
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class _PendingGlyph:
21
+ """保存尚未分配视觉行的局部 PDF 字符。"""
22
+
23
+ glyph_id: int
24
+ source_index: int
25
+ text: str
26
+ bbox: BBox
27
+ explicit_space_before: bool = False
28
+ explicit_break_before: bool = False
29
+
30
+
31
+ def _select_pending_glyphs(table_input: NativeTableInput) -> list[_PendingGlyph]:
32
+ """按字符中心选择表格内可见字符,并转换到正向局部坐标。"""
33
+
34
+ table_bbox = normalize_bbox(table_input.table_bbox)
35
+ if table_bbox is None:
36
+ return []
37
+ selected: list[tuple[int, int, Char]] = []
38
+ for fallback_index, char in enumerate(table_input.chars):
39
+ char_bbox = normalize_bbox(char.get("bbox"))
40
+ if char_bbox is None:
41
+ continue
42
+ center_x, center_y = bbox_center(char_bbox)
43
+ if not (table_bbox[0] <= center_x <= table_bbox[2] and table_bbox[1] <= center_y <= table_bbox[3]):
44
+ continue
45
+ try:
46
+ source_index = int(char.get("char_idx", fallback_index))
47
+ except (TypeError, ValueError):
48
+ source_index = fallback_index
49
+ selected.append((source_index, fallback_index, char))
50
+
51
+ selected.sort(key=lambda item: (item[0], item[1]))
52
+ output: list[_PendingGlyph] = []
53
+ angle = normalize_angle(table_input.angle)
54
+ pending_space = False
55
+ pending_break = False
56
+ for _source_sort, fallback_index, char in selected:
57
+ raw_text = str(char.get("char") or "")
58
+ if not raw_text:
59
+ continue
60
+ if raw_text in {"\r", "\n"}:
61
+ pending_break = True
62
+ pending_space = False
63
+ continue
64
+ if raw_text.isspace():
65
+ if not pending_break:
66
+ pending_space = True
67
+ continue
68
+ text = _normalize_table_text(raw_text)
69
+ if not text or text.isspace():
70
+ continue
71
+ absolute_bbox = normalize_bbox(char.get("bbox"))
72
+ if absolute_bbox is None:
73
+ continue
74
+ local_bbox = page_bbox_to_table_local(
75
+ absolute_bbox,
76
+ table_bbox,
77
+ angle,
78
+ )
79
+ if local_bbox is None or local_bbox[3] - local_bbox[1] < 0.5:
80
+ continue
81
+ try:
82
+ source_index = int(char.get("char_idx", fallback_index))
83
+ except (TypeError, ValueError):
84
+ source_index = fallback_index
85
+ output.append(
86
+ _PendingGlyph(
87
+ glyph_id=len(output),
88
+ source_index=source_index,
89
+ text=text,
90
+ bbox=local_bbox,
91
+ explicit_space_before=pending_space,
92
+ explicit_break_before=pending_break,
93
+ )
94
+ )
95
+ pending_space = False
96
+ pending_break = False
97
+ return output
98
+
99
+
100
+ def _vertical_overlap_ratio(first: BBox, second: BBox) -> float:
101
+ """返回两个字符框相对较小高度的垂直交叠比例。"""
102
+
103
+ overlap = min(first[3], second[3]) - max(first[1], second[1])
104
+ minimum_height = min(first[3] - first[1], second[3] - second[1])
105
+ if overlap <= 0 or minimum_height <= 0:
106
+ return 0.0
107
+ return min(1.0, overlap / minimum_height)
108
+
109
+
110
+ def _assign_visual_rows(
111
+ pending: list[_PendingGlyph],
112
+ median_height: float,
113
+ ) -> list[list[_PendingGlyph]]:
114
+ """按垂直交叠和中心距离把字符聚成稳定视觉行。"""
115
+
116
+ sorted_glyphs = sorted(
117
+ pending,
118
+ key=lambda glyph: (
119
+ (glyph.bbox[1] + glyph.bbox[3]) / 2.0,
120
+ glyph.bbox[0],
121
+ glyph.glyph_id,
122
+ ),
123
+ )
124
+ rows: list[list[_PendingGlyph]] = []
125
+ row_bboxes: list[BBox] = []
126
+ prefix_max_bottoms: list[float] = []
127
+ center_tolerance = max(0.75, median_height * 0.40)
128
+ for glyph in sorted_glyphs:
129
+ glyph_center_y = (glyph.bbox[1] + glyph.bbox[3]) / 2.0
130
+ best_index: int | None = None
131
+ best_distance = float("inf")
132
+ for row_index in range(len(row_bboxes) - 1, -1, -1):
133
+ row_bbox = row_bboxes[row_index]
134
+ row_center_y = (row_bbox[1] + row_bbox[3]) / 2.0
135
+ distance = abs(glyph_center_y - row_center_y)
136
+ if (
137
+ _vertical_overlap_ratio(glyph.bbox, row_bbox) >= 0.45 or distance <= center_tolerance
138
+ ) and distance < best_distance:
139
+ best_index = row_index
140
+ best_distance = distance
141
+ if (
142
+ glyph_center_y >= row_center_y
143
+ and glyph_center_y - row_center_y > center_tolerance
144
+ and glyph.bbox[1] > prefix_max_bottoms[row_index]
145
+ ):
146
+ break
147
+ if best_index is None:
148
+ rows.append([glyph])
149
+ row_bboxes.append(glyph.bbox)
150
+ prefix_max_bottoms.append(
151
+ max(
152
+ prefix_max_bottoms[-1] if prefix_max_bottoms else glyph.bbox[3],
153
+ glyph.bbox[3],
154
+ )
155
+ )
156
+ else:
157
+ rows[best_index].append(glyph)
158
+ row_bboxes[best_index] = bbox_union([row_bboxes[best_index], glyph.bbox])
159
+ for row_index in range(best_index, len(row_bboxes)):
160
+ prefix_max_bottoms[row_index] = max(
161
+ prefix_max_bottoms[row_index - 1] if row_index > 0 else row_bboxes[row_index][3],
162
+ row_bboxes[row_index][3],
163
+ )
164
+
165
+ ordered = sorted(
166
+ zip(rows, row_bboxes),
167
+ key=lambda item: (item[1][1], item[1][0]),
168
+ )
169
+ return [sorted(row, key=lambda glyph: (glyph.bbox[0], glyph.bbox[1], glyph.glyph_id)) for row, _bbox in ordered]
170
+
171
+
172
+ def _contains_cjk(text: str) -> bool:
173
+ """判断文本是否包含常见中日韩统一表意字符。"""
174
+
175
+ return any("\u3400" <= char <= "\u9fff" for char in text)
176
+
177
+
178
+ def _join_glyph_line(
179
+ glyphs: list[NativeTableGlyph] | list[_PendingGlyph],
180
+ median_height: float,
181
+ ) -> str:
182
+ """按字符间距重建单行文本,避免在中文字符之间强插空格。"""
183
+
184
+ return "".join(text for text, _source_index in _glyph_line_parts(glyphs, median_height))
185
+
186
+
187
+ def _nearest_alphabetic_char(text: str, *, reverse: bool) -> str:
188
+ """从物理行边界向内查找最近的 Unicode 字母。"""
189
+
190
+ normalized = text.rstrip() if reverse else text.lstrip()
191
+ characters = reversed(normalized) if reverse else iter(normalized)
192
+ return next((char for char in characters if char.isalpha()), "")
193
+
194
+
195
+ def _is_latin_letter(char: str) -> bool:
196
+ """判断单个 Unicode 字符规范化后是否只由 Latin 字母组成。"""
197
+
198
+ if len(char) != 1:
199
+ return False
200
+ normalized = unicodedata.normalize("NFKC", char)
201
+ return bool(normalized) and all(item.isalpha() and unicodedata.name(item, "").startswith("LATIN ") for item in normalized)
202
+
203
+
204
+ def _looks_like_compact_unit_or_identifier(
205
+ previous_line: str,
206
+ next_line: str,
207
+ ) -> bool:
208
+ """识别不应因视觉换行插入空格的紧凑单位或标识符片段。"""
209
+
210
+ combined = previous_line.strip() + next_line.strip()
211
+ if not combined or any(char.isspace() for char in combined):
212
+ return False
213
+ return any(char.isdigit() or char in "/_^%°µμ" for char in combined)
214
+
215
+
216
+ def _starts_with_url(text: str) -> bool:
217
+ """判断下一物理行是否自身以完整 URL 前缀开始。"""
218
+
219
+ normalized = text.lstrip().casefold()
220
+ return normalized.startswith(("http://", "https://", "ftp://", "www."))
221
+
222
+
223
+ def _cell_row_separator(
224
+ accumulated_content: str,
225
+ previous_line: str,
226
+ next_line: str,
227
+ ) -> str:
228
+ """按相邻行边界词元返回安全分隔符,非 Latin 边界保持直连。"""
229
+
230
+ previous_letter = _nearest_alphabetic_char(previous_line, reverse=True)
231
+ next_letter = _nearest_alphabetic_char(next_line, reverse=False)
232
+ if not (_is_latin_letter(previous_letter) and _is_latin_letter(next_letter)):
233
+ return ""
234
+
235
+ _processed_previous, separator = resolve_text_line_boundary(
236
+ accumulated_content,
237
+ block_language="en",
238
+ next_content=next_line,
239
+ )
240
+ if separator != " ":
241
+ # 表格 HTML 保留原始连字符,只复用正文规则的 URL/连字符直连判定。
242
+ return separator
243
+ if not _starts_with_url(next_line) and _looks_like_compact_unit_or_identifier(
244
+ previous_line,
245
+ next_line,
246
+ ):
247
+ return ""
248
+ return separator
249
+
250
+
251
+ def _glyph_line_parts(
252
+ glyphs: list[NativeTableGlyph] | list[_PendingGlyph],
253
+ median_height: float,
254
+ ) -> list[tuple[str, int | None]]:
255
+ """重建单行文本片段,并保留每个可见片段的原字符索引。"""
256
+
257
+ if not glyphs:
258
+ return []
259
+ ordered = sorted(glyphs, key=lambda glyph: (glyph.bbox[0], glyph.bbox[1], glyph.glyph_id))
260
+ parts: list[tuple[str, int | None]] = [(ordered[0].text, ordered[0].source_index)]
261
+ previous = ordered[0]
262
+ for glyph in ordered[1:]:
263
+ gap = glyph.bbox[0] - previous.bbox[2]
264
+ previous_tail = previous.text[-1:] or ""
265
+ current_head = glyph.text[:1] or ""
266
+ needs_space = glyph.explicit_space_before or (
267
+ not glyph.explicit_break_before
268
+ and gap > max(0.6, median_height * 0.20)
269
+ and not _contains_cjk(previous_tail + current_head)
270
+ and previous_tail.isprintable()
271
+ and current_head.isprintable()
272
+ )
273
+ if needs_space and not parts[-1][0].endswith(" "):
274
+ parts.append((" ", None))
275
+ parts.append((glyph.text, glyph.source_index))
276
+ previous = glyph
277
+ return parts
278
+
279
+
280
+ def _tokenize_row(
281
+ glyphs: list[NativeTableGlyph],
282
+ median_width: float,
283
+ median_height: float,
284
+ ) -> tuple[NativeTableToken, ...]:
285
+ """按显著水平间隙把视觉行拆成供列推断使用的文本项。"""
286
+
287
+ if not glyphs:
288
+ return ()
289
+ split_gap = max(2.0 * median_width, 0.70 * median_height, 2.0)
290
+ groups: list[list[NativeTableGlyph]] = [[glyphs[0]]]
291
+ for glyph in glyphs[1:]:
292
+ if glyph.bbox[0] - groups[-1][-1].bbox[2] > split_gap:
293
+ groups.append([glyph])
294
+ else:
295
+ groups[-1].append(glyph)
296
+ tokens: list[NativeTableToken] = []
297
+ for group in groups:
298
+ content = _join_glyph_line(group, median_height)
299
+ if not content:
300
+ continue
301
+ tokens.append(
302
+ NativeTableToken(
303
+ text=content,
304
+ bbox=bbox_union(item.bbox for item in group),
305
+ glyph_ids=tuple(item.glyph_id for item in group),
306
+ source_char_indices=tuple(item.source_index for item in group),
307
+ )
308
+ )
309
+ return tuple(tokens)
310
+
311
+
312
+ def build_native_table_text(table_input: NativeTableInput) -> NativeTableText | None:
313
+ """把原生 PDF 字符转换为正向表格字形、视觉行和文本项。"""
314
+
315
+ pending = _select_pending_glyphs(table_input)
316
+ if not pending:
317
+ return None
318
+ widths = [glyph.bbox[2] - glyph.bbox[0] for glyph in pending if glyph.bbox[2] > glyph.bbox[0]]
319
+ heights = [glyph.bbox[3] - glyph.bbox[1] for glyph in pending if glyph.bbox[3] > glyph.bbox[1]]
320
+ median_width = max(0.1, float(statistics.median(widths)) if widths else 1.0)
321
+ median_height = max(0.1, float(statistics.median(heights)) if heights else 1.0)
322
+ pending_rows = _assign_visual_rows(pending, median_height)
323
+
324
+ glyphs: list[NativeTableGlyph] = []
325
+ rows: list[NativeTableTextRow] = []
326
+ for row_index, pending_row in enumerate(pending_rows):
327
+ row_glyphs = [
328
+ NativeTableGlyph(
329
+ glyph_id=item.glyph_id,
330
+ source_index=item.source_index,
331
+ text=item.text,
332
+ bbox=item.bbox,
333
+ visual_row=row_index,
334
+ explicit_space_before=item.explicit_space_before,
335
+ explicit_break_before=item.explicit_break_before,
336
+ )
337
+ for item in pending_row
338
+ ]
339
+ glyphs.extend(row_glyphs)
340
+ rows.append(
341
+ NativeTableTextRow(
342
+ row_index=row_index,
343
+ bbox=bbox_union(item.bbox for item in row_glyphs),
344
+ tokens=_tokenize_row(row_glyphs, median_width, median_height),
345
+ glyph_ids=tuple(item.glyph_id for item in row_glyphs),
346
+ )
347
+ )
348
+ glyphs.sort(key=lambda item: item.glyph_id)
349
+ return NativeTableText(
350
+ glyphs=tuple(glyphs),
351
+ rows=tuple(rows),
352
+ median_glyph_width=median_width,
353
+ median_glyph_height=median_height,
354
+ )
355
+
356
+
357
+ def build_cell_text(
358
+ glyphs: list[NativeTableGlyph],
359
+ median_height: float,
360
+ ) -> str:
361
+ """按视觉行和局部横向顺序重建文本,并将同一单元格内的多行直接拼接。"""
362
+
363
+ return "".join(text for text, _source_index in build_cell_text_parts(glyphs, median_height))
364
+
365
+
366
+ def build_cell_text_parts(
367
+ glyphs: list[NativeTableGlyph],
368
+ median_height: float,
369
+ ) -> list[tuple[str, int | None]]:
370
+ """按视觉行重建 cell 文本片段,供安全插入字符级语义标签。"""
371
+
372
+ if not glyphs:
373
+ return []
374
+ grouped: dict[int, list[NativeTableGlyph]] = {}
375
+ for glyph in glyphs:
376
+ grouped.setdefault(glyph.visual_row, []).append(glyph)
377
+ parts: list[tuple[str, int | None]] = []
378
+ previous_line = ""
379
+ for row_index in sorted(grouped):
380
+ row_parts = _glyph_line_parts(grouped[row_index], median_height)
381
+ row_text = "".join(text for text, _source_index in row_parts)
382
+ if not row_text:
383
+ continue
384
+ if parts:
385
+ accumulated_content = "".join(text for text, _source_index in parts)
386
+ separator = _cell_row_separator(
387
+ accumulated_content,
388
+ previous_line,
389
+ row_text,
390
+ )
391
+ if separator and not accumulated_content.endswith(" ") and not row_text.startswith(" "):
392
+ parts.append((separator, None))
393
+ parts.extend(row_parts)
394
+ previous_line = row_text
395
+ return parts
396
+
397
+
398
+ def glyph_overlap_ratio(glyph: NativeTableGlyph, bbox: BBox) -> float:
399
+ """返回字符框面积被目标单元格覆盖的比例。"""
400
+
401
+ intersection = bbox_intersection(glyph.bbox, bbox)
402
+ glyph_area = (glyph.bbox[2] - glyph.bbox[0]) * (glyph.bbox[3] - glyph.bbox[1])
403
+ if intersection is None or glyph_area <= 0:
404
+ return 0.0
405
+ intersection_area = (intersection[2] - intersection[0]) * (intersection[3] - intersection[1])
406
+ return min(1.0, intersection_area / glyph_area)
407
+
408
+
409
+ __all__ = [
410
+ "build_cell_text",
411
+ "build_cell_text_parts",
412
+ "build_native_table_text",
413
+ "glyph_overlap_ratio",
414
+ ]