python-hwpx 5.1.1__tar.gz → 5.3.0__tar.gz

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 (216) hide show
  1. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/NOTICE +9 -3
  2. {python_hwpx-5.1.1/src/python_hwpx.egg-info → python_hwpx-5.3.0}/PKG-INFO +1 -1
  3. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/pyproject.toml +3 -1
  4. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/__init__.py +4 -0
  5. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/shapes.py +130 -0
  6. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/document.py +89 -0
  7. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/equation/__init__.py +9 -1
  8. python_hwpx-5.3.0/src/hwpx/equation/authoring.py +457 -0
  9. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/equation/tokens.py +12 -0
  10. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/experimental.py +8 -0
  11. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/paragraph.py +158 -0
  12. {python_hwpx-5.1.1 → python_hwpx-5.3.0/src/python_hwpx.egg-info}/PKG-INFO +1 -1
  13. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/python_hwpx.egg-info/SOURCES.txt +3 -0
  14. python_hwpx-5.3.0/tests/test_chart_authoring.py +172 -0
  15. python_hwpx-5.3.0/tests/test_equation_authoring.py +345 -0
  16. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_oxml_modularization.py +4 -3
  17. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_product_boundary.py +1 -1
  18. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_stable_surface.py +2 -2
  19. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/LICENSE +0 -0
  20. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/README.md +0 -0
  21. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/setup.cfg +0 -0
  22. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/__init__.py +0 -0
  23. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/_units.py +0 -0
  24. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/fields.py +0 -0
  25. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/layout.py +0 -0
  26. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/media.py +0 -0
  27. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/memos.py +0 -0
  28. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/persistence.py +0 -0
  29. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/_document/tracked.py +0 -0
  30. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/body_patch.py +0 -0
  31. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/data/Skeleton.hwpx +0 -0
  32. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/equation/eqedit.py +0 -0
  33. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/equation/mathml.py +0 -0
  34. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/equation/render.py +0 -0
  35. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/errors.py +0 -0
  36. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/__init__.py +0 -0
  37. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/apply.py +0 -0
  38. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/engine.py +0 -0
  39. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/measure.py +0 -0
  40. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/policy.py +0 -0
  41. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/form_fit/report.py +0 -0
  42. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/ingest/__init__.py +0 -0
  43. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/ingest/base.py +0 -0
  44. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/ingest/hwpx_converter.py +0 -0
  45. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/layout/__init__.py +0 -0
  46. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/layout/lint.py +0 -0
  47. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/layout/report.py +0 -0
  48. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/mutation_report.py +0 -0
  49. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/opc/package.py +0 -0
  50. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/opc/relationships.py +0 -0
  51. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/opc/security.py +0 -0
  52. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/opc/xml_utils.py +0 -0
  53. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/__init__.py +0 -0
  54. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/_document_impl.py +0 -0
  55. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/_document_primitives.py +0 -0
  56. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/body.py +0 -0
  57. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/canonical_defaults.py +0 -0
  58. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/color.py +0 -0
  59. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/common.py +0 -0
  60. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/document.py +0 -0
  61. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/document_parts.py +0 -0
  62. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/header.py +0 -0
  63. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/header_part.py +0 -0
  64. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/memo.py +0 -0
  65. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/namespaces.py +0 -0
  66. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/numbering.py +0 -0
  67. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/objects.py +0 -0
  68. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/parser.py +0 -0
  69. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/run.py +0 -0
  70. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/schema.py +0 -0
  71. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/section.py +0 -0
  72. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/section_format.py +0 -0
  73. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/section_story.py +0 -0
  74. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/simple_parts.py +0 -0
  75. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/table.py +0 -0
  76. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/oxml/utils.py +0 -0
  77. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/package.py +0 -0
  78. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/patch.py +0 -0
  79. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/py.typed +0 -0
  80. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/__init__.py +0 -0
  81. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/ledger.py +0 -0
  82. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/policy.py +0 -0
  83. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/rendering.py +0 -0
  84. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/report.py +0 -0
  85. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/quality/save_pipeline.py +0 -0
  86. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/table_patch.py +0 -0
  87. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/templates.py +0 -0
  88. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/__init__.py +0 -0
  89. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/_schemas/header.xsd +0 -0
  90. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/_schemas/section.xsd +0 -0
  91. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/archive_cli.py +0 -0
  92. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/doc_diff.py +0 -0
  93. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/document_viewer.py +0 -0
  94. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/exporter.py +0 -0
  95. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/generic_inventory.py +0 -0
  96. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/id_integrity.py +0 -0
  97. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/idempotence.py +0 -0
  98. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/ir_equality.py +0 -0
  99. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/layout_preview.py +0 -0
  100. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/mail_merge.py +0 -0
  101. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/markdown_export.py +0 -0
  102. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/object_finder.py +0 -0
  103. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/package_reconcile.py +0 -0
  104. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/package_validator.py +0 -0
  105. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/page_guard.py +0 -0
  106. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/read_fidelity.py +0 -0
  107. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/recover.py +0 -0
  108. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/redline.py +0 -0
  109. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/repair.py +0 -0
  110. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/report_utils.py +0 -0
  111. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/roundtrip_diff.py +0 -0
  112. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/table_cleanup.py +0 -0
  113. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/table_navigation.py +0 -0
  114. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/template_analyzer.py +0 -0
  115. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/text_extract_cli.py +0 -0
  116. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/text_extractor.py +0 -0
  117. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/toc_author.py +0 -0
  118. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/toc_fidelity.py +0 -0
  119. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/hwpx/tools/validator.py +0 -0
  120. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
  121. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/python_hwpx.egg-info/entry_points.txt +0 -0
  122. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/python_hwpx.egg-info/requires.txt +0 -0
  123. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/src/python_hwpx.egg-info/top_level.txt +0 -0
  124. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_add_control.py +0 -0
  125. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_adoption_metrics_snapshot.py +0 -0
  126. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_authoring_quality_corpus.py +0 -0
  127. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_blind_eval_fixture.py +0 -0
  128. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_byte_patch_identity.py +0 -0
  129. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_cell_line_spacing.py +0 -0
  130. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_comment_node_robustness.py +0 -0
  131. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_corpus_read_fidelity.py +0 -0
  132. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_coverage_promotion.py +0 -0
  133. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_coverage_targets.py +0 -0
  134. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_deviations_registry.py +0 -0
  135. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_doc_diff.py +0 -0
  136. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_context_manager.py +0 -0
  137. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_facade_surface.py +0 -0
  138. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_formatting.py +0 -0
  139. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_ops_runtime_boundary.py +0 -0
  140. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_save_api.py +0 -0
  141. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_document_viewer.py +0 -0
  142. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_documentation_code_fences.py +0 -0
  143. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_equation_converter.py +0 -0
  144. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_equation_render.py +0 -0
  145. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_error_messages.py +0 -0
  146. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_evalplan_core_primitives.py +0 -0
  147. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_existing_document_format_editing.py +0 -0
  148. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_form_field_authoring.py +0 -0
  149. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_form_fields.py +0 -0
  150. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_form_fill_core_primitives.py +0 -0
  151. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_form_fit.py +0 -0
  152. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_form_fit_integration.py +0 -0
  153. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_fuzz_catalog_derive_expected.py +0 -0
  154. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_fuzz_loop.py +0 -0
  155. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_fuzz_regressions.py +0 -0
  156. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_gap_closure_tools.py +0 -0
  157. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_golden_api.py +0 -0
  158. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_hp_tab_support.py +0 -0
  159. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_hwpxlib_corpus_read.py +0 -0
  160. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_id_generator_range.py +0 -0
  161. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_image_object_workflow.py +0 -0
  162. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_ingest.py +0 -0
  163. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_inline_models.py +0 -0
  164. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_integration_hwpx_compatibility.py +0 -0
  165. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_integration_roundtrip.py +0 -0
  166. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_layout_cache_scope.py +0 -0
  167. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_layout_lint.py +0 -0
  168. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_layout_preview.py +0 -0
  169. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_library_boundary_closure.py +0 -0
  170. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_llms_txt.py +0 -0
  171. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_mail_merge_xlsx.py +0 -0
  172. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_markdown_export.py +0 -0
  173. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_memo_and_style_editing.py +0 -0
  174. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_moved_surface_hints.py +0 -0
  175. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_mutation_report.py +0 -0
  176. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_namespace_handling.py +0 -0
  177. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_new_features.py +0 -0
  178. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_opc_package.py +0 -0
  179. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_open_safety_corpus.py +0 -0
  180. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_oxml_parsing.py +0 -0
  181. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_packaging_license_metadata.py +0 -0
  182. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_packaging_py_typed.py +0 -0
  183. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_paragraph_keep_together.py +0 -0
  184. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_paragraph_section_management.py +0 -0
  185. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_public_artifact_hygiene.py +0 -0
  186. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_public_runtime_boundary.py +0 -0
  187. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_read_fidelity.py +0 -0
  188. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_recover_broken_zip.py +0 -0
  189. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_redline_authoring.py +0 -0
  190. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_redline_verify.py +0 -0
  191. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_release_workflow_safety.py +0 -0
  192. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_repair_repack.py +0 -0
  193. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_report_utils.py +0 -0
  194. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_repr_snapshots.py +0 -0
  195. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_roundtrip_fidelity.py +0 -0
  196. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_row_heights.py +0 -0
  197. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_save_pipeline.py +0 -0
  198. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_section_headers.py +0 -0
  199. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_set_paragraph_format_keep.py +0 -0
  200. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_shape_geometry_contract.py +0 -0
  201. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_skeleton_template_ids.py +0 -0
  202. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_split_cell.py +0 -0
  203. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_split_merged_cell.py +0 -0
  204. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_table_cleanup.py +0 -0
  205. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_table_navigation.py +0 -0
  206. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_table_patch.py +0 -0
  207. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_table_patch_dryrun.py +0 -0
  208. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_table_patch_m105.py +0 -0
  209. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_tables_default_border.py +0 -0
  210. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_template_analyzer_enrichment.py +0 -0
  211. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_text_extractor_annotations.py +0 -0
  212. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_toc_author.py +0 -0
  213. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_toc_fidelity.py +0 -0
  214. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_validation_severity.py +0 -0
  215. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_validator_comment_nodes.py +0 -0
  216. {python_hwpx-5.1.1 → python_hwpx-5.3.0}/tests/test_version_metadata.py +0 -0
