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,394 @@
1
+ """按来源行和标记组装页面脚注。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import statistics
6
+ from typing import Any, Sequence
7
+
8
+ from .....schema import BBox
9
+ from ..geometry import _bbox_center_y, _bbox_union_many, _rotate_bbox_from_upright, _rotate_bbox_to_upright
10
+ from ..line_layout import _effective_text_row_gap, _line_effective_height, _lines_tight_output_bbox, _title_fonts_compatible
11
+ from ..models import _LineItem
12
+ from .common import _merge_text_line_content
13
+
14
+
15
+ def _build_grouped_page_footnote_blocks(
16
+ lines: list[_LineItem],
17
+ source_groups: Sequence[set[int]],
18
+ page_size: tuple[float, float],
19
+ ) -> tuple[list[dict[str, Any]], set[int]]:
20
+ """按分隔线组切分几何脚注条目,并返回已消费的来源编号。"""
21
+
22
+ footnote_by_source = {line.source_index: line for line in lines if line.semantic_type == "page_footnote"}
23
+ blocks: list[dict[str, Any]] = []
24
+ consumed_source_indices: set[int] = set()
25
+ for source_group in source_groups:
26
+ group_lines = [footnote_by_source[source_index] for source_index in source_group if source_index in footnote_by_source]
27
+ for angle in sorted({line.angle for line in group_lines}):
28
+ directional_lines = [line for line in group_lines if line.angle == angle]
29
+ tight_bboxes = _tight_page_footnote_bboxes(
30
+ directional_lines,
31
+ page_size,
32
+ )
33
+ for entry_lines in _split_page_footnote_entries(
34
+ directional_lines,
35
+ page_size,
36
+ ):
37
+ ordered_lines = sorted(
38
+ entry_lines,
39
+ key=lambda line: (
40
+ _rotate_bbox_to_upright(line.bbox, page_size, angle)[1],
41
+ _rotate_bbox_to_upright(line.bbox, page_size, angle)[0],
42
+ line.source_index,
43
+ ),
44
+ )
45
+ content = _merge_text_line_content([line.text for line in ordered_lines])
46
+ if not content:
47
+ continue
48
+ visual_row_ids = {line.visual_row_id for line in ordered_lines if line.visual_row_id is not None}
49
+ single_run_row_id = (
50
+ ordered_lines[0].visual_row_id
51
+ if len(ordered_lines) == 1
52
+ and ordered_lines[0].split_from_row
53
+ and ordered_lines[0].visual_row_id is not None
54
+ else None
55
+ )
56
+ block = {
57
+ "type": "page_footnote",
58
+ "bbox": _bbox_union_many([tight_bboxes[line.source_index] for line in ordered_lines]),
59
+ "angle": angle,
60
+ "content": content,
61
+ "_visual_row_ids": visual_row_ids,
62
+ "_single_run_row_id": single_run_row_id,
63
+ "_inline_math_regions": [region for line in ordered_lines for region in line.inline_math_regions],
64
+ }
65
+ tight_output_bbox = _lines_tight_output_bbox(
66
+ ordered_lines,
67
+ page_size,
68
+ )
69
+ if tight_output_bbox is not None:
70
+ block["_tight_output_bbox"] = tight_output_bbox
71
+ blocks.append(block)
72
+ consumed_source_indices.update(line.source_index for line in ordered_lines)
73
+ return blocks, consumed_source_indices
74
+
75
+
76
+ def _split_page_footnote_entries(
77
+ lines: list[_LineItem],
78
+ page_size: tuple[float, float],
79
+ ) -> list[list[_LineItem]]:
80
+ """按首行或续行缩进模式,把同一分隔线下的脚注行切成条目。"""
81
+
82
+ if not lines:
83
+ return []
84
+ angle = lines[0].angle
85
+ line_geometry = [(line, _rotate_bbox_to_upright(line.bbox, page_size, angle)) for line in lines]
86
+ line_geometry.sort(key=lambda item: (item[1][1], item[1][0], item[0].source_index))
87
+ if len(line_geometry) == 1:
88
+ return [[line_geometry[0][0]]]
89
+
90
+ effective_heights = [_line_effective_height(line, bbox) for line, bbox in line_geometry]
91
+ median_height = statistics.median(effective_heights)
92
+ glyph_widths = [line.median_glyph_width for line, _bbox in line_geometry if line.median_glyph_width is not None]
93
+ median_glyph_width = statistics.median(glyph_widths) if glyph_widths else 0.5 * median_height
94
+ indent_threshold = max(1.5 * median_height, 2.0 * median_glyph_width)
95
+ base_left = min(bbox[0] for _line, bbox in line_geometry)
96
+ maximum_row_width = max(bbox[2] - bbox[0] for _line, bbox in line_geometry)
97
+ marker_rows = _find_page_footnote_marker_rows(
98
+ line_geometry,
99
+ median_height,
100
+ median_glyph_width,
101
+ base_left,
102
+ )
103
+ if marker_rows:
104
+ return _split_marked_page_footnote_entries(
105
+ line_geometry,
106
+ marker_rows,
107
+ median_height,
108
+ median_glyph_width,
109
+ base_left,
110
+ maximum_row_width,
111
+ indent_threshold,
112
+ )
113
+ return _split_unmarked_page_footnote_entries(
114
+ line_geometry,
115
+ base_left,
116
+ maximum_row_width,
117
+ indent_threshold,
118
+ )
119
+
120
+
121
+ def _find_page_footnote_marker_rows(
122
+ line_geometry: list[tuple[_LineItem, BBox]],
123
+ median_height: float,
124
+ median_glyph_width: float,
125
+ base_left: float,
126
+ ) -> list[tuple[tuple[_LineItem, BBox], tuple[_LineItem, BBox]]]:
127
+ """用同视觉行的窄左片段和右侧正文识别脚注编号锚点。"""
128
+
129
+ by_visual_row: dict[int, list[tuple[_LineItem, BBox]]] = {}
130
+ for item in line_geometry:
131
+ line = item[0]
132
+ if line.visual_row_id is None or not line.split_from_row:
133
+ continue
134
+ by_visual_row.setdefault(line.visual_row_id, []).append(item)
135
+
136
+ marker_rows: list[tuple[tuple[_LineItem, BBox], tuple[_LineItem, BBox]]] = []
137
+ marker_width_limit = max(1.5 * median_glyph_width, 0.75 * median_height)
138
+ for members in by_visual_row.values():
139
+ ordered = sorted(members, key=lambda item: item[1][0])
140
+ if len(ordered) < 2:
141
+ continue
142
+ marker, body = ordered[0], ordered[1]
143
+ marker_width = marker[1][2] - marker[1][0]
144
+ body_width = body[1][2] - body[1][0]
145
+ horizontal_gap = body[1][0] - marker[1][2]
146
+ if (
147
+ marker_width > marker_width_limit
148
+ or marker[1][0] - base_left > 0.5 * median_height
149
+ or not 0.0 <= horizontal_gap <= 1.5 * median_height
150
+ or body[1][0] - base_left < max(1.5 * marker_width, 0.75 * median_height)
151
+ or body_width < max(4.0 * marker_width, 4.0 * median_glyph_width)
152
+ or abs(_bbox_center_y(marker[1]) - _bbox_center_y(body[1])) > 0.4 * median_height
153
+ ):
154
+ continue
155
+ marker_rows.append((marker, body))
156
+ marker_rows.sort(key=lambda pair: (min(pair[0][1][1], pair[1][1][1]), pair[0][1][0]))
157
+ if not marker_rows:
158
+ marker_rows = _find_geometric_page_footnote_marker_rows(
159
+ line_geometry,
160
+ median_height,
161
+ median_glyph_width,
162
+ base_left,
163
+ )
164
+ return marker_rows
165
+
166
+
167
+ def _find_geometric_page_footnote_marker_rows(
168
+ line_geometry: list[tuple[_LineItem, BBox]],
169
+ median_height: float,
170
+ median_glyph_width: float,
171
+ base_left: float,
172
+ ) -> list[tuple[tuple[_LineItem, BBox], tuple[_LineItem, BBox]]]:
173
+ """在 row id 缺失时用同基线窄标记和右侧正文恢复脚注首行。"""
174
+
175
+ marker_width_limit = max(
176
+ 1.5 * median_glyph_width,
177
+ 0.75 * median_height,
178
+ )
179
+ candidates: list[
180
+ tuple[
181
+ float,
182
+ tuple[_LineItem, BBox],
183
+ tuple[_LineItem, BBox],
184
+ ]
185
+ ] = []
186
+ for marker in line_geometry:
187
+ marker_width = marker[1][2] - marker[1][0]
188
+ if marker_width > marker_width_limit or marker[1][0] - base_left > 0.5 * median_height:
189
+ continue
190
+ for body in line_geometry:
191
+ if body is marker:
192
+ continue
193
+ body_width = body[1][2] - body[1][0]
194
+ horizontal_gap = body[1][0] - marker[1][2]
195
+ if (
196
+ not 0.0 <= horizontal_gap <= 1.5 * median_height
197
+ or body[1][0] - base_left
198
+ < max(
199
+ 1.5 * marker_width,
200
+ 0.4 * median_height,
201
+ )
202
+ or body_width
203
+ < max(
204
+ 4.0 * marker_width,
205
+ 4.0 * median_glyph_width,
206
+ )
207
+ or abs(_bbox_center_y(marker[1]) - _bbox_center_y(body[1])) > 0.4 * median_height
208
+ or not _title_fonts_compatible(
209
+ marker[0],
210
+ body[0],
211
+ )
212
+ ):
213
+ continue
214
+ candidates.append(
215
+ (
216
+ horizontal_gap,
217
+ marker,
218
+ body,
219
+ )
220
+ )
221
+
222
+ output = []
223
+ consumed_sources: set[int] = set()
224
+ for _gap, marker, body in sorted(
225
+ candidates,
226
+ key=lambda item: (
227
+ min(item[1][1][1], item[2][1][1]),
228
+ item[0],
229
+ item[1][0].source_index,
230
+ item[2][0].source_index,
231
+ ),
232
+ ):
233
+ pair_sources = {
234
+ marker[0].source_index,
235
+ body[0].source_index,
236
+ }
237
+ if pair_sources & consumed_sources:
238
+ continue
239
+ output.append((marker, body))
240
+ consumed_sources.update(pair_sources)
241
+ return output
242
+
243
+
244
+ def _split_marked_page_footnote_entries(
245
+ line_geometry: list[tuple[_LineItem, BBox]],
246
+ marker_rows: list[tuple[tuple[_LineItem, BBox], tuple[_LineItem, BBox]]],
247
+ median_height: float,
248
+ median_glyph_width: float,
249
+ base_left: float,
250
+ maximum_row_width: float,
251
+ indent_threshold: float,
252
+ ) -> list[list[_LineItem]]:
253
+ """把编号、右侧首行和对齐续行聚合,并保留编号区之前的独立脚注。"""
254
+
255
+ first_marker_top = min(marker_rows[0][0][1][1], marker_rows[0][1][1][1])
256
+ prefix_geometry = [item for item in line_geometry if item[1][1] < first_marker_top]
257
+ entries = _split_unmarked_page_footnote_entries(
258
+ prefix_geometry,
259
+ base_left,
260
+ maximum_row_width,
261
+ indent_threshold,
262
+ )
263
+ consumed = {line.source_index for entry in entries for line in entry}
264
+ continuation_tolerance = max(0.75 * median_height, 2.0 * median_glyph_width)
265
+ for marker_index, (marker, body) in enumerate(marker_rows):
266
+ next_marker_top = (
267
+ min(marker_rows[marker_index + 1][0][1][1], marker_rows[marker_index + 1][1][1][1])
268
+ if marker_index + 1 < len(marker_rows)
269
+ else float("inf")
270
+ )
271
+ entry_items = [marker, body]
272
+ consumed.update((marker[0].source_index, body[0].source_index))
273
+ previous = body
274
+ for candidate in line_geometry:
275
+ if candidate[0].source_index in consumed:
276
+ continue
277
+ if candidate[1][1] < body[1][1] or candidate[1][1] >= next_marker_top:
278
+ continue
279
+ if abs(candidate[1][0] - body[1][0]) > continuation_tolerance:
280
+ continue
281
+ if _effective_text_row_gap(previous, candidate) > 1.5 * median_height:
282
+ continue
283
+ entry_items.append(candidate)
284
+ consumed.add(candidate[0].source_index)
285
+ previous = candidate
286
+ entry_items.sort(key=lambda item: (item[1][1], item[1][0], item[0].source_index))
287
+ entries.append([line for line, _bbox in entry_items])
288
+
289
+ leftovers = [item for item in line_geometry if item[0].source_index not in consumed]
290
+ entries.extend(
291
+ _split_unmarked_page_footnote_entries(
292
+ leftovers,
293
+ base_left,
294
+ maximum_row_width,
295
+ indent_threshold,
296
+ )
297
+ )
298
+ entries.sort(
299
+ key=lambda entry: min(
300
+ bbox[1] for line in entry for candidate, bbox in line_geometry if candidate.source_index == line.source_index
301
+ )
302
+ )
303
+ return entries
304
+
305
+
306
+ def _split_unmarked_page_footnote_entries(
307
+ line_geometry: list[tuple[_LineItem, BBox]],
308
+ base_left: float,
309
+ maximum_row_width: float,
310
+ indent_threshold: float,
311
+ ) -> list[list[_LineItem]]:
312
+ """沿用首行和续行缩进模式切分没有稳定编号锚点的脚注行。"""
313
+
314
+ if not line_geometry:
315
+ return []
316
+ first_line_is_indented = line_geometry[0][1][0] - base_left > indent_threshold
317
+
318
+ entries: list[list[_LineItem]] = [[line_geometry[0][0]]]
319
+ for previous, current in zip(line_geometry, line_geometry[1:]):
320
+ previous_width = previous[1][2] - previous[1][0]
321
+ previous_is_near_full = previous_width >= 0.8 * maximum_row_width
322
+ current_is_indented = current[1][0] - base_left > indent_threshold
323
+ same_left_compact_continuation = (
324
+ not first_line_is_indented
325
+ and previous_width >= 0.7 * maximum_row_width
326
+ and abs(current[1][0] - previous[1][0]) <= 0.5 * indent_threshold
327
+ and _title_fonts_compatible(previous[0], current[0])
328
+ and _effective_text_row_gap(previous, current) <= indent_threshold
329
+ )
330
+ # 满行后必然续接;其余行按首页观测到的首行/续行缩进模式决定边界。
331
+ continues_previous = (
332
+ previous_is_near_full
333
+ or same_left_compact_continuation
334
+ or (not current_is_indented if first_line_is_indented else current_is_indented)
335
+ )
336
+ if continues_previous:
337
+ entries[-1].append(current[0])
338
+ else:
339
+ entries.append([current[0]])
340
+ return entries
341
+
342
+
343
+ def _tight_page_footnote_bboxes(
344
+ lines: list[_LineItem],
345
+ page_size: tuple[float, float],
346
+ ) -> dict[int, BBox]:
347
+ """以有效行高和相邻行距收紧脚注框,避免异常字体框跨入相邻条目。"""
348
+
349
+ if not lines:
350
+ return {}
351
+ angle = lines[0].angle
352
+ line_geometry = [(line, _rotate_bbox_to_upright(line.bbox, page_size, angle)) for line in lines]
353
+ line_geometry.sort(key=lambda item: (item[1][1], item[1][0], item[0].source_index))
354
+ median_height = statistics.median(_line_effective_height(line, bbox) for line, bbox in line_geometry)
355
+ row_pitches = [
356
+ current[1][1] - previous[1][1]
357
+ for previous, current in zip(line_geometry, line_geometry[1:])
358
+ if current[1][1] - previous[1][1] > 0.25 * median_height
359
+ ]
360
+ median_pitch = statistics.median(row_pitches) if row_pitches else None
361
+
362
+ output: dict[int, BBox] = {}
363
+ for line, bbox in line_geometry:
364
+ tight_height = min(
365
+ bbox[3] - bbox[1],
366
+ _line_effective_height(line, bbox),
367
+ )
368
+ if median_pitch is not None:
369
+ tight_height = min(tight_height, 0.9 * median_pitch)
370
+ tight_height = max(0.1, tight_height)
371
+ center_y = _bbox_center_y(bbox)
372
+ tight_local_bbox = (
373
+ bbox[0],
374
+ center_y - 0.5 * tight_height,
375
+ bbox[2],
376
+ center_y + 0.5 * tight_height,
377
+ )
378
+ output[line.source_index] = _rotate_bbox_from_upright(
379
+ tight_local_bbox,
380
+ page_size,
381
+ angle,
382
+ )
383
+ return output
384
+
385
+
386
+ __all__ = [
387
+ "_build_grouped_page_footnote_blocks",
388
+ "_split_page_footnote_entries",
389
+ "_find_page_footnote_marker_rows",
390
+ "_find_geometric_page_footnote_marker_rows",
391
+ "_split_marked_page_footnote_entries",
392
+ "_split_unmarked_page_footnote_entries",
393
+ "_tight_page_footnote_bboxes",
394
+ ]