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,293 @@
1
+ """把 OFD 页面场景投影为带 bbox 的有序 raw model-list。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import statistics
7
+ from dataclasses import replace
8
+ from typing import Any
9
+
10
+ from .._shared.xycut import sort_entries
11
+ from ....schema import BlockType
12
+ from .geometry import bbox_center, bbox_union, normalize_bbox
13
+ from .models import OfdPageScene, TextLine
14
+ from .table import OfdTableBudget, OfdTableRegion, recover_tables
15
+ from .text import format_line_spans
16
+
17
+ _PAGE_NUMBER_RE = re.compile(r"^(?:\d+|[IVXLCDM]+)$", re.IGNORECASE)
18
+ _ASCII_WORD_GAP_RATIO = 0.2
19
+
20
+
21
+ def _same_baseline(first: TextLine, second: TextLine) -> bool:
22
+ """判断两个文字片段是否可在同一视觉基线上拼接。"""
23
+ if first.angle != second.angle or first.styles != second.styles:
24
+ return False
25
+ if (
26
+ min(first.font_size, second.font_size) <= 0
27
+ or max(first.font_size, second.font_size) / min(first.font_size, second.font_size) > 1.35
28
+ ):
29
+ return False
30
+ first_height = first.bbox[3] - first.bbox[1]
31
+ second_height = second.bbox[3] - second.bbox[1]
32
+ tolerance = 0.4 * max(first_height, second_height, 0.5)
33
+ if second.bbox[0] + tolerance < first.bbox[0]:
34
+ return False
35
+ center_delta = abs(bbox_center(first.bbox)[1] - bbox_center(second.bbox)[1])
36
+ gap = max(0.0, second.bbox[0] - first.bbox[2])
37
+ return center_delta <= tolerance and gap <= 2.0 * max(first_height, second_height, 0.5)
38
+
39
+
40
+ def _join_text(first: TextLine, second: TextLine) -> str:
41
+ """按语言字符边界和实际字形间距决定同行片段间是否补空格。"""
42
+ if not first.text or not second.text:
43
+ return first.text + second.text
44
+ ascii_word_boundary = (
45
+ first.text[-1].isascii() and second.text[0].isascii() and first.text[-1].isalnum() and second.text[0].isalnum()
46
+ )
47
+ if not ascii_word_boundary:
48
+ return first.text + second.text
49
+ first_glyph = first.glyphs[-1] if first.glyphs else None
50
+ second_glyph = second.glyphs[0] if second.glyphs else None
51
+ first_right = first_glyph.bbox[2] if first_glyph is not None else first.bbox[2]
52
+ second_left = second_glyph.bbox[0] if second_glyph is not None else second.bbox[0]
53
+ gap = second_left - first_right
54
+ glyph_widths = [
55
+ glyph.bbox[2] - glyph.bbox[0]
56
+ for glyph in (first_glyph, second_glyph)
57
+ if glyph is not None and glyph.bbox[2] > glyph.bbox[0]
58
+ ]
59
+ reference_width = statistics.median(glyph_widths) if glyph_widths else 0.0
60
+ spacing_threshold = _ASCII_WORD_GAP_RATIO * max(min(first.font_size, second.font_size), reference_width, 0.5)
61
+ needs_space = gap >= spacing_threshold
62
+ return f"{first.text}{' ' if needs_space else ''}{second.text}"
63
+
64
+
65
+ def merge_same_baseline_lines(lines: list[TextLine]) -> list[TextLine]:
66
+ """保守合并相邻 TextObject 形成的同基线文字片段。"""
67
+
68
+ def sort_key(item: TextLine) -> tuple[int, int, float, float, int]:
69
+ """按自适应基线带和横向位置排列候选片段。"""
70
+ height = max(item.bbox[3] - item.bbox[1], 0.5)
71
+ baseline_bucket = round(bbox_center(item.bbox)[1] / max(0.4 * height, 0.5))
72
+ return item.angle, baseline_bucket, item.bbox[0], item.bbox[1], item.paint_order
73
+
74
+ ordered = sorted(lines, key=sort_key)
75
+ output: list[TextLine] = []
76
+ for line in ordered:
77
+ if output and _same_baseline(output[-1], line):
78
+ previous = output[-1]
79
+ merged_bbox = bbox_union((previous.bbox, line.bbox))
80
+ if merged_bbox is not None:
81
+ output[-1] = replace(
82
+ previous,
83
+ text=_join_text(previous, line),
84
+ bbox=merged_bbox,
85
+ glyphs=previous.glyphs + line.glyphs,
86
+ paint_order=min(previous.paint_order, line.paint_order),
87
+ )
88
+ continue
89
+ output.append(line)
90
+ return output
91
+
92
+
93
+ def _normalized_signature(line: TextLine, scene: OfdPageScene) -> tuple[str, int, int]:
94
+ """构造跨页重复边缘文字的稳定签名。"""
95
+ width = scene.physical_box[2] - scene.physical_box[0]
96
+ height = scene.physical_box[3] - scene.physical_box[1]
97
+ center_x, center_y = bbox_center(line.bbox)
98
+ text = re.sub(r"\d+", "#", re.sub(r"\s+", "", line.text)).casefold()
99
+ return (
100
+ text,
101
+ round((center_x - scene.physical_box[0]) / max(width, 1.0) * 20),
102
+ round((center_y - scene.physical_box[1]) / max(height, 1.0) * 20),
103
+ )
104
+
105
+
106
+ def repeated_edge_signatures(scenes: list[OfdPageScene]) -> frozenset[tuple[str, int, int]]:
107
+ """识别至少跨两页重复出现的页边缘文字。"""
108
+ pages_by_signature: dict[tuple[str, int, int], set[int]] = {}
109
+ for scene in scenes:
110
+ height = scene.physical_box[3] - scene.physical_box[1]
111
+ for line in scene.text_lines:
112
+ center_y = bbox_center(line.bbox)[1]
113
+ relative_y = (center_y - scene.physical_box[1]) / max(height, 1.0)
114
+ if 0.18 < relative_y < 0.82:
115
+ continue
116
+ signature = _normalized_signature(line, scene)
117
+ if signature[0]:
118
+ pages_by_signature.setdefault(signature, set()).add(scene.page_idx)
119
+ return frozenset(signature for signature, pages in pages_by_signature.items() if len(pages) >= 2)
120
+
121
+
122
+ def _auxiliary_type(line: TextLine, scene: OfdPageScene, repeated: frozenset[tuple[str, int, int]]) -> BlockType | None:
123
+ """利用 ContentBox、页边缘和跨页重复性识别辅助文字。"""
124
+ physical = scene.physical_box
125
+ width = physical[2] - physical[0]
126
+ height = physical[3] - physical[1]
127
+ center_x, center_y = bbox_center(line.bbox)
128
+ relative_x = (center_x - physical[0]) / max(width, 1.0)
129
+ relative_y = (center_y - physical[1]) / max(height, 1.0)
130
+ normalized_text = line.text.strip()
131
+ if len(normalized_text) <= 6 and _PAGE_NUMBER_RE.fullmatch(normalized_text) and (relative_y <= 0.08 or relative_y >= 0.92):
132
+ return BlockType.PAGE_NUMBER
133
+ outside_content = False
134
+ if scene.content_box is not None:
135
+ content = scene.content_box
136
+ outside_content = not (content[0] <= center_x <= content[2] and content[1] <= center_y <= content[3])
137
+ is_repeated = _normalized_signature(line, scene) in repeated
138
+ if (outside_content and relative_y <= 0.2) or (is_repeated and relative_y <= 0.08):
139
+ return BlockType.HEADER
140
+ if (outside_content and relative_y >= 0.8) or (is_repeated and relative_y >= 0.92):
141
+ return BlockType.FOOTER
142
+ if outside_content and (relative_x <= 0.12 or relative_x >= 0.88):
143
+ return BlockType.ASIDE_TEXT
144
+ return None
145
+
146
+
147
+ def _upright_bbox(bbox: list[float], scene: OfdPageScene, angle: int) -> list[float]:
148
+ """把直角旋转对象的 bbox 转到共享 XYCut 使用的正向坐标。"""
149
+ physical = scene.physical_box
150
+ width = physical[2] - physical[0]
151
+ height = physical[3] - physical[1]
152
+ x0, y0, x1, y1 = (
153
+ bbox[0] - physical[0],
154
+ bbox[1] - physical[1],
155
+ bbox[2] - physical[0],
156
+ bbox[3] - physical[1],
157
+ )
158
+ if angle == 90:
159
+ return [y0, width - x1, y1, width - x0]
160
+ if angle == 180:
161
+ return [width - x1, height - y1, width - x0, height - y0]
162
+ if angle == 270:
163
+ return [height - y1, x0, height - y0, x1]
164
+ return [x0, y0, x1, y1]
165
+
166
+
167
+ class OfdReadingOrderProjector:
168
+ """执行表格认领、辅助类型判定和 OFD-aware XYCut++。"""
169
+
170
+ def __init__(self, scenes: list[OfdPageScene]) -> None:
171
+ """缓存跨页重复签名和正文字号基线。"""
172
+ self.scenes = scenes
173
+ self.repeated = repeated_edge_signatures(scenes)
174
+ body_sizes = [line.font_size for scene in scenes for line in scene.text_lines if line.font_size > 0]
175
+ self.body_size = statistics.median(body_sizes) if body_sizes else 1.0
176
+ self.document_title_emitted = False
177
+ self.table_budget = OfdTableBudget()
178
+
179
+ def _text_block(self, line: TextLine, scene: OfdPageScene) -> dict[str, Any]:
180
+ """把 TextLine 转换为尚未归一化的 raw block。"""
181
+ auxiliary = _auxiliary_type(line, scene, self.repeated)
182
+ block_type: BlockType = auxiliary or BlockType.TEXT
183
+ level: int | None = None
184
+ relative_top = (line.bbox[1] - scene.physical_box[1]) / max(scene.physical_box[3] - scene.physical_box[1], 1.0)
185
+ if auxiliary is None and line.font_size >= 1.8 * self.body_size and relative_top <= 0.35:
186
+ if not self.document_title_emitted:
187
+ block_type = BlockType.DOC_TITLE
188
+ level = 1
189
+ self.document_title_emitted = True
190
+ else:
191
+ block_type = BlockType.PARAGRAPH_TITLE
192
+ level = 2
193
+ elif auxiliary is None and line.font_size >= 1.45 * self.body_size:
194
+ block_type = BlockType.PARAGRAPH_TITLE
195
+ level = 2
196
+ block: dict[str, Any] = {
197
+ "type": block_type,
198
+ "content": format_line_spans(line.text, line.styles),
199
+ "bbox_mm": line.bbox,
200
+ "angle": line.angle,
201
+ "paint_order": line.paint_order,
202
+ "line_bboxes_mm": [line.bbox],
203
+ }
204
+ if level is not None:
205
+ block["level"] = level
206
+ return block
207
+
208
+ def _table_block(self, table: OfdTableRegion) -> dict[str, Any]:
209
+ """把表格区域转换为尚未归一化的 raw block。"""
210
+ return {
211
+ "type": BlockType.TABLE,
212
+ "content": table.html,
213
+ "bbox_mm": table.bbox,
214
+ "angle": 0,
215
+ "paint_order": table.paint_order,
216
+ }
217
+
218
+ def _image_block(self, image: object) -> dict[str, Any]:
219
+ """把 ImageItem 转换为 raw block或内部占位原子。"""
220
+ payload = getattr(image, "image_base64", None)
221
+ block: dict[str, Any] = {
222
+ "type": BlockType.IMAGE,
223
+ "content": "",
224
+ "bbox_mm": getattr(image, "bbox"),
225
+ "angle": 0,
226
+ "paint_order": getattr(image, "paint_order"),
227
+ "drop_after_sort": payload is None,
228
+ }
229
+ if payload is not None:
230
+ block["image_base64"] = payload
231
+ return block
232
+
233
+ def project_page(self, scene: OfdPageScene) -> list[dict[str, Any]]:
234
+ """把一页场景投影为最终阅读顺序 raw model-list。"""
235
+ merged_lines = merge_same_baseline_lines(scene.text_lines)
236
+ tables = recover_tables(scene.axis_lines, merged_lines, self.table_budget)
237
+ consumed = {line_id for table in tables for line_id in table.consumed_line_ids}
238
+ blocks = [self._text_block(line, scene) for line in merged_lines if id(line) not in consumed]
239
+ blocks.extend(self._table_block(table) for table in tables)
240
+ blocks.extend(self._image_block(image) for image in scene.images)
241
+ page_center_y = bbox_center(scene.physical_box)[1]
242
+ top = [
243
+ block
244
+ for block in blocks
245
+ if block["type"] == BlockType.HEADER
246
+ or (block["type"] == BlockType.PAGE_NUMBER and bbox_center(block["bbox_mm"])[1] < page_center_y)
247
+ ]
248
+ top_ids = {id(block) for block in top}
249
+ bottom = [
250
+ block
251
+ for block in blocks
252
+ if block["type"] == BlockType.FOOTER or (block["type"] == BlockType.PAGE_NUMBER and id(block) not in top_ids)
253
+ ]
254
+ bottom_ids = {id(block) for block in bottom}
255
+ body = [block for block in blocks if id(block) not in top_ids and id(block) not in bottom_ids]
256
+ sortable: list[dict[str, Any]] = []
257
+ for block in body:
258
+ upright = _upright_bbox(list(block["bbox_mm"]), scene, int(block.get("angle", 0) or 0) % 360)
259
+ sortable.append({"bbox": [value * 72.0 / 25.4 for value in upright], "payload": block})
260
+ ordered_body = [entry["payload"] for entry in sort_entries(sortable)]
261
+ ordered = sorted(top, key=lambda block: (block["bbox_mm"][1], block["bbox_mm"][0], block["paint_order"]))
262
+ ordered.extend(ordered_body)
263
+ ordered.extend(sorted(bottom, key=lambda block: (block["bbox_mm"][1], block["bbox_mm"][0], block["paint_order"])))
264
+ output: list[dict[str, Any]] = []
265
+ for block in ordered:
266
+ if block.get("drop_after_sort"):
267
+ continue
268
+ normalized = normalize_bbox(block["bbox_mm"], scene.physical_box)
269
+ if normalized is None:
270
+ continue
271
+ result: dict[str, Any] = {
272
+ "type": block["type"],
273
+ "content": block.get("content", ""),
274
+ "bbox": normalized,
275
+ "angle": block.get("angle", 0),
276
+ }
277
+ if "level" in block:
278
+ result["level"] = block["level"]
279
+ if "image_base64" in block:
280
+ result["image_base64"] = block["image_base64"]
281
+ line_bboxes = [normalize_bbox(item, scene.physical_box) for item in block.get("line_bboxes_mm", [])]
282
+ valid_line_bboxes = [item for item in line_bboxes if item is not None]
283
+ if valid_line_bboxes:
284
+ result["lines"] = [{"bbox": item} for item in valid_line_bboxes]
285
+ output.append(result)
286
+ return output
287
+
288
+ def project(self) -> list[list[dict[str, Any]]]:
289
+ """投影全部页面并保持原始页树顺序。"""
290
+ return [self.project_page(scene) for scene in self.scenes]
291
+
292
+
293
+ __all__ = ["OfdReadingOrderProjector", "merge_same_baseline_lines", "repeated_edge_signatures"]
@@ -0,0 +1,122 @@
1
+ """OFD 资源文件索引与作用域合并。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from loguru import logger
6
+
7
+ from .constants import MAX_DRAW_PARAM_INHERITANCE, OFD_NAMESPACES
8
+ from .errors import OfdParseError, OfdResourceLimitError
9
+ from .geometry import parse_numbers
10
+ from .models import CompositeResource, FontResource, MediaResource, ResourceRegistry
11
+ from .package import OfdPackage, element_text, first_child, local_name, namespace_name, parse_int
12
+
13
+
14
+ def _bool_attr(value: str | None) -> bool:
15
+ """把 OFD 布尔属性解析为确定值。"""
16
+ return (value or "").strip().casefold() in {"true", "1"}
17
+
18
+
19
+ def _resource_asset_part(package: OfdPackage, resource_part: str, base_loc: str, location: str) -> str | None:
20
+ """把资源 BaseLoc 与资源内相对路径组合成包成员。"""
21
+ base_part = package.resolve_reference(resource_part, base_loc) if base_loc else posix_parent(resource_part)
22
+ if base_part is None:
23
+ return None
24
+ synthetic_base = f"{base_part.rstrip('/')}/_resource.xml" if base_loc else resource_part
25
+ return package.resolve_reference(synthetic_base, location)
26
+
27
+
28
+ def posix_parent(part_name: str) -> str:
29
+ """返回包成员的 POSIX 父目录。"""
30
+ return part_name.rsplit("/", 1)[0] if "/" in part_name else ""
31
+
32
+
33
+ def parse_resource_part(package: OfdPackage, resource_part: str | None) -> ResourceRegistry:
34
+ """解析单个 PublicRes、DocumentRes 或 PageRes。"""
35
+ registry = ResourceRegistry()
36
+ if resource_part is None:
37
+ return registry
38
+ root = package.xml_part(resource_part, required=True)
39
+ assert root is not None
40
+ namespace = namespace_name(root.tag)
41
+ if namespace not in OFD_NAMESPACES:
42
+ raise OfdParseError(f"Malformed OFD package: unsupported namespace {namespace!r} in {resource_part!r}")
43
+ base_loc = (root.get("BaseLoc") or "").strip()
44
+ for element in root.iter():
45
+ name = local_name(element.tag)
46
+ resource_id = parse_int(element.get("ID"))
47
+ if resource_id is None:
48
+ continue
49
+ if name == "Font":
50
+ font_file = element_text(first_child(element, "FontFile"))
51
+ font_part = _resource_asset_part(package, resource_part, base_loc, font_file) if font_file else None
52
+ registry.fonts[resource_id] = FontResource(
53
+ resource_id=resource_id,
54
+ font_name=(element.get("FontName") or "").strip(),
55
+ family_name=(element.get("FamilyName") or "").strip(),
56
+ font_part=font_part,
57
+ bold=_bool_attr(element.get("Bold")),
58
+ italic=_bool_attr(element.get("Italic")),
59
+ )
60
+ elif name == "MultiMedia":
61
+ media_file = element_text(first_child(element, "MediaFile"))
62
+ media_part = _resource_asset_part(package, resource_part, base_loc, media_file) if media_file else None
63
+ if media_part:
64
+ registry.media[resource_id] = MediaResource(
65
+ resource_id=resource_id,
66
+ media_type=(element.get("Type") or "").strip(),
67
+ media_format=(element.get("Format") or "").strip(),
68
+ media_part=media_part,
69
+ )
70
+ elif name == "CompositeGraphicUnit":
71
+ size = parse_numbers(f"{element.get('Width') or ''} {element.get('Height') or ''}", expected=2)
72
+ registry.composites[resource_id] = CompositeResource(
73
+ resource_id=resource_id,
74
+ width=size[0] if size else 0.0,
75
+ height=size[1] if size else 0.0,
76
+ element=element,
77
+ )
78
+ elif name == "DrawParam":
79
+ registry.draw_params[resource_id] = {str(key): str(value) for key, value in element.attrib.items()}
80
+ return registry
81
+
82
+
83
+ def merge_registries(*registries: ResourceRegistry) -> ResourceRegistry:
84
+ """按 Public→Document→Page 顺序合并资源并记录重复 ID。"""
85
+ merged = ResourceRegistry()
86
+ for registry in registries:
87
+ for field_name in ("fonts", "media", "composites", "draw_params"):
88
+ target = getattr(merged, field_name)
89
+ incoming = getattr(registry, field_name)
90
+ for resource_id, value in incoming.items():
91
+ if resource_id in target:
92
+ logger.warning(f"OFD_DUPLICATE_RESOURCE_ID: kind={field_name}, id={resource_id}; later scope wins")
93
+ target[resource_id] = value
94
+ return merged
95
+
96
+
97
+ def resolve_draw_param(registry: ResourceRegistry, resource_id: int | None) -> dict[str, str]:
98
+ """迭代解析 DrawParam Relative 继承,并限制深度、检测循环。"""
99
+ if resource_id is None:
100
+ return {}
101
+ inheritance_chain: list[dict[str, str]] = []
102
+ visited: set[int] = set()
103
+ current_id: int | None = resource_id
104
+ while current_id is not None:
105
+ if current_id in visited:
106
+ raise ValueError(f"OFD DrawParam cycle detected at id={current_id}")
107
+ current = registry.draw_params.get(current_id)
108
+ if current is None:
109
+ break
110
+ if len(inheritance_chain) >= MAX_DRAW_PARAM_INHERITANCE:
111
+ raise OfdResourceLimitError(f"OFD resource limit exceeded: max_draw_param_inheritance={MAX_DRAW_PARAM_INHERITANCE}")
112
+ visited.add(current_id)
113
+ inheritance_chain.append(current)
114
+ current_id = parse_int(current.get("Relative"))
115
+
116
+ result: dict[str, str] = {}
117
+ for current in reversed(inheritance_chain):
118
+ result.update({key: value for key, value in current.items() if key not in {"ID", "Relative"}})
119
+ return result
120
+
121
+
122
+ __all__ = ["merge_registries", "parse_resource_part", "resolve_draw_param"]