@@ -30,9 +30,15 @@ reimplemented from public references, without copying source code:
30
30
  structure and roundtrip fixtures.
31
31
 
32
32
  - OpenBapul/hml-equation-parser (Apache-2.0): HULK-style EqEdit <-> LaTeX token
33
- mapping, referenced for reader-direction <hp:equation> script rendering. The
34
- token maps are a clean-room re-derivation from the public EqEdit grammar; no
35
- source code was copied, translated, or vendored.
33
+ mapping, referenced for reader-direction <hp:equation> script rendering and
34
+ for the authoring-direction LaTeX -> EqEdit conversion. The token maps are a
35
+ clean-room re-derivation from the public EqEdit grammar; no source code was
36
+ copied, translated, or vendored.
37
+ - chrisryugj/kordoc (MIT): the authoring-direction approach for native
38
+ <hp:equation> generation — the XML shell contract, reserved-word quoting,
39
+ and untrusted-input guards (source-size and recursion-depth caps) were
40
+ referenced as behavior evidence. Clean-room Python implementation; no source
41
+ code was copied, translated, or vendored.
36
42
 
37
43
  These form-fill and equation references were used as behavior evidence only. The
38
44
  implementation is a clean-room Python implementation; no source code was
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 5.1.1
3
+ Version: 5.3.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx"
7
- version = "5.1.1"
7
+ version = "5.3.0"
8
8
  description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
