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,306 @@
1
+ """把 EPUB XHTML/SVG 内容文档转换为 DocVortex raw blocks。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ from dataclasses import dataclass
7
+
8
+ from lxml import etree # type: ignore[reportMissingImports]
9
+
10
+ from ....foundation.image_payload import parse_image_data_uri_strict
11
+ from .._shared.hyperlink import sanitize_hyperlink_target
12
+ from docvortex.content.markup import (
13
+ MarkupAnchorDocument,
14
+ MarkupAnchorRegistry,
15
+ MarkupProjector,
16
+ MarkupStylesheet,
17
+ ResolvedMarkupImage,
18
+ element_id,
19
+ visible_element_text,
20
+ )
21
+ from docvortex.content.markup.projector import (
22
+ BLOCK_TAGS as _BLOCK_TAGS,
23
+ SKIPPED_TAGS as _SKIPPED_TAGS,
24
+ clean_text_node as _clean_text_node,
25
+ entity_text as _entity_text,
26
+ local_name as _local_name,
27
+ )
28
+ from .constants import IMAGE_MEDIA_BY_EXTENSION, SVG_MEDIA_TYPE
29
+ from .package import EpubPackage
30
+
31
+ _INDIVIDUAL_NOTE_TYPE_ORDER = ("footnote", "endnote", "rearnote")
32
+ _INDIVIDUAL_NOTE_ROLE_ORDER = ("doc-footnote", "doc-endnote")
33
+ _INDIVIDUAL_NOTE_TYPES = frozenset(_INDIVIDUAL_NOTE_TYPE_ORDER)
34
+ _INDIVIDUAL_NOTE_ROLES = frozenset(_INDIVIDUAL_NOTE_ROLE_ORDER)
35
+ _NOTE_BLOCK_TAGS = _BLOCK_TAGS | {"li"}
36
+ _NOTE_NON_TEXT_SUBTREES = frozenset(
37
+ {
38
+ "figure",
39
+ "h1",
40
+ "h2",
41
+ "h3",
42
+ "h4",
43
+ "h5",
44
+ "h6",
45
+ "math",
46
+ "ol",
47
+ "pre",
48
+ "svg",
49
+ "table",
50
+ "ul",
51
+ }
52
+ )
53
+
54
+
55
+ def _epub_types(element: etree._Element) -> frozenset[str]:
56
+ """读取 EPUB 命名空间或未命名 type 属性中的结构语义 token。"""
57
+ values: list[str] = []
58
+ for name, value in element.attrib.items():
59
+ local_name = etree.QName(name).localname if name.startswith("{") else name.split(":", 1)[-1]
60
+ if local_name == "type":
61
+ values.extend(value.casefold().split())
62
+ return frozenset(values)
63
+
64
+
65
+ def _roles(element: etree._Element) -> frozenset[str]:
66
+ """读取 ARIA role 属性中的小写语义 token。"""
67
+ return frozenset((element.get("role") or "").casefold().split())
68
+
69
+
70
+ def _is_individual_note(element: etree._Element) -> bool:
71
+ """判断块级元素是否表示单条 EPUB Footnote/Endnote。"""
72
+ if _local_name(element) not in _NOTE_BLOCK_TAGS:
73
+ return False
74
+ return bool(_epub_types(element) & _INDIVIDUAL_NOTE_TYPES or _roles(element) & _INDIVIDUAL_NOTE_ROLES)
75
+
76
+
77
+ def _note_semantic(element: etree._Element) -> str:
78
+ """按固定优先级返回 note 的 EPUB type 或 ARIA role。"""
79
+ epub_types = _epub_types(element)
80
+ for note_type in _INDIVIDUAL_NOTE_TYPE_ORDER:
81
+ if note_type in epub_types:
82
+ return note_type
83
+ roles = _roles(element)
84
+ for role in _INDIVIDUAL_NOTE_ROLE_ORDER:
85
+ if role in roles:
86
+ return role
87
+ return "note"
88
+
89
+
90
+ def _note_has_text_block(element: etree._Element) -> bool:
91
+ """判断 note 是否能产生非空 text,从而避免注册没有正文目标的 anchor。"""
92
+ if _clean_text_node(element.text).strip():
93
+ return True
94
+ for child in element:
95
+ if child.tail and _clean_text_node(child.tail).strip():
96
+ return True
97
+ if not isinstance(child.tag, str):
98
+ if _entity_text(child):
99
+ return True
100
+ continue
101
+ name = _local_name(child)
102
+ if name in _SKIPPED_TAGS or name in _NOTE_NON_TEXT_SUBTREES or name in {"img", "image"}:
103
+ continue
104
+ if _note_has_text_block(child):
105
+ return True
106
+ return False
107
+
108
+
109
+ def _load_chapter_stylesheet(package: EpubPackage, chapter_path: str, root: etree._Element) -> MarkupStylesheet:
110
+ """按章节 head 顺序加载包内 CSS 与内联 style。"""
111
+ stylesheet = MarkupStylesheet()
112
+ for element in root.iter():
113
+ if not isinstance(element.tag, str):
114
+ continue
115
+ name = _local_name(element)
116
+ if name == "link" and "stylesheet" in (element.get("rel") or "").casefold().split():
117
+ target = package.resolve_reference(element.get("href") or "", base_part=chapter_path)
118
+ if target is None:
119
+ continue
120
+ data = package.read_part(target.path)
121
+ if data is not None:
122
+ stylesheet.add(data.decode("utf-8-sig", errors="replace"))
123
+ elif name == "style":
124
+ stylesheet.add("".join(element.itertext()))
125
+ return stylesheet
126
+
127
+
128
+ class _EpubAnchorPolicy:
129
+ """保持 EPUB 标题、脚注 identity 与可落地性判定的既有契约。"""
130
+
131
+ anchor_prefix = "epub"
132
+ register_document_start = True
133
+
134
+ @staticmethod
135
+ def heading_identity(element: etree._Element, ordinal: int) -> str:
136
+ """按源 ID 或匿名标题序号生成 EPUB 标题 identity。"""
137
+ return f"{element_id(element) or 'heading'}-{ordinal}"
138
+
139
+ @staticmethod
140
+ def is_materializable_note(element: etree._Element, document: MarkupAnchorDocument) -> bool:
141
+ """沿用 EPUB note 语义、文本块能力和最终可见性判断。"""
142
+ return _is_individual_note(element) and _note_has_text_block(element) and bool(visible_element_text(element, document))
143
+
144
+ @staticmethod
145
+ def note_identity(element: etree._Element, ordinal: int) -> str:
146
+ """按 note 类型、源 ID 与章节内序号生成 EPUB 脚注 identity。"""
147
+ source_id = element_id(element)
148
+ return f"note-{_note_semantic(element)}-{source_id or 'anonymous'}-{ordinal}"
149
+
150
+
151
+ class EpubAnchorRegistry:
152
+ """建立章节路径、标题与 note fragment 到实际 canonical anchor 的别名表。"""
153
+
154
+ def __init__(self, chapters: list[tuple[str, etree._Element]], package: EpubPackage) -> None:
155
+ """预扫描全部选中 XHTML 章节,建立标题、note 与章节起点映射。"""
156
+ self._package = package
157
+ documents = [
158
+ MarkupAnchorDocument(
159
+ key=chapter_path,
160
+ root=root,
161
+ stylesheet=_load_chapter_stylesheet(package, chapter_path, root),
162
+ visibility_scope="nearest_body",
163
+ text_normalization="xhtml_whitespace",
164
+ )
165
+ for chapter_path, root in chapters
166
+ ]
167
+ self._registry = MarkupAnchorRegistry(documents, _EpubAnchorPolicy())
168
+
169
+ def heading_anchor(self, heading: etree._Element) -> str | None:
170
+ """返回一个已预扫描 EPUB 标题的规范 anchor。"""
171
+ return self._registry.heading_anchor(heading)
172
+
173
+ def heading_label(self, anchor: str) -> str | None:
174
+ """返回规范 EPUB 标题 anchor 对应的可见标签。"""
175
+ return self._registry.heading_label(anchor)
176
+
177
+ def note_anchor(self, note: etree._Element) -> str | None:
178
+ """返回一个已预扫描 EPUB Footnote/Endnote anchor。"""
179
+ return self._registry.note_anchor(note)
180
+
181
+ def resolve_anchor(self, href: str, *, base_part: str) -> str | None:
182
+ """解析指向正文标题或 note 的 EPUB 包内链接,并返回不带井号的 anchor。"""
183
+ normalized = sanitize_hyperlink_target(
184
+ href,
185
+ allowed_schemes=(),
186
+ allow_relative=True,
187
+ allow_fragment=True,
188
+ )
189
+ if normalized is None:
190
+ return None
191
+ target = self._package.resolve_reference(normalized, base_part=base_part)
192
+ if target is None:
193
+ return None
194
+ return self._registry.resolve_target(target.path, target.fragment)
195
+
196
+ def resolve_link(self, href: str, *, base_part: str) -> str | None:
197
+ """解析安全外部链接或指向已输出标题/note 的 EPUB 内部链接。"""
198
+ external = sanitize_hyperlink_target(href)
199
+ if external is not None:
200
+ return external
201
+ anchor = self.resolve_anchor(href, base_part=base_part)
202
+ return f"#{anchor}" if anchor else None
203
+
204
+
205
+ def build_anchor_registry(chapters: list[tuple[str, etree._Element]], package: EpubPackage) -> EpubAnchorRegistry:
206
+ """从已解析章节元组建立跨章节锚点注册表。"""
207
+ return EpubAnchorRegistry(chapters, package)
208
+
209
+
210
+ @dataclass(frozen=True, slots=True)
211
+ class _EpubMarkupContext:
212
+ """把 EPUB 包资源与文档级 anchor 适配到共享 markup projector。"""
213
+
214
+ package: EpubPackage
215
+ chapter_path: str
216
+ anchors: EpubAnchorRegistry
217
+
218
+ def resolve_link(self, href: str) -> str | None:
219
+ """解析安全外部链接或实际存在的 EPUB 包内 anchor。"""
220
+ return self.anchors.resolve_link(href, base_part=self.chapter_path)
221
+
222
+ def resolve_image(self, source: str, *, alt: str = "") -> ResolvedMarkupImage | None:
223
+ """读取并严格校验一个 EPUB 包内栅格图片引用。"""
224
+ target = self.package.resolve_reference(source, base_part=self.chapter_path)
225
+ if target is None:
226
+ return ResolvedMarkupImage(alt=alt) if alt else None
227
+ media_type = (self.package.content_type_for(target.path) or "").casefold()
228
+ extension = target.path.rsplit(".", 1)[-1].casefold() if "." in target.path else ""
229
+ media_type = media_type or IMAGE_MEDIA_BY_EXTENSION.get(extension, "")
230
+ if not media_type.startswith("image/") or media_type == SVG_MEDIA_TYPE:
231
+ return ResolvedMarkupImage(alt=alt) if alt else None
232
+ payload = self.package.read_part(target.path, asset=True)
233
+ if payload is None:
234
+ return ResolvedMarkupImage(alt=alt) if alt else None
235
+ data_uri = f"data:{media_type};base64,{base64.b64encode(payload).decode('ascii')}"
236
+ try:
237
+ parse_image_data_uri_strict(data_uri)
238
+ except ValueError:
239
+ return ResolvedMarkupImage(alt=alt) if alt else None
240
+ return ResolvedMarkupImage(image_base64=data_uri, alt=alt)
241
+
242
+ def heading_anchor(self, heading: etree._Element) -> str | None:
243
+ """返回一个已预扫描 EPUB 标题的规范 anchor。"""
244
+ return self.anchors.heading_anchor(heading)
245
+
246
+ def heading_label(self, anchor: str) -> str | None:
247
+ """返回规范 EPUB 标题 anchor 对应的可见标签。"""
248
+ return self.anchors.heading_label(anchor)
249
+
250
+ def note_anchor(self, note: etree._Element) -> str | None:
251
+ """返回一个已预扫描 EPUB Footnote/Endnote anchor。"""
252
+ return self.anchors.note_anchor(note)
253
+
254
+
255
+ class EpubChapterConverter:
256
+ """把一个 XHTML spine item 通过共享 projector 投影为 raw blocks。"""
257
+
258
+ def __init__(
259
+ self,
260
+ package: EpubPackage,
261
+ chapter_path: str,
262
+ root: etree._Element,
263
+ anchors: EpubAnchorRegistry,
264
+ ) -> None:
265
+ """绑定单个章节的包、路径、DOM 与文档级 anchor 注册表。"""
266
+ self.package = package
267
+ self.chapter_path = chapter_path
268
+ self.root = root
269
+ self.anchors = anchors
270
+ self.stylesheet = _load_chapter_stylesheet(package, chapter_path, root)
271
+
272
+ def convert(self) -> list[dict[str, object]]:
273
+ """解析 XHTML body,并保持既有 EPUB 标题与脚注语义。"""
274
+ body = next(
275
+ (element for element in self.root.iter() if isinstance(element.tag, str) and _local_name(element) == "body"),
276
+ None,
277
+ )
278
+ if body is None:
279
+ return []
280
+ context = _EpubMarkupContext(self.package, self.chapter_path, self.anchors)
281
+ return MarkupProjector(
282
+ body,
283
+ context,
284
+ self.stylesheet,
285
+ single_document_title=False,
286
+ ).convert()
287
+
288
+
289
+ def convert_svg_spine(
290
+ package: EpubPackage,
291
+ chapter_path: str,
292
+ root: etree._Element,
293
+ ) -> list[dict[str, object]]:
294
+ """把 standalone SVG spine item 尽力转换为文本和包内栅格图片。"""
295
+ empty_registry = EpubAnchorRegistry([], package)
296
+ context = _EpubMarkupContext(package, chapter_path, empty_registry)
297
+ stylesheet = _load_chapter_stylesheet(package, chapter_path, root)
298
+ return MarkupProjector(root, context, stylesheet).convert_svg()
299
+
300
+
301
+ __all__ = [
302
+ "EpubAnchorRegistry",
303
+ "EpubChapterConverter",
304
+ "build_anchor_registry",
305
+ "convert_svg_spine",
306
+ ]
@@ -0,0 +1,6 @@
1
+ """HTML 静态 Flash 解析实现。"""
2
+
3
+ from docvortex.document.contracts import HtmlSourceContext
4
+ from .errors import HtmlParseError, HtmlResourceLimitError
5
+
6
+ __all__ = ["HtmlParseError", "HtmlResourceLimitError", "HtmlSourceContext"]
@@ -0,0 +1,271 @@
1
+ """Standalone HTML 标题、脚注与 fragment anchor 规范化。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from copy import deepcopy
7
+
8
+ from lxml import etree # type: ignore[reportMissingImports]
9
+
10
+ from docvortex.content.markup import MarkupAnchorDocument, MarkupAnchorRegistry, MarkupStylesheet, TextStyle, element_id
11
+ from docvortex.content.markup.projector import BLOCK_TAGS, SKIPPED_TAGS, local_name, visible_raw_text_with_style
12
+
13
+
14
+ _NOTE_TYPES = frozenset({"footnote", "endnote", "rearnote"})
15
+ _NOTE_ROLES = frozenset({"doc-footnote", "doc-endnote"})
16
+ _NON_TEXT_BLOCK_TAGS = frozenset(
17
+ {
18
+ "figure",
19
+ "h1",
20
+ "h2",
21
+ "h3",
22
+ "h4",
23
+ "h5",
24
+ "h6",
25
+ "hr",
26
+ "math",
27
+ "ol",
28
+ "pre",
29
+ "svg",
30
+ "table",
31
+ "ul",
32
+ }
33
+ )
34
+
35
+
36
+ def is_note_element(element: etree._Element) -> bool:
37
+ """判断元素是否表示一条可独立投影的 Footnote/Endnote。"""
38
+ roles = frozenset((element.get("role") or "").casefold().split())
39
+ classes = frozenset((element.get("class") or "").casefold().split())
40
+ if (element.get("data-block-type") or "").casefold() == "page_footnote" or "docvortex-page-footnote" in classes:
41
+ return True
42
+ types: set[str] = set()
43
+ for name, value in element.attrib.items():
44
+ attribute = etree.QName(name).localname if name.startswith("{") else name.split(":", 1)[-1]
45
+ if attribute == "type":
46
+ types.update(value.casefold().split())
47
+ return bool(roles & _NOTE_ROLES or types & _NOTE_TYPES)
48
+
49
+
50
+ def append_referenced_notes(
51
+ selected_root: etree._Element,
52
+ original_body: etree._Element,
53
+ *,
54
+ stylesheet: MarkupStylesheet,
55
+ resolve_same_document_fragment: Callable[[str], str | None],
56
+ ) -> etree._Element:
57
+ """把正文候选引用但位于候选外的脚注副本追加到内容根末尾。"""
58
+ selected_ids = {
59
+ identity
60
+ for element in selected_root.iter()
61
+ if isinstance(element.tag, str) and (identity := element_id(element)) is not None
62
+ }
63
+ targets = {
64
+ identity: element
65
+ for element in original_body.iter()
66
+ if isinstance(element.tag, str) and (identity := element_id(element)) is not None and is_note_element(element)
67
+ }
68
+ referenced_ids = dict.fromkeys(
69
+ fragment
70
+ for element in selected_root.iter()
71
+ if isinstance(element.tag, str)
72
+ and local_name(element) == "a"
73
+ and (fragment := resolve_same_document_fragment(element.get("href") or "")) is not None
74
+ )
75
+ companions = [
76
+ element for identity, element in targets.items() if identity in referenced_ids and identity not in selected_ids
77
+ ]
78
+ companion_copies = [
79
+ copy for companion in companions if (copy := _copy_note_with_source_visibility(companion, stylesheet)) is not None
80
+ ]
81
+ if not companion_copies:
82
+ return selected_root
83
+ wrapper = etree.Element("div")
84
+ wrapper.append(selected_root)
85
+ for companion in companion_copies:
86
+ wrapper.append(companion)
87
+ return wrapper
88
+
89
+
90
+ def _copy_note_with_source_visibility(
91
+ note: etree._Element,
92
+ stylesheet: MarkupStylesheet,
93
+ ) -> etree._Element | None:
94
+ """按原始祖先链复制 note;整树隐藏时丢弃,并保留继承文字样式与 visibility。"""
95
+ inherited = TextStyle()
96
+ visibility_hidden = False
97
+ chain = [ancestor for ancestor in reversed(list(note.iterancestors())) if isinstance(ancestor.tag, str)]
98
+ for current in chain:
99
+ resolved = stylesheet.resolve(current, inherited, visibility_hidden)
100
+ if resolved.subtree_hidden:
101
+ return None
102
+ inherited = resolved.text
103
+ visibility_hidden = resolved.visibility_hidden
104
+ if stylesheet.resolve(note, inherited, visibility_hidden).subtree_hidden:
105
+ return None
106
+ copied = deepcopy(note)
107
+ declarations: list[str] = []
108
+ if inherited.bold:
109
+ declarations.append("font-weight:bold")
110
+ if inherited.italic:
111
+ declarations.append("font-style:italic")
112
+ decorations = [
113
+ decoration
114
+ for enabled, decoration in (
115
+ (inherited.underline, "underline"),
116
+ (inherited.strikethrough, "line-through"),
117
+ )
118
+ if enabled
119
+ ]
120
+ if decorations:
121
+ declarations.append(f"text-decoration:{' '.join(decorations)}")
122
+ if inherited.superscript:
123
+ declarations.append("vertical-align:super")
124
+ elif inherited.subscript:
125
+ declarations.append("vertical-align:sub")
126
+ if visibility_hidden:
127
+ declarations.append("visibility:hidden")
128
+ if not declarations:
129
+ return copied
130
+ wrapper = etree.Element("div")
131
+ wrapper.set("style", ";".join(declarations))
132
+ if inherited.superscript and inherited.subscript:
133
+ subscript_wrapper = etree.SubElement(wrapper, "sub")
134
+ subscript_wrapper.append(copied)
135
+ else:
136
+ wrapper.append(copied)
137
+ return wrapper
138
+
139
+
140
+ class _HtmlAnchorPolicy:
141
+ """保持 standalone HTML 标题与脚注 identity 的既有生成规则。"""
142
+
143
+ anchor_prefix = "html"
144
+ register_document_start = False
145
+
146
+ @staticmethod
147
+ def heading_identity(element: etree._Element, ordinal: int) -> str:
148
+ """按源 ID 或匿名标题序号生成 HTML 标题 identity。"""
149
+ identity = element_id(element) or f"heading-{ordinal}"
150
+ return f"heading-{identity}-{ordinal}"
151
+
152
+ @staticmethod
153
+ def is_materializable_note(element: etree._Element, document: MarkupAnchorDocument) -> bool:
154
+ """沿用 HTML note marker 与顶层文本可落地性判断。"""
155
+ return is_note_element(element) and _note_has_materializable_text_target(element, document.stylesheet)
156
+
157
+ @staticmethod
158
+ def note_identity(element: etree._Element, ordinal: int) -> str:
159
+ """按源 ID 或匿名脚注序号生成 HTML 脚注 identity。"""
160
+ identity = element_id(element) or f"note-{ordinal}"
161
+ return f"note-{identity}-{ordinal}"
162
+
163
+
164
+ class HtmlAnchorRegistry:
165
+ """把选中 DOM 的标题、note 和源 fragment 映射到稳定 anchor。"""
166
+
167
+ def __init__(
168
+ self,
169
+ root: etree._Element,
170
+ stylesheet: MarkupStylesheet,
171
+ *,
172
+ source_key: str = "html",
173
+ ) -> None:
174
+ """预扫描选中内容,建立 document-wide 唯一 anchor 映射。"""
175
+ self._source_key = source_key
176
+ document = MarkupAnchorDocument(
177
+ key=source_key,
178
+ root=root,
179
+ stylesheet=stylesheet,
180
+ visibility_scope="all_ancestors",
181
+ text_normalization="unicode_whitespace",
182
+ )
183
+ self._registry = MarkupAnchorRegistry([document], _HtmlAnchorPolicy())
184
+
185
+ def heading_anchor(self, heading: etree._Element) -> str | None:
186
+ """返回标题的规范 anchor。"""
187
+ return self._registry.heading_anchor(heading)
188
+
189
+ def heading_label(self, anchor: str) -> str | None:
190
+ """返回规范标题 anchor 对应的可见标签。"""
191
+ return self._registry.heading_label(anchor)
192
+
193
+ def note_anchor(self, note: etree._Element) -> str | None:
194
+ """返回单条 Footnote/Endnote 的规范 anchor。"""
195
+ return self._registry.note_anchor(note)
196
+
197
+ def resolve_fragment(self, fragment: str) -> str | None:
198
+ """把源文档 fragment 转换为实际可输出的内部链接。"""
199
+ normalized = fragment.removeprefix("#").strip()
200
+ anchor = self._registry.resolve_target(self._source_key, normalized)
201
+ return f"#{anchor}" if anchor else None
202
+
203
+
204
+ def _note_has_materializable_text_target(element: etree._Element, stylesheet: MarkupStylesheet) -> bool:
205
+ """判断 note 是否会投影出可挂载 anchor 的顶层文本 block。"""
206
+ inherited = TextStyle()
207
+ visibility_hidden = False
208
+ chain = [ancestor for ancestor in reversed(list(element.iterancestors())) if isinstance(ancestor.tag, str)]
209
+ for ancestor in chain:
210
+ resolved = stylesheet.resolve(ancestor, inherited, visibility_hidden)
211
+ if resolved.subtree_hidden:
212
+ return False
213
+ inherited = resolved.text
214
+ visibility_hidden = resolved.visibility_hidden
215
+ resolved = stylesheet.resolve(element, inherited, visibility_hidden)
216
+ if resolved.subtree_hidden:
217
+ return False
218
+ return _container_materializes_text_block(
219
+ element,
220
+ stylesheet,
221
+ resolved.text,
222
+ resolved.visibility_hidden,
223
+ )
224
+
225
+
226
+ def _container_materializes_text_block(
227
+ element: etree._Element,
228
+ stylesheet: MarkupStylesheet,
229
+ style: TextStyle,
230
+ visibility_hidden: bool,
231
+ ) -> bool:
232
+ """按共享 projector 的容器分块规则判断是否会产生顶层文本。"""
233
+ if not visibility_hidden and (element.text or "").strip():
234
+ return True
235
+ for child in element:
236
+ if isinstance(child.tag, str):
237
+ resolved = stylesheet.resolve(child, style, visibility_hidden)
238
+ if not resolved.subtree_hidden:
239
+ name = local_name(child)
240
+ if name == "p":
241
+ value = visible_raw_text_with_style(
242
+ child,
243
+ stylesheet,
244
+ resolved.text,
245
+ resolved.visibility_hidden,
246
+ )
247
+ if value.strip():
248
+ return True
249
+ elif name in BLOCK_TAGS:
250
+ if name not in _NON_TEXT_BLOCK_TAGS and _container_materializes_text_block(
251
+ child,
252
+ stylesheet,
253
+ resolved.text,
254
+ resolved.visibility_hidden,
255
+ ):
256
+ return True
257
+ elif name not in SKIPPED_TAGS:
258
+ value = visible_raw_text_with_style(
259
+ child,
260
+ stylesheet,
261
+ resolved.text,
262
+ resolved.visibility_hidden,
263
+ )
264
+ if value.strip():
265
+ return True
266
+ if not visibility_hidden and (child.tail or "").strip():
267
+ return True
268
+ return False
269
+
270
+
271
+ __all__ = ["HtmlAnchorRegistry", "append_referenced_notes", "is_note_element"]
@@ -0,0 +1,25 @@
1
+ """HTML 静态解析使用的固定资源限制。"""
2
+
3
+ from typing import Final
4
+
5
+
6
+ MAX_HTML_BYTES: Final = 128 * 1024 * 1024
7
+ MAX_HTML_NODES: Final = 2_000_000
8
+ MAX_HTML_DEPTH: Final = 256
9
+ MAX_HTML_IMAGE_BYTES: Final = 20 * 1024 * 1024
10
+ MAX_HTML_IMAGE_TOTAL_BYTES: Final = 128 * 1024 * 1024
11
+ MAX_HTML_STYLESHEET_BYTES: Final = 5 * 1024 * 1024
12
+ MAX_HTML_STYLESHEET_TOTAL_BYTES: Final = 20 * 1024 * 1024
13
+ MAX_HTML_RENDERED_BYTES: Final = 256 * 1024 * 1024
14
+
15
+
16
+ __all__ = [
17
+ "MAX_HTML_BYTES",
18
+ "MAX_HTML_DEPTH",
19
+ "MAX_HTML_IMAGE_BYTES",
20
+ "MAX_HTML_IMAGE_TOTAL_BYTES",
21
+ "MAX_HTML_NODES",
22
+ "MAX_HTML_RENDERED_BYTES",
23
+ "MAX_HTML_STYLESHEET_BYTES",
24
+ "MAX_HTML_STYLESHEET_TOTAL_BYTES",
25
+ ]
@@ -0,0 +1,7 @@
1
+ """保留原有导入入口;共享实现由下层模块唯一维护。"""
2
+
3
+ from docvortex.document.contracts import (
4
+ HtmlSourceContext as HtmlSourceContext,
5
+ )
6
+
7
+ __all__ = ["HtmlSourceContext"]