@@ -148,6 +148,7 @@ files = [
148
148
  "src/hwpx/equation/__init__.py",
149
149
  "src/hwpx/equation/tokens.py",
150
150
  "src/hwpx/equation/eqedit.py",
151
+ "src/hwpx/equation/authoring.py",
151
152
  "src/hwpx/equation/mathml.py",
152
153
  "src/hwpx/equation/render.py",
153
154
  "src/hwpx/tools/document_viewer.py",
@@ -202,6 +203,7 @@ include = [
202
203
  "src/hwpx/equation/__init__.py",
203
204
  "src/hwpx/equation/tokens.py",
204
205
  "src/hwpx/equation/eqedit.py",
206
+ "src/hwpx/equation/authoring.py",
205
207
  "src/hwpx/equation/mathml.py",
206
208
  "src/hwpx/equation/render.py",
207
209
  "src/hwpx/tools/document_viewer.py",
@@ -48,6 +48,10 @@ _EXPERIMENTAL_EXPORTS = {
48
48
  # 문서 프리뷰 뷰어(3.8.0 신규). 계약 유동.
49
49
  "DocumentViewer": "hwpx.tools.document_viewer",
50
50
  "render_document_viewer": "hwpx.tools.document_viewer",
51
+ # 수식 저작(5.2.0 신규, LaTeX -> EqEdit). 계약 유동.
52
+ "UnsupportedLatexError": "hwpx.equation.authoring",
53
+ "estimate_equation_size": "hwpx.equation.authoring",
54
+ "latex_to_eqedit": "hwpx.equation.authoring",
51
55
  }
52
56
 
53
57
  # Emptied in 5.0. The 4.x notice on these names said they would go in the next
@@ -227,3 +227,133 @@ def add_ellipse(
227
227
  line_color=line_color, line_width=line_width,
228
228
  fill_color=fill_color, treat_as_char=treat_as_char,
229
229
  )
230
+
231
+
232
+ def add_equation(
233
+ doc: "HwpxDocument",
234
+ script: str,
235
+ *,
236
+ paragraph: HwpxOxmlParagraph | None = None,
237
+ section: HwpxOxmlSection | None = None,
238
+ section_index: int | None = None,
239
+ base_unit: int = 1100,
240
+ size: tuple[int, int] | None = None,
241
+ char_pr_id_ref: str | int | None = None,
242
+ ) -> HwpxOxmlInlineObject:
243
+ """Insert an inline equation and prove the standard reader recognizes it.
244
+
245
+ The emitted XML follows the real-Hancom ``<hp:equation>`` contract
246
+ (specs/054-equation-authoring/evidence/p0/equation-contract.md). After
247
+ insertion the equation is re-read through the standard section scan —
248
+ creation fails loudly if the script did not land verbatim (no
249
+ special-casing by design).
250
+ """
251
+ from ..equation.authoring import estimate_equation_size
252
+ from ..equation.eqedit import MAX_SOURCE_LENGTH
253
+ from ..oxml.namespaces import HP
254
+
255
+ text = (script or "").strip()
256
+ if not text:
257
+ raise ValueError("equation script must be a non-empty string")
258
+ if len(text) > MAX_SOURCE_LENGTH:
259
+ raise ValueError("equation script exceeds size limit")
260
+ if paragraph is None:
261
+ paragraph = doc.add_paragraph(
262
+ "", section=section, section_index=section_index,
263
+ include_run=False,
264
+ )
265
+ if size is None:
266
+ size = estimate_equation_size(text, base_unit=base_unit)
267
+ inline_object = paragraph.add_equation(
268
+ text, base_unit=base_unit, size=size, char_pr_id_ref=char_pr_id_ref,
269
+ )
270
+
271
+ created_id = inline_object.element.get("id", "")
272
+ for owning_section in doc.sections:
273
+ for candidate in owning_section.element.iter(f"{HP}equation"):
274
+ if candidate.get("id") != created_id:
275
+ continue
276
+ script_element = candidate.find(f"{HP}script")
277
+ if script_element is None or (script_element.text or "") != text:
278
+ raise RuntimeError(
279
+ "created equation did not store its script verbatim"
280
+ )
281
+ return inline_object
282
+ raise RuntimeError(
283
+ "created equation was not recognized by the standard section scan"
284
+ )
285
+
286
+
287
+ def add_chart(
288
+ doc: "HwpxDocument",
289
+ chart_xml: bytes | str,
290
+ *,
291
+ paragraph: HwpxOxmlParagraph | None = None,
292
+ section: HwpxOxmlSection | None = None,
293
+ section_index: int | None = None,
294
+ size: tuple[int, int] | None = None,
295
+ treat_as_char: bool = False,
296
+ char_pr_id_ref: str | int | None = None,
297
+ ) -> HwpxOxmlInlineObject:
298
+ """Add a chartML part and its ``<hp:chart>`` anchor, then prove recognition.
299
+
300
+ The part is stored under ``Chart/chartN.xml`` and addressed directly by
301
+ the anchor's ``chartIDRef`` — real Hancom registers chart parts in no
302
+ manifest and draws the chart from the ECMA-376 chartML alone
303
+ (specs/055-chart-authoring/evidence/p0/chart-contract.md). The chartML is
304
+ validated to parse and to carry the ``c:chartSpace`` root before any part
305
+ is written; after insertion the anchor is re-read through the standard
306
+ section scan — creation fails loudly if it did not land (no
307
+ special-casing by design).
308
+ """
309
+ from lxml import etree as _etree # type: ignore[reportAttributeAccessIssue] # lxml has no complete bundled typing
310
+
311
+ from ..oxml.namespaces import HP
312
+
313
+ _CHART_SPACE = "{http://schemas.openxmlformats.org/drawingml/2006/chart}chartSpace"
314
+
315
+ data = chart_xml.encode("utf-8") if isinstance(chart_xml, str) else bytes(chart_xml)
316
+ if not data.strip():
317
+ raise ValueError("chart_xml must be non-empty chartML")
318
+ try:
319
+ root = _etree.fromstring(data)
320
+ except _etree.XMLSyntaxError as exc:
321
+ raise ValueError(f"chart_xml is not well-formed XML: {exc}") from exc
322
+ if root.tag != _CHART_SPACE:
323
+ raise ValueError(
324
+ "chart_xml root must be the ECMA-376 c:chartSpace element, "
325
+ f"got {root.tag!r}"
326
+ )
327
+
328
+ existing = {name for name in doc._package.part_names() if name.startswith("Chart/")}
329
+ n = 1
330
+ while f"Chart/chart{n}.xml" in existing:
331
+ n += 1
332
+ part_path = f"Chart/chart{n}.xml"
333
+
334
+ if paragraph is None:
335
+ paragraph = doc.add_paragraph(
336
+ "", section=section, section_index=section_index,
337
+ include_run=False,
338
+ )
339
+ doc._package.write(part_path, data)
340
+ inline_object = paragraph.add_chart(
341
+ part_path,
342
+ size=size,
343
+ treat_as_char=treat_as_char,
344
+ char_pr_id_ref=char_pr_id_ref,
345
+ )
346
+
347
+ created_id = inline_object.element.get("id", "")
348
+ for owning_section in doc.sections:
349
+ for candidate in owning_section.element.iter(f"{HP}chart"):
350
+ if candidate.get("id") != created_id:
351
+ continue
352
+ if candidate.get("chartIDRef") != part_path:
353
+ raise RuntimeError(
354
+ "created chart anchor does not reference its part"
355
+ )
356
+ return inline_object
357
+ raise RuntimeError(
358
+ "created chart was not recognized by the standard section scan"
359
+ )
@@ -1431,6 +1431,95 @@ class HwpxDocument:
1431
1431
  )
1432
1432
 
1433
1433
 
1434
+ def add_chart(
1435
+ self,
1436
+ chart_xml: bytes | str,
1437
+ *,
1438
+ paragraph: HwpxOxmlParagraph | None = None,
1439
+ section: HwpxOxmlSection | None = None,
1440
+ section_index: int | None = None,
1441
+ size: tuple[int, int] | None = None,
1442
+ treat_as_char: bool = False,
1443
+ char_pr_id_ref: str | int | None = None,
1444
+ ) -> HwpxOxmlInlineObject:
1445
+ """Insert a native chart from ECMA-376 chartML. **Experimental contract.**
1446
+
1447
+ Stores *chart_xml* as a ``Chart/chartN.xml`` package part and emits the
1448
+ real-Hancom ``<hp:chart>`` anchor referencing it via ``chartIDRef``
1449
+ (contract: ``specs/055-chart-authoring/evidence/p0/chart-contract.md``).
1450
+ Hancom draws the chart from the chartML alone — no OLE fallback or
1451
+ pre-rendered image is written. The chartML must parse and carry the
1452
+ ``c:chartSpace`` root (typed rejection otherwise), and the created
1453
+ anchor is re-read through the standard section scan — creation fails
1454
+ loudly if the standard consumer would not see it.
1455
+
1456
+ Args:
1457
+ chart_xml: ECMA-376 chartML document (``c:chartSpace``).
1458
+ paragraph: Target paragraph (e.g. inside a table cell). When
1459
+ omitted a new paragraph is appended to *section*.
1460
+ size: Optional ``(width, height)`` HWPUNIT pair for the anchor.
1461
+ treat_as_char: ``True`` places the chart inline in the text flow;
1462
+ default mirrors the render-verified gold float placement.
1463
+ """
1464
+
1465
+ return _shapes.add_chart(
1466
+ self,
1467
+ chart_xml,
1468
+ paragraph=paragraph,
1469
+ section=section,
1470
+ section_index=section_index,
1471
+ size=size,
1472
+ treat_as_char=treat_as_char,
1473
+ char_pr_id_ref=char_pr_id_ref,
1474
+ )
1475
+
1476
+ def add_equation(
1477
+ self,
1478
+ script: str,
1479
+ *,
1480
+ paragraph: HwpxOxmlParagraph | None = None,
1481
+ section: HwpxOxmlSection | None = None,
1482
+ section_index: int | None = None,
1483
+ base_unit: int = 1100,
1484
+ size: tuple[int, int] | None = None,
1485
+ char_pr_id_ref: str | int | None = None,
1486
+ ) -> HwpxOxmlInlineObject:
1487
+ """Insert an inline equation from an EqEdit script. **Experimental contract.**
1488
+
1489
+ Emits the real-Hancom ``<hp:equation>`` shape (contract:
1490
+ ``specs/054-equation-authoring/evidence/p0/equation-contract.md``): the
1491
+ EqEdit source is stored verbatim in ``<hp:script>``, no layout cache is
1492
+ written (Hancom re-lays-out on open), and the shape is inline so it
1493
+ renders in the page flow. The created element is immediately re-read
1494
+ through the standard section scan — creation fails loudly if the
1495
+ standard consumer would not see it (no special-casing by design).
1496
+
1497
+ To author from LaTeX, convert first (typed refusal outside the
1498
+ verified token set)::
1499
+
1500
+ from hwpx.equation import latex_to_eqedit
1501
+ doc.add_equation(latex_to_eqedit(r"\\frac{a}{b}"))
1502
+
1503
+ Args:
1504
+ script: EqEdit script stored as-is (e.g. ``{a} over {b}``).
1505
+ paragraph: Target paragraph (e.g. inside a table cell). When
1506
+ omitted a new paragraph is appended to *section*.
1507
+ base_unit: Equation base font size in 1/100 pt.
1508
+ size: Optional explicit ``(width, height)`` HWPUNIT pair;
1509
+ defaults to a proportional estimate (Hancom re-measures).
1510
+ """
1511
+
1512
+ return _shapes.add_equation(
1513
+ self,
1514
+ script,
1515
+ paragraph=paragraph,
1516
+ section=section,
1517
+ section_index=section_index,
1518
+ base_unit=base_unit,
1519
+ size=size,
1520
+ char_pr_id_ref=char_pr_id_ref,
1521
+ )
1522
+
1434
1523
  def set_page_size(
1435
1524
  self,
1436
1525
  *,
@@ -1,5 +1,5 @@
1
1
  # SPDX-License-Identifier: Apache-2.0
2
- """Reader-direction equation support: EqEdit → LaTeX → MathML.
2
+ """Equation support: EqEdit → LaTeX → MathML (reader) and LaTeX → EqEdit (authoring).
3
3
 
4
4
  Clean-room re-derivation of the HULK-style EqEdit vocabulary; see NOTICE for the
5
5
  referenced projects. ``latex2mathml`` is an optional dependency
@@ -9,6 +9,11 @@ block rather than dropping the equation.
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ from .authoring import (
13
+ UnsupportedLatexError,
14
+ estimate_equation_size,
15
+ latex_to_eqedit,
16
+ )
12
17
  from .eqedit import (
13
18
  MAX_GROUP_DEPTH,
14
19
  MAX_SOURCE_LENGTH,
@@ -39,8 +44,11 @@ __all__ = [
39
44
  "EquationConversionError",
40
45
  "EquationRender",
41
46
  "MathMLUnavailableError",
47
+ "UnsupportedLatexError",
42
48
  "eqedit_to_latex",
49
+ "estimate_equation_size",
43
50
  "latex2mathml_available",
51
+ "latex_to_eqedit",
44
52
  "latex_to_mathml",
45
53
  "render_equation",
46
54
  ]