python-hwpx 4.2.0__tar.gz → 5.0.2__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 (402) hide show
  1. python_hwpx-5.0.2/PKG-INFO +238 -0
  2. python_hwpx-5.0.2/README.md +187 -0
  3. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/pyproject.toml +57 -32
  4. python_hwpx-5.0.2/src/hwpx/__init__.py +417 -0
  5. python_hwpx-5.0.2/src/hwpx/_document/__init__.py +2 -0
  6. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/_units.py +1 -1
  7. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/fields.py +1 -1
  8. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/layout.py +1 -1
  9. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/media.py +1 -1
  10. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/memos.py +1 -1
  11. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/persistence.py +1 -1
  12. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/shapes.py +14 -3
  13. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/_document/tracked.py +53 -9
  14. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/body_patch.py +3 -3
  15. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/document.py +35 -3
  16. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/errors.py +9 -2
  17. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/engine.py +2 -2
  18. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/measure.py +1 -1
  19. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/opc/package.py +51 -11
  20. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/body.py +56 -0
  21. python_hwpx-5.0.2/src/hwpx/oxml/color.py +60 -0
  22. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/objects.py +140 -14
  23. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/paragraph.py +95 -4
  24. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/patch.py +22 -0
  25. python_hwpx-5.0.2/src/hwpx/quality/rendering.py +136 -0
  26. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/quality/report.py +1 -1
  27. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/quality/save_pipeline.py +12 -10
  28. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/table_patch.py +77 -15
  29. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/__init__.py +19 -63
  30. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/doc_diff.py +0 -39
  31. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/exporter.py +20 -7
  32. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/mail_merge.py +16 -12
  33. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/page_guard.py +11 -12
  34. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/read_fidelity.py +1 -1
  35. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/redline.py +81 -42
  36. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/template_analyzer.py +0 -46
  37. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/toc_author.py +1 -1
  38. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/toc_fidelity.py +45 -7
  39. python_hwpx-5.0.2/src/python_hwpx.egg-info/PKG-INFO +238 -0
  40. python_hwpx-5.0.2/src/python_hwpx.egg-info/SOURCES.txt +210 -0
  41. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/python_hwpx.egg-info/entry_points.txt +0 -2
  42. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/python_hwpx.egg-info/requires.txt +2 -3
  43. python_hwpx-5.0.2/tests/test_add_control.py +71 -0
  44. python_hwpx-5.0.2/tests/test_adoption_metrics_snapshot.py +90 -0
  45. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_authoring_quality_corpus.py +11 -0
  46. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_blind_eval_fixture.py +1 -1
  47. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_corpus_read_fidelity.py +1 -1
  48. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_doc_diff.py +0 -31
  49. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_document_facade_surface.py +1 -1
  50. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_document_formatting.py +6 -2
  51. python_hwpx-5.0.2/tests/test_document_ops_runtime_boundary.py +204 -0
  52. python_hwpx-5.0.2/tests/test_documentation_code_fences.py +464 -0
  53. python_hwpx-5.0.2/tests/test_error_messages.py +57 -0
  54. python_hwpx-5.0.2/tests/test_evalplan_core_primitives.py +71 -0
  55. python_hwpx-5.0.2/tests/test_form_fill_core_primitives.py +38 -0
  56. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_form_fit.py +3 -3
  57. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_form_fit_integration.py +1 -1
  58. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_fuzz_catalog_derive_expected.py +13 -2
  59. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_fuzz_loop.py +14 -3
  60. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_fuzz_regressions.py +12 -1
  61. python_hwpx-5.0.2/tests/test_golden_api.py +286 -0
  62. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_layout_cache_scope.py +1 -1
  63. python_hwpx-5.0.2/tests/test_library_boundary_closure.py +195 -0
  64. python_hwpx-5.0.2/tests/test_llms_txt.py +62 -0
  65. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_mail_merge_xlsx.py +1 -3
  66. python_hwpx-5.0.2/tests/test_moved_surface_hints.py +66 -0
  67. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_mutation_report.py +1 -1
  68. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_new_features.py +76 -7
  69. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_opc_package.py +39 -0
  70. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_oxml_modularization.py +12 -23
  71. python_hwpx-5.0.2/tests/test_product_boundary.py +842 -0
  72. python_hwpx-5.0.2/tests/test_public_artifact_hygiene.py +548 -0
  73. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_read_fidelity.py +1 -1
  74. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_redline_authoring.py +126 -0
  75. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_redline_verify.py +43 -15
  76. python_hwpx-5.0.2/tests/test_release_workflow_safety.py +1164 -0
  77. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_save_pipeline.py +40 -18
  78. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_section_headers.py +3 -3
  79. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_set_paragraph_format_keep.py +1 -1
  80. python_hwpx-5.0.2/tests/test_shape_geometry_contract.py +322 -0
  81. python_hwpx-5.0.2/tests/test_stable_surface.py +354 -0
  82. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_table_patch.py +4 -4
  83. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_table_patch_m105.py +2 -2
  84. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_template_analyzer_enrichment.py +4 -18
  85. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_toc_author.py +1 -1
  86. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_toc_fidelity.py +64 -19
  87. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_version_metadata.py +1 -1
  88. python_hwpx-4.2.0/PKG-INFO +0 -182
  89. python_hwpx-4.2.0/README.md +0 -133
  90. python_hwpx-4.2.0/src/hwpx/__init__.py +0 -272
  91. python_hwpx-4.2.0/src/hwpx/_document/__init__.py +0 -2
  92. python_hwpx-4.2.0/src/hwpx/agent/__init__.py +0 -125
  93. python_hwpx-4.2.0/src/hwpx/agent/_batch_verification.py +0 -383
  94. python_hwpx-4.2.0/src/hwpx/agent/blueprint/__init__.py +0 -58
  95. python_hwpx-4.2.0/src/hwpx/agent/blueprint/bundle.py +0 -282
  96. python_hwpx-4.2.0/src/hwpx/agent/blueprint/catalog.py +0 -136
  97. python_hwpx-4.2.0/src/hwpx/agent/blueprint/dump.py +0 -520
  98. python_hwpx-4.2.0/src/hwpx/agent/blueprint/mapping.py +0 -312
  99. python_hwpx-4.2.0/src/hwpx/agent/blueprint/model.py +0 -722
  100. python_hwpx-4.2.0/src/hwpx/agent/blueprint/native.py +0 -621
  101. python_hwpx-4.2.0/src/hwpx/agent/blueprint/replay.py +0 -622
  102. python_hwpx-4.2.0/src/hwpx/agent/catalog.py +0 -252
  103. python_hwpx-4.2.0/src/hwpx/agent/cli.py +0 -631
  104. python_hwpx-4.2.0/src/hwpx/agent/commands.py +0 -1383
  105. python_hwpx-4.2.0/src/hwpx/agent/document.py +0 -801
  106. python_hwpx-4.2.0/src/hwpx/agent/form_plan.py +0 -1760
  107. python_hwpx-4.2.0/src/hwpx/agent/model.py +0 -808
  108. python_hwpx-4.2.0/src/hwpx/agent/path.py +0 -155
  109. python_hwpx-4.2.0/src/hwpx/agent/query.py +0 -230
  110. python_hwpx-4.2.0/src/hwpx/agent/story.py +0 -207
  111. python_hwpx-4.2.0/src/hwpx/authoring.py +0 -3483
  112. python_hwpx-4.2.0/src/hwpx/benchmark/__init__.py +0 -25
  113. python_hwpx-4.2.0/src/hwpx/benchmark/blind_eval.py +0 -261
  114. python_hwpx-4.2.0/src/hwpx/builder/__init__.py +0 -52
  115. python_hwpx-4.2.0/src/hwpx/builder/core.py +0 -992
  116. python_hwpx-4.2.0/src/hwpx/builder/report.py +0 -195
  117. python_hwpx-4.2.0/src/hwpx/conformance/__init__.py +0 -54
  118. python_hwpx-4.2.0/src/hwpx/conformance/badges.py +0 -198
  119. python_hwpx-4.2.0/src/hwpx/conformance/corpus/corpus.json +0 -53
  120. python_hwpx-4.2.0/src/hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
  121. python_hwpx-4.2.0/src/hwpx/conformance/corpus/notice.hwpx +0 -0
  122. python_hwpx-4.2.0/src/hwpx/conformance/corpus/report_table.hwpx +0 -0
  123. python_hwpx-4.2.0/src/hwpx/conformance/corpus.py +0 -260
  124. python_hwpx-4.2.0/src/hwpx/conformance/report.py +0 -223
  125. python_hwpx-4.2.0/src/hwpx/conformance/roundtrip_batch.py +0 -171
  126. python_hwpx-4.2.0/src/hwpx/conformance/runner.py +0 -395
  127. python_hwpx-4.2.0/src/hwpx/design/__init__.py +0 -30
  128. python_hwpx-4.2.0/src/hwpx/design/_support.py +0 -144
  129. python_hwpx-4.2.0/src/hwpx/design/composer.py +0 -282
  130. python_hwpx-4.2.0/src/hwpx/design/harvest.py +0 -305
  131. python_hwpx-4.2.0/src/hwpx/design/plan.py +0 -69
  132. python_hwpx-4.2.0/src/hwpx/design/profile.py +0 -88
  133. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/fragments/body.xml +0 -1
  134. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/fragments/heading.xml +0 -1
  135. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/fragments/info_table.xml +0 -1
  136. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/fragments/title.xml +0 -1
  137. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/profile.json +0 -25
  138. python_hwpx-4.2.0/src/hwpx/design/profiles/application_form/template.hwpx +0 -0
  139. python_hwpx-4.2.0/src/hwpx/design/profiles/home_notice/fragments/body.xml +0 -1
  140. python_hwpx-4.2.0/src/hwpx/design/profiles/home_notice/fragments/heading.xml +0 -1
  141. python_hwpx-4.2.0/src/hwpx/design/profiles/home_notice/fragments/title.xml +0 -1
  142. python_hwpx-4.2.0/src/hwpx/design/profiles/home_notice/profile.json +0 -24
  143. python_hwpx-4.2.0/src/hwpx/design/profiles/home_notice/template.hwpx +0 -0
  144. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/fragments/body.xml +0 -1
  145. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/fragments/heading.xml +0 -1
  146. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -1
  147. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/fragments/title.xml +0 -1
  148. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/profile.json +0 -25
  149. python_hwpx-4.2.0/src/hwpx/design/profiles/official_notice/template.hwpx +0 -0
  150. python_hwpx-4.2.0/src/hwpx/design/profiles/report/fragments/body.xml +0 -1
  151. python_hwpx-4.2.0/src/hwpx/design/profiles/report/fragments/heading.xml +0 -1
  152. python_hwpx-4.2.0/src/hwpx/design/profiles/report/fragments/info_table.xml +0 -1
  153. python_hwpx-4.2.0/src/hwpx/design/profiles/report/fragments/title.xml +0 -1
  154. python_hwpx-4.2.0/src/hwpx/design/profiles/report/profile.json +0 -25
  155. python_hwpx-4.2.0/src/hwpx/design/profiles/report/template.hwpx +0 -0
  156. python_hwpx-4.2.0/src/hwpx/design/validator.py +0 -107
  157. python_hwpx-4.2.0/src/hwpx/evalplan_fill.py +0 -2727
  158. python_hwpx-4.2.0/src/hwpx/exam/__init__.py +0 -22
  159. python_hwpx-4.2.0/src/hwpx/exam/compose.py +0 -237
  160. python_hwpx-4.2.0/src/hwpx/exam/ir.py +0 -41
  161. python_hwpx-4.2.0/src/hwpx/exam/measure.py +0 -147
  162. python_hwpx-4.2.0/src/hwpx/exam/parser.py +0 -145
  163. python_hwpx-4.2.0/src/hwpx/exam/profile.py +0 -116
  164. python_hwpx-4.2.0/src/hwpx/fill_residue.py +0 -200
  165. python_hwpx-4.2.0/src/hwpx/form_fill.py +0 -333
  166. python_hwpx-4.2.0/src/hwpx/form_fit/seal.py +0 -450
  167. python_hwpx-4.2.0/src/hwpx/form_fit/wordbox.py +0 -1238
  168. python_hwpx-4.2.0/src/hwpx/formfill_quality.py +0 -822
  169. python_hwpx-4.2.0/src/hwpx/guidance_scan.py +0 -616
  170. python_hwpx-4.2.0/src/hwpx/presets/__init__.py +0 -22
  171. python_hwpx-4.2.0/src/hwpx/presets/proposal.py +0 -538
  172. python_hwpx-4.2.0/src/hwpx/template_formfit.py +0 -627
  173. python_hwpx-4.2.0/src/hwpx/tools/advanced_generators.py +0 -154
  174. python_hwpx-4.2.0/src/hwpx/tools/fuzz/__init__.py +0 -36
  175. python_hwpx-4.2.0/src/hwpx/tools/fuzz/__main__.py +0 -80
  176. python_hwpx-4.2.0/src/hwpx/tools/fuzz/catalog.py +0 -320
  177. python_hwpx-4.2.0/src/hwpx/tools/fuzz/generator.py +0 -195
  178. python_hwpx-4.2.0/src/hwpx/tools/fuzz/minimize.py +0 -33
  179. python_hwpx-4.2.0/src/hwpx/tools/fuzz/runner.py +0 -502
  180. python_hwpx-4.2.0/src/hwpx/tools/official_lint.py +0 -478
  181. python_hwpx-4.2.0/src/hwpx/tools/pii.py +0 -389
  182. python_hwpx-4.2.0/src/hwpx/tools/report_parser.py +0 -135
  183. python_hwpx-4.2.0/src/hwpx/tools/style_profile.py +0 -437
  184. python_hwpx-4.2.0/src/hwpx/tools/table_compute.py +0 -477
  185. python_hwpx-4.2.0/src/hwpx/visual/__init__.py +0 -115
  186. python_hwpx-4.2.0/src/hwpx/visual/_hancom_open_rate.ps1 +0 -374
  187. python_hwpx-4.2.0/src/hwpx/visual/_refresh_hwpx_mac.applescript +0 -162
  188. python_hwpx-4.2.0/src/hwpx/visual/_render_hwpx.ps1 +0 -72
  189. python_hwpx-4.2.0/src/hwpx/visual/_render_hwpx_mac.applescript +0 -249
  190. python_hwpx-4.2.0/src/hwpx/visual/detectors.py +0 -151
  191. python_hwpx-4.2.0/src/hwpx/visual/diff.py +0 -153
  192. python_hwpx-4.2.0/src/hwpx/visual/fixture_corpus.py +0 -210
  193. python_hwpx-4.2.0/src/hwpx/visual/hancom_worker.py +0 -287
  194. python_hwpx-4.2.0/src/hwpx/visual/masks.py +0 -51
  195. python_hwpx-4.2.0/src/hwpx/visual/oracle.py +0 -942
  196. python_hwpx-4.2.0/src/hwpx/visual/page_qa.py +0 -244
  197. python_hwpx-4.2.0/src/hwpx/visual/qa_contracts.py +0 -293
  198. python_hwpx-4.2.0/src/hwpx/visual/qa_metrics.py +0 -236
  199. python_hwpx-4.2.0/src/hwpx/visual/report.py +0 -47
  200. python_hwpx-4.2.0/src/python_hwpx.egg-info/PKG-INFO +0 -182
  201. python_hwpx-4.2.0/src/python_hwpx.egg-info/SOURCES.txt +0 -376
  202. python_hwpx-4.2.0/tests/test_advanced_generators.py +0 -117
  203. python_hwpx-4.2.0/tests/test_agent_blueprint_contracts.py +0 -191
  204. python_hwpx-4.2.0/tests/test_agent_blueprint_dump.py +0 -321
  205. python_hwpx-4.2.0/tests/test_agent_blueprint_replay.py +0 -500
  206. python_hwpx-4.2.0/tests/test_agent_catalog.py +0 -38
  207. python_hwpx-4.2.0/tests/test_agent_cli.py +0 -525
  208. python_hwpx-4.2.0/tests/test_agent_commands.py +0 -1114
  209. python_hwpx-4.2.0/tests/test_agent_contracts.py +0 -239
  210. python_hwpx-4.2.0/tests/test_agent_document.py +0 -201
  211. python_hwpx-4.2.0/tests/test_agent_mixed_form.py +0 -726
  212. python_hwpx-4.2.0/tests/test_agent_path_query.py +0 -128
  213. python_hwpx-4.2.0/tests/test_agent_story_commands.py +0 -602
  214. python_hwpx-4.2.0/tests/test_authoring_native_toc.py +0 -240
  215. python_hwpx-4.2.0/tests/test_authoring_profile_routing.py +0 -148
  216. python_hwpx-4.2.0/tests/test_authoring_profile_routing_oracle.py +0 -84
  217. python_hwpx-4.2.0/tests/test_authoring_render_check.py +0 -56
  218. python_hwpx-4.2.0/tests/test_authoring_v2_block_validation.py +0 -180
  219. python_hwpx-4.2.0/tests/test_body_patch.py +0 -222
  220. python_hwpx-4.2.0/tests/test_builder_core.py +0 -945
  221. python_hwpx-4.2.0/tests/test_builder_plan_v2.py +0 -308
  222. python_hwpx-4.2.0/tests/test_builder_vertical_slice.py +0 -165
  223. python_hwpx-4.2.0/tests/test_conformance.py +0 -423
  224. python_hwpx-4.2.0/tests/test_corpus_toc_verify.py +0 -452
  225. python_hwpx-4.2.0/tests/test_design_builder.py +0 -316
  226. python_hwpx-4.2.0/tests/test_document_plan.py +0 -824
  227. python_hwpx-4.2.0/tests/test_document_plan_computed_fields.py +0 -137
  228. python_hwpx-4.2.0/tests/test_evalplan_fill.py +0 -1166
  229. python_hwpx-4.2.0/tests/test_exam_compose.py +0 -85
  230. python_hwpx-4.2.0/tests/test_exam_compose_oracle.py +0 -39
  231. python_hwpx-4.2.0/tests/test_exam_fixtures.py +0 -20
  232. python_hwpx-4.2.0/tests/test_exam_ir.py +0 -19
  233. python_hwpx-4.2.0/tests/test_exam_measure.py +0 -46
  234. python_hwpx-4.2.0/tests/test_exam_parser.py +0 -74
  235. python_hwpx-4.2.0/tests/test_exam_profile.py +0 -39
  236. python_hwpx-4.2.0/tests/test_fill_residue.py +0 -71
  237. python_hwpx-4.2.0/tests/test_form_fill_split_run.py +0 -301
  238. python_hwpx-4.2.0/tests/test_form_fit_seal.py +0 -125
  239. python_hwpx-4.2.0/tests/test_form_fit_seal_placement.py +0 -378
  240. python_hwpx-4.2.0/tests/test_form_fit_wordbox.py +0 -1144
  241. python_hwpx-4.2.0/tests/test_formfill_differential_driver.py +0 -482
  242. python_hwpx-4.2.0/tests/test_formfill_quality.py +0 -216
  243. python_hwpx-4.2.0/tests/test_government_report_preset.py +0 -121
  244. python_hwpx-4.2.0/tests/test_government_table_profile.py +0 -78
  245. python_hwpx-4.2.0/tests/test_guidance_scan.py +0 -195
  246. python_hwpx-4.2.0/tests/test_hancom_render_worker.py +0 -140
  247. python_hwpx-4.2.0/tests/test_id_integrity.py +0 -93
  248. python_hwpx-4.2.0/tests/test_kordoc_absorption.py +0 -288
  249. python_hwpx-4.2.0/tests/test_m9_p0_spike_tools.py +0 -109
  250. python_hwpx-4.2.0/tests/test_mail_merge_fit.py +0 -129
  251. python_hwpx-4.2.0/tests/test_mail_merge_table_compute.py +0 -143
  252. python_hwpx-4.2.0/tests/test_mixed_form_plan.py +0 -426
  253. python_hwpx-4.2.0/tests/test_mutation_contract_unification.py +0 -104
  254. python_hwpx-4.2.0/tests/test_mutation_report_projections.py +0 -318
  255. python_hwpx-4.2.0/tests/test_official_document_style.py +0 -134
  256. python_hwpx-4.2.0/tests/test_official_lint_gongmun_gate.py +0 -60
  257. python_hwpx-4.2.0/tests/test_official_lint_tableaware.py +0 -21
  258. python_hwpx-4.2.0/tests/test_openrate.py +0 -1075
  259. python_hwpx-4.2.0/tests/test_pii.py +0 -96
  260. python_hwpx-4.2.0/tests/test_pii_leak_sweep.py +0 -280
  261. python_hwpx-4.2.0/tests/test_pii_structural.py +0 -149
  262. python_hwpx-4.2.0/tests/test_pii_wiring.py +0 -103
  263. python_hwpx-4.2.0/tests/test_proposal_preset.py +0 -47
  264. python_hwpx-4.2.0/tests/test_question_split_detector.py +0 -27
  265. python_hwpx-4.2.0/tests/test_report_parser.py +0 -79
  266. python_hwpx-4.2.0/tests/test_rhwp_t1_gates.py +0 -276
  267. python_hwpx-4.2.0/tests/test_rhwp_t2_verification.py +0 -258
  268. python_hwpx-4.2.0/tests/test_save_pipeline_no_bypass.py +0 -152
  269. python_hwpx-4.2.0/tests/test_schema_freeze.py +0 -219
  270. python_hwpx-4.2.0/tests/test_stable_surface.py +0 -145
  271. python_hwpx-4.2.0/tests/test_style_profile.py +0 -114
  272. python_hwpx-4.2.0/tests/test_template_formfit.py +0 -404
  273. python_hwpx-4.2.0/tests/test_visual_fixture_corpus.py +0 -88
  274. python_hwpx-4.2.0/tests/test_visual_oracle.py +0 -312
  275. python_hwpx-4.2.0/tests/test_visual_oracle_budget.py +0 -207
  276. python_hwpx-4.2.0/tests/test_visual_qa_contracts.py +0 -102
  277. python_hwpx-4.2.0/tests/test_visual_qa_metrics.py +0 -89
  278. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/LICENSE +0 -0
  279. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/NOTICE +0 -0
  280. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/setup.cfg +0 -0
  281. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/data/Skeleton.hwpx +0 -0
  282. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/equation/__init__.py +0 -0
  283. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/equation/eqedit.py +0 -0
  284. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/equation/mathml.py +0 -0
  285. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/equation/render.py +0 -0
  286. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/equation/tokens.py +0 -0
  287. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/experimental.py +0 -0
  288. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/__init__.py +0 -0
  289. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/apply.py +0 -0
  290. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/policy.py +0 -0
  291. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/form_fit/report.py +0 -0
  292. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/ingest/__init__.py +0 -0
  293. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/ingest/base.py +0 -0
  294. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/ingest/hwpx_converter.py +0 -0
  295. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/layout/__init__.py +0 -0
  296. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/layout/lint.py +0 -0
  297. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/layout/report.py +0 -0
  298. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/mutation_report.py +0 -0
  299. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/opc/relationships.py +0 -0
  300. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/opc/security.py +0 -0
  301. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/opc/xml_utils.py +0 -0
  302. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/__init__.py +0 -0
  303. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/_document_impl.py +0 -0
  304. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/_document_primitives.py +0 -0
  305. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/canonical_defaults.py +0 -0
  306. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/common.py +0 -0
  307. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/document.py +0 -0
  308. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/document_parts.py +0 -0
  309. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/header.py +0 -0
  310. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/header_part.py +0 -0
  311. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/memo.py +0 -0
  312. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/namespaces.py +0 -0
  313. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/numbering.py +0 -0
  314. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/parser.py +0 -0
  315. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/run.py +0 -0
  316. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/schema.py +0 -0
  317. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/section.py +0 -0
  318. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/section_format.py +0 -0
  319. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/section_story.py +0 -0
  320. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/simple_parts.py +0 -0
  321. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/table.py +0 -0
  322. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/oxml/utils.py +0 -0
  323. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/package.py +0 -0
  324. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/py.typed +0 -0
  325. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/quality/__init__.py +0 -0
  326. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/quality/ledger.py +0 -0
  327. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/quality/policy.py +0 -0
  328. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/templates.py +0 -0
  329. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/_schemas/header.xsd +0 -0
  330. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/_schemas/section.xsd +0 -0
  331. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/archive_cli.py +0 -0
  332. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/document_viewer.py +0 -0
  333. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/generic_inventory.py +0 -0
  334. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/id_integrity.py +0 -0
  335. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/idempotence.py +0 -0
  336. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/ir_equality.py +0 -0
  337. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/layout_preview.py +0 -0
  338. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/markdown_export.py +0 -0
  339. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/object_finder.py +0 -0
  340. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/package_reconcile.py +0 -0
  341. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/package_validator.py +0 -0
  342. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/recover.py +0 -0
  343. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/repair.py +0 -0
  344. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/report_utils.py +0 -0
  345. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/roundtrip_diff.py +0 -0
  346. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/table_cleanup.py +0 -0
  347. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/table_navigation.py +0 -0
  348. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/text_extract_cli.py +0 -0
  349. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/text_extractor.py +0 -0
  350. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/hwpx/tools/validator.py +0 -0
  351. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
  352. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/src/python_hwpx.egg-info/top_level.txt +0 -0
  353. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_byte_patch_identity.py +0 -0
  354. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_cell_line_spacing.py +0 -0
  355. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_comment_node_robustness.py +0 -0
  356. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_coverage_promotion.py +0 -0
  357. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_coverage_targets.py +0 -0
  358. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_deviations_registry.py +0 -0
  359. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_document_context_manager.py +0 -0
  360. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_document_save_api.py +0 -0
  361. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_document_viewer.py +0 -0
  362. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_equation_converter.py +0 -0
  363. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_equation_render.py +0 -0
  364. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_existing_document_format_editing.py +0 -0
  365. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_form_fields.py +0 -0
  366. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_gap_closure_tools.py +0 -0
  367. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_hp_tab_support.py +0 -0
  368. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_hwpxlib_corpus_read.py +0 -0
  369. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_id_generator_range.py +0 -0
  370. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_image_object_workflow.py +0 -0
  371. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_ingest.py +0 -0
  372. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_inline_models.py +0 -0
  373. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_integration_hwpx_compatibility.py +0 -0
  374. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_integration_roundtrip.py +0 -0
  375. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_layout_lint.py +0 -0
  376. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_layout_preview.py +0 -0
  377. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_markdown_export.py +0 -0
  378. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_memo_and_style_editing.py +0 -0
  379. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_namespace_handling.py +0 -0
  380. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_open_safety_corpus.py +0 -0
  381. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_oxml_parsing.py +0 -0
  382. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_packaging_license_metadata.py +0 -0
  383. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_packaging_py_typed.py +0 -0
  384. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_paragraph_keep_together.py +0 -0
  385. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_paragraph_section_management.py +0 -0
  386. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_public_runtime_boundary.py +0 -0
  387. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_recover_broken_zip.py +0 -0
  388. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_repair_repack.py +0 -0
  389. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_report_utils.py +0 -0
  390. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_repr_snapshots.py +0 -0
  391. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_roundtrip_fidelity.py +0 -0
  392. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_row_heights.py +0 -0
  393. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_skeleton_template_ids.py +0 -0
  394. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_split_cell.py +0 -0
  395. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_split_merged_cell.py +0 -0
  396. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_table_cleanup.py +0 -0
  397. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_table_navigation.py +0 -0
  398. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_table_patch_dryrun.py +0 -0
  399. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_tables_default_border.py +0 -0
  400. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_text_extractor_annotations.py +0 -0
  401. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_validation_severity.py +0 -0
  402. {python_hwpx-4.2.0 → python_hwpx-5.0.2}/tests/test_validator_comment_nodes.py +0 -0
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-hwpx
3
+ Version: 5.0.2
4
+ Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리
5
+ Author: python-hwpx Maintainers
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/airmang/python-hwpx
8
+ Project-URL: Documentation, https://airmang.github.io/python-hwpx/
9
+ Project-URL: Issues, https://github.com/airmang/python-hwpx/issues
10
+ Keywords: hwp,hwpx,hancom,opc,xml,document-automation,validation,template
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Classifier: Topic :: Text Processing :: Markup :: XML
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ License-File: NOTICE
26
+ Requires-Dist: lxml<7,>=4.9
27
+ Provides-Extra: visual
28
+ Provides-Extra: xlsx
29
+ Requires-Dist: openpyxl>=3.1; extra == "xlsx"
30
+ Provides-Extra: preview
31
+ Requires-Dist: latex2mathml>=3.77; extra == "preview"
32
+ Provides-Extra: dev
33
+ Requires-Dist: build>=1.0; extra == "dev"
34
+ Requires-Dist: twine>=4.0; extra == "dev"
35
+ Requires-Dist: pytest>=7.4; extra == "dev"
36
+ Requires-Dist: latex2mathml>=3.77; extra == "dev"
37
+ Provides-Extra: test
38
+ Requires-Dist: build>=1.0; extra == "test"
39
+ Requires-Dist: numpy>=1.26; extra == "test"
40
+ Requires-Dist: openpyxl>=3.1; extra == "test"
41
+ Requires-Dist: pillow>=10.0; extra == "test"
42
+ Requires-Dist: pytest>=7.4; extra == "test"
43
+ Requires-Dist: pytest-cov>=5.0; extra == "test"
44
+ Requires-Dist: pyyaml<7,>=6.0; extra == "test"
45
+ Requires-Dist: ruff>=0.12; extra == "test"
46
+ Requires-Dist: latex2mathml>=3.77; extra == "test"
47
+ Provides-Extra: typecheck
48
+ Requires-Dist: mypy>=1.10; extra == "typecheck"
49
+ Requires-Dist: pyright>=1.1.390; extra == "typecheck"
50
+ Dynamic: license-file
51
+
52
+ <p align="center">
53
+ <h1 align="center">python-hwpx</h1>
54
+ <p align="center">
55
+ <strong>한컴 없이 HWPX를 읽고, 고치고, 만드는 순수 파이썬 라이브러리</strong>
56
+ </p>
57
+ <p align="center">
58
+ <a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/v/python-hwpx?color=blue&label=PyPI" alt="PyPI"></a>
59
+ <a href="https://pepy.tech/project/python-hwpx"><img src="https://static.pepy.tech/badge/python-hwpx/month" alt="Downloads"></a>
60
+ <a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
61
+ <a href="https://github.com/airmang/python-hwpx/actions/workflows/tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/airmang/python-hwpx/tests.yml?branch=main&label=tests" alt="Tests"></a>
62
+ <a href="https://airmang.github.io/python-hwpx/corpus-metrics.html"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fairmang.github.io%2Fpython-hwpx%2F_static%2Fbadge-hancom-open.json" alt="Hancom open"></a>
63
+ <a href="https://github.com/airmang/python-hwpx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
64
+ </p>
65
+ </p>
66
+
67
+ <p align="center">한국어 | <a href="README_EN.md">English</a></p>
68
+
69
+ 한컴오피스가 없어도 됩니다. HWPX는 ZIP+XML(OWPML) 포맷이라 순수 파이썬만으로
70
+ 읽고, 고치고, 새로 만들 수 있습니다 — Windows·macOS·Linux·CI, 그리고
71
+ **파이썬이 도는 ChatGPT 채팅 안에서도** 그대로 동작합니다. 기존 문서는 손댄
72
+ 곳만 바뀌고 나머지는 바이트 그대로 유지되며, 새 문서는 실제 한컴오피스가 여는
73
+ 형태로 만들어집니다.
74
+
75
+ <p align="center">
76
+ <img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/assets/chatgpt-formfill.png" width="760" alt="일반 ChatGPT 대화에 hwpx 양식을 올려 지도안 작성을 부탁하고, 양식이 유지된 채 채워진 문서를 돌려받는 화면">
77
+ </p>
78
+ <p align="center"><sub>일반 ChatGPT 대화 — 양식 <code>.hwpx</code>를 올리고 말로 부탁하면, 서식을 유지한 채 채워진 문서가 돌아옵니다.</sub></p>
79
+
80
+ **ChatGPT에서 그대로 따라 하기** — 문서를 올리면서 이렇게 부탁하면 됩니다:
81
+
82
+ ```text
83
+ 이 .hwpx 파일을 python-hwpx 라이브러리로 열어서 작업해줘.
84
+ (pip install python-hwpx 로 설치하면 돼)
85
+ 양식과 서식은 그대로 두고, ○○만 바꿔서 새 파일로 돌려줘.
86
+ ```
87
+
88
+ 설치부터 결과 파일까지 대화 안에서 끝납니다 — 내 컴퓨터에 파이썬이 없어도 됩니다.
89
+ AI 도구가 정확한 API를 배우도록 [llms.txt](https://airmang.github.io/python-hwpx/llms.txt)도 제공합니다.
90
+
91
+ | | 저장소 | 역할 |
92
+ |---|---|---|
93
+ | 📦 | [`python-hwpx`](https://github.com/airmang/python-hwpx) | HWPX 문서를 읽고·고치고·만드는 순수 파이썬 엔진 |
94
+ | 🔌 | [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation) | 저작·양식 채움 워크플로, `hwpx` CLI, 선택형 MCP 서버 |
95
+ | 🎯 | [`hwpx-plugins`](https://github.com/airmang/hwpx-plugins) | 에이전트가 알맞은 도구를 고르도록 돕는 플러그인/스킬 번들 |
96
+
97
+ ## 시작하기
98
+
99
+ ```bash
100
+ pip install python-hwpx # Python 3.10+
101
+ ```
102
+
103
+ ```python
104
+ from hwpx import HwpxDocument
105
+
106
+ doc = HwpxDocument.open("보고서.hwpx")
107
+ doc.add_paragraph("자동화로 추가한 문단입니다.")
108
+ doc.save_to_path("보고서-수정.hwpx")
109
+ ```
110
+
111
+ 백지에서 시작할 때는 `HwpxDocument.new()`로 만들어 같은 API로 문단·표·머리말을
112
+ 채우면 됩니다. 문서 저작·양식 채움·시험지 조판 같은 상위 워크플로가 필요하면
113
+ [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)을
114
+ 함께 설치하세요.
115
+
116
+ > 기존 설치 명령 호환을 위해 `pip install "python-hwpx[visual]"`도 계속
117
+ > 동작합니다. 다만 이 extra는 비어 있어 렌더·PDF 의존성을 설치하지 않습니다 —
118
+ > 그 역할은 `python-hwpx-automation[oracle]`이 맡습니다.
119
+
120
+ ## 무엇을 하나
121
+
122
+ - **읽기·추출** — 텍스트/HTML/Markdown 내보내기 (서식·중첩 표·각주 보존)
123
+ - **편집** — 문단·표·이미지·머리글/바닥글·메모·각주, 줄간격·여백·쪽번호 같은 서식
124
+ - **양식 채우기** — 라벨로 셀을 찾아 값만 채우기, 행·열 조정 같은 구조 편집도 바이트 보존으로
125
+ - **생성·일괄 처리** — 새 문서 저작, 목차·상호참조, mail merge, 텍스트 diff, 변경추적(redline)
126
+ - **검증·안전** — 패키지 구조 검증 CLI, 열림 안전 게이트, 모든 저장에 영수증(`MutationReport`)
127
+
128
+ 자세한 내용: [5분 퀵스타트](docs/quickstart.md) · [사용 가이드](docs/usage.md) · [API 레퍼런스](https://airmang.github.io/python-hwpx/) · [예제](docs/examples.md)
129
+
130
+ ### 양식 채우기 — 서식은 그대로, 값만
131
+
132
+ ```python
133
+ doc = HwpxDocument.open("신청서.hwpx")
134
+ result = doc.fill_by_path({
135
+ "성명 > right": "홍길동",
136
+ "소속 > right": "플랫폼팀",
137
+ })
138
+ doc.save_to_path("신청서-작성완료.hwpx")
139
+ ```
140
+
141
+ 라벨 기준으로 셀을 찾아 채우고, 손대지 않은 영역은 원본 바이트가 그대로 유지됩니다.
142
+
143
+ ### 저장에는 영수증이 따라옵니다
144
+
145
+ ```python
146
+ report = doc.save_to_path("결과.hwpx", return_report=True)
147
+ print(report.actual_mode) # "patch" — 문서 재조립 없이 저장됨
148
+ print(report.preservation.untouched_part_payloads.to_dict())
149
+ # {"verified": 17, "changed": 0}
150
+ ```
151
+
152
+ 요청한 보존 등급을 지킬 수 없으면 아무것도 쓰지 않고 실패합니다(fail-closed).
153
+ 전체 규칙: [안전한 쓰기 계약](docs/safe-write-contract.md).
154
+
155
+ *예제 중 **독립 실행 예제** 표시가 없는 블록은 여러분의 기존 문서를 입력으로
156
+ 쓰는 조각입니다. 예제별 Python 블록 판정은
157
+ [실행 ledger](docs/python-example-ledger.json)에 동결돼 있습니다.*
158
+
159
+ ## 실측으로 말합니다
160
+
161
+ 만든 파일이 실제 한컴오피스에서 열리는지, 동결 코퍼스 전수(N=497)를 재서
162
+ 그대로 공개합니다:
163
+
164
+ - **한컴 열림 476/476** — 우리가 만든 파일을 실한컴이 전부 엽니다
165
+ - **미수정 영역 바이트 보존 497/497** · 개인정보 유출 0
166
+ - **렌더 검증 416/476** — 한컴 자체가 PDF 내보내기를 거부한 43건도 숨기지 않고 집계
167
+ - 전체 수치와 주의사항: [실측 코퍼스 메트릭](https://airmang.github.io/python-hwpx/corpus-metrics.html) · 기능별 등급: [지원 매트릭스](docs/support-matrix.md)
168
+
169
+ <p align="center">
170
+ <img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/assets/hancom-open-generated.png" width="760" alt="ChatGPT 실행 환경에서 python-hwpx로 생성한 HWPX 문서를 실제 한컴오피스에서 연 모습">
171
+ </p>
172
+ <p align="center"><sub>ChatGPT 실행 환경에서 생성한 문서를 실제 한컴오피스로 연 모습.</sub></p>
173
+
174
+ 현재 개발 상태는 Alpha입니다 — API는 바뀔 수 있습니다.
175
+
176
+ > 위 수치는 "만든 파일을 실한컴이 받아주는가"라는 축입니다. 문서 파싱 성능과는
177
+ > 다른 축이므로 파서 프로젝트 수치와 직접 비교하지 마세요.
178
+
179
+ ## 어디서 쓰나
180
+
181
+ | 환경 | 무엇을 하면 되나 |
182
+ |---|---|
183
+ | 일반 ChatGPT 대화 | `.hwpx`를 올리고 `pip install python-hwpx` 후 파이썬으로 편집해 되받기 |
184
+ | 로컬·서버 파이썬 | `pip install python-hwpx` — 스크립트·배치·CI |
185
+ | 파이썬 자동화 (MCP 없이) | `pip install python-hwpx-automation` — 저작·양식 채움·검증 워크플로를 그냥 파이썬으로 |
186
+ | ChatGPT MCP 앱 | [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)의 MCP adapter를 커넥터로 등록 |
187
+ | Codex 마켓플레이스 플러그인 | [`hwpx-plugins`](https://github.com/airmang/hwpx-plugins) 설치 |
188
+ | Claude Code · Hermes · OpenClaw | 같은 MCP 서버를 각 클라이언트에 등록 ([`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)) |
189
+
190
+ 실제로 일반 ChatGPT 대화에 `.hwpx`를 올리고 설치를 부탁했을 때 PyPI 설치와
191
+ 편집·되받기까지 동작하는 것을 확인했습니다. 파이썬 실행과 네트워크 허용 범위는
192
+ 플랜·설정에 따라 다르며, 네트워크가 막힌 환경에서는 wheel 파일을 함께 올리면
193
+ 오프라인 설치로 같은 여정이 됩니다.
194
+
195
+ ## 비교
196
+
197
+ | | python-hwpx | pyhwpx | pyhwp |
198
+ |---|---|---|---|
199
+ | **대상 포맷** | `.hwpx` (OWPML/OPC) | `.hwpx` | `.hwp` (v5 바이너리) |
200
+ | **한/글 설치** | 불필요 | 필요 (Windows COM) | 불필요 |
201
+ | **크로스 플랫폼** | ✅ Linux / macOS / Windows / CI | ❌ Windows 전용 | ✅ |
202
+ | **편집/생성 API** | ✅ | ✅ (COM) | ❌ 대부분 읽기 |
203
+ | **AI 에이전트 연동 (MCP)** | ✅ companion 경유 | ❌ | ❌ |
204
+
205
+ > HWP(v5 바이너리)는 지원하지 않습니다. 한컴오피스에서 HWPX로 변환 후 사용하세요.
206
+
207
+ ## 알려진 제약
208
+
209
+ - `add_shape()` / `add_control()`은 저수준 탈출구라, 그대로 저장하면 한/글이
210
+ 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 `add_line()` /
211
+ `add_rectangle()` / `add_ellipse()`를 쓰세요.
212
+ - 그림은 단순 개체 생성까지 지원합니다 (그룹·효과 미지원).
213
+ - 암호화된 HWPX는 지원하지 않습니다.
214
+
215
+ ## 기여하기
216
+
217
+ [help wanted](https://github.com/airmang/python-hwpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) ·
218
+ [로드맵](https://github.com/airmang/python-hwpx/milestones) ·
219
+ [Discussions](https://github.com/airmang/python-hwpx/discussions) ·
220
+ [CONTRIBUTING](CONTRIBUTING.md)
221
+
222
+ HWPX 내부 구조가 처음이라면 [내부 실전 가이드](docs/internals/)부터 — 실제 한/글
223
+ 동작에서 확인된 조판 캐시·목차 필드·OPC 재패킹 같은 실전 지식을 정리해 두었습니다.
224
+ 공개 API의 안정 범위는 [안정 API 표면](docs/stable-api.md), 계층 간 소유권은
225
+ [제품 경계 문서](docs/architecture/product-boundary.md)에 있습니다.
226
+
227
+ ## 감사의 말
228
+
229
+ 아래 공개 표준·프로젝트에 빚지고 있습니다.
230
+
231
+ - **[OWPML — 개방형 워드프로세서 마크업 언어 (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — HWPX가 기반하는 한국 산업 표준
232
+ - **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — OWPML 요소 구조 참조 모델 · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — 오라클 샘플 코퍼스
233
+ - **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — 멱등성·검증 게이트 설계 영감
234
+ - **범정부오피스** — 공무 문서 편집 워크플로 아이디어
235
+
236
+ ## License · Maintainer
237
+
238
+ Apache-2.0 ([LICENSE](LICENSE) · [NOTICE](NOTICE)) — **Kohkyuhyun** [@airmang](https://github.com/airmang) · [kokyuhyun@hotmail.com](mailto:kokyuhyun@hotmail.com)
@@ -0,0 +1,187 @@
1
+ <p align="center">
2
+ <h1 align="center">python-hwpx</h1>
3
+ <p align="center">
4
+ <strong>한컴 없이 HWPX를 읽고, 고치고, 만드는 순수 파이썬 라이브러리</strong>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/v/python-hwpx?color=blue&label=PyPI" alt="PyPI"></a>
8
+ <a href="https://pepy.tech/project/python-hwpx"><img src="https://static.pepy.tech/badge/python-hwpx/month" alt="Downloads"></a>
9
+ <a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
10
+ <a href="https://github.com/airmang/python-hwpx/actions/workflows/tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/airmang/python-hwpx/tests.yml?branch=main&label=tests" alt="Tests"></a>
11
+ <a href="https://airmang.github.io/python-hwpx/corpus-metrics.html"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fairmang.github.io%2Fpython-hwpx%2F_static%2Fbadge-hancom-open.json" alt="Hancom open"></a>
12
+ <a href="https://github.com/airmang/python-hwpx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
13
+ </p>
14
+ </p>
15
+
16
+ <p align="center">한국어 | <a href="README_EN.md">English</a></p>
17
+
18
+ 한컴오피스가 없어도 됩니다. HWPX는 ZIP+XML(OWPML) 포맷이라 순수 파이썬만으로
19
+ 읽고, 고치고, 새로 만들 수 있습니다 — Windows·macOS·Linux·CI, 그리고
20
+ **파이썬이 도는 ChatGPT 채팅 안에서도** 그대로 동작합니다. 기존 문서는 손댄
21
+ 곳만 바뀌고 나머지는 바이트 그대로 유지되며, 새 문서는 실제 한컴오피스가 여는
22
+ 형태로 만들어집니다.
23
+
24
+ <p align="center">
25
+ <img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/assets/chatgpt-formfill.png" width="760" alt="일반 ChatGPT 대화에 hwpx 양식을 올려 지도안 작성을 부탁하고, 양식이 유지된 채 채워진 문서를 돌려받는 화면">
26
+ </p>
27
+ <p align="center"><sub>일반 ChatGPT 대화 — 양식 <code>.hwpx</code>를 올리고 말로 부탁하면, 서식을 유지한 채 채워진 문서가 돌아옵니다.</sub></p>
28
+
29
+ **ChatGPT에서 그대로 따라 하기** — 문서를 올리면서 이렇게 부탁하면 됩니다:
30
+
31
+ ```text
32
+ 이 .hwpx 파일을 python-hwpx 라이브러리로 열어서 작업해줘.
33
+ (pip install python-hwpx 로 설치하면 돼)
34
+ 양식과 서식은 그대로 두고, ○○만 바꿔서 새 파일로 돌려줘.
35
+ ```
36
+
37
+ 설치부터 결과 파일까지 대화 안에서 끝납니다 — 내 컴퓨터에 파이썬이 없어도 됩니다.
38
+ AI 도구가 정확한 API를 배우도록 [llms.txt](https://airmang.github.io/python-hwpx/llms.txt)도 제공합니다.
39
+
40
+ | | 저장소 | 역할 |
41
+ |---|---|---|
42
+ | 📦 | [`python-hwpx`](https://github.com/airmang/python-hwpx) | HWPX 문서를 읽고·고치고·만드는 순수 파이썬 엔진 |
43
+ | 🔌 | [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation) | 저작·양식 채움 워크플로, `hwpx` CLI, 선택형 MCP 서버 |
44
+ | 🎯 | [`hwpx-plugins`](https://github.com/airmang/hwpx-plugins) | 에이전트가 알맞은 도구를 고르도록 돕는 플러그인/스킬 번들 |
45
+
46
+ ## 시작하기
47
+
48
+ ```bash
49
+ pip install python-hwpx # Python 3.10+
50
+ ```
51
+
52
+ ```python
53
+ from hwpx import HwpxDocument
54
+
55
+ doc = HwpxDocument.open("보고서.hwpx")
56
+ doc.add_paragraph("자동화로 추가한 문단입니다.")
57
+ doc.save_to_path("보고서-수정.hwpx")
58
+ ```
59
+
60
+ 백지에서 시작할 때는 `HwpxDocument.new()`로 만들어 같은 API로 문단·표·머리말을
61
+ 채우면 됩니다. 문서 저작·양식 채움·시험지 조판 같은 상위 워크플로가 필요하면
62
+ [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)을
63
+ 함께 설치하세요.
64
+
65
+ > 기존 설치 명령 호환을 위해 `pip install "python-hwpx[visual]"`도 계속
66
+ > 동작합니다. 다만 이 extra는 비어 있어 렌더·PDF 의존성을 설치하지 않습니다 —
67
+ > 그 역할은 `python-hwpx-automation[oracle]`이 맡습니다.
68
+
69
+ ## 무엇을 하나
70
+
71
+ - **읽기·추출** — 텍스트/HTML/Markdown 내보내기 (서식·중첩 표·각주 보존)
72
+ - **편집** — 문단·표·이미지·머리글/바닥글·메모·각주, 줄간격·여백·쪽번호 같은 서식
73
+ - **양식 채우기** — 라벨로 셀을 찾아 값만 채우기, 행·열 조정 같은 구조 편집도 바이트 보존으로
74
+ - **생성·일괄 처리** — 새 문서 저작, 목차·상호참조, mail merge, 텍스트 diff, 변경추적(redline)
75
+ - **검증·안전** — 패키지 구조 검증 CLI, 열림 안전 게이트, 모든 저장에 영수증(`MutationReport`)
76
+
77
+ 자세한 내용: [5분 퀵스타트](docs/quickstart.md) · [사용 가이드](docs/usage.md) · [API 레퍼런스](https://airmang.github.io/python-hwpx/) · [예제](docs/examples.md)
78
+
79
+ ### 양식 채우기 — 서식은 그대로, 값만
80
+
81
+ ```python
82
+ doc = HwpxDocument.open("신청서.hwpx")
83
+ result = doc.fill_by_path({
84
+ "성명 > right": "홍길동",
85
+ "소속 > right": "플랫폼팀",
86
+ })
87
+ doc.save_to_path("신청서-작성완료.hwpx")
88
+ ```
89
+
90
+ 라벨 기준으로 셀을 찾아 채우고, 손대지 않은 영역은 원본 바이트가 그대로 유지됩니다.
91
+
92
+ ### 저장에는 영수증이 따라옵니다
93
+
94
+ ```python
95
+ report = doc.save_to_path("결과.hwpx", return_report=True)
96
+ print(report.actual_mode) # "patch" — 문서 재조립 없이 저장됨
97
+ print(report.preservation.untouched_part_payloads.to_dict())
98
+ # {"verified": 17, "changed": 0}
99
+ ```
100
+
101
+ 요청한 보존 등급을 지킬 수 없으면 아무것도 쓰지 않고 실패합니다(fail-closed).
102
+ 전체 규칙: [안전한 쓰기 계약](docs/safe-write-contract.md).
103
+
104
+ *예제 중 **독립 실행 예제** 표시가 없는 블록은 여러분의 기존 문서를 입력으로
105
+ 쓰는 조각입니다. 예제별 Python 블록 판정은
106
+ [실행 ledger](docs/python-example-ledger.json)에 동결돼 있습니다.*
107
+
108
+ ## 실측으로 말합니다
109
+
110
+ 만든 파일이 실제 한컴오피스에서 열리는지, 동결 코퍼스 전수(N=497)를 재서
111
+ 그대로 공개합니다:
112
+
113
+ - **한컴 열림 476/476** — 우리가 만든 파일을 실한컴이 전부 엽니다
114
+ - **미수정 영역 바이트 보존 497/497** · 개인정보 유출 0
115
+ - **렌더 검증 416/476** — 한컴 자체가 PDF 내보내기를 거부한 43건도 숨기지 않고 집계
116
+ - 전체 수치와 주의사항: [실측 코퍼스 메트릭](https://airmang.github.io/python-hwpx/corpus-metrics.html) · 기능별 등급: [지원 매트릭스](docs/support-matrix.md)
117
+
118
+ <p align="center">
119
+ <img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/assets/hancom-open-generated.png" width="760" alt="ChatGPT 실행 환경에서 python-hwpx로 생성한 HWPX 문서를 실제 한컴오피스에서 연 모습">
120
+ </p>
121
+ <p align="center"><sub>ChatGPT 실행 환경에서 생성한 문서를 실제 한컴오피스로 연 모습.</sub></p>
122
+
123
+ 현재 개발 상태는 Alpha입니다 — API는 바뀔 수 있습니다.
124
+
125
+ > 위 수치는 "만든 파일을 실한컴이 받아주는가"라는 축입니다. 문서 파싱 성능과는
126
+ > 다른 축이므로 파서 프로젝트 수치와 직접 비교하지 마세요.
127
+
128
+ ## 어디서 쓰나
129
+
130
+ | 환경 | 무엇을 하면 되나 |
131
+ |---|---|
132
+ | 일반 ChatGPT 대화 | `.hwpx`를 올리고 `pip install python-hwpx` 후 파이썬으로 편집해 되받기 |
133
+ | 로컬·서버 파이썬 | `pip install python-hwpx` — 스크립트·배치·CI |
134
+ | 파이썬 자동화 (MCP 없이) | `pip install python-hwpx-automation` — 저작·양식 채움·검증 워크플로를 그냥 파이썬으로 |
135
+ | ChatGPT MCP 앱 | [`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)의 MCP adapter를 커넥터로 등록 |
136
+ | Codex 마켓플레이스 플러그인 | [`hwpx-plugins`](https://github.com/airmang/hwpx-plugins) 설치 |
137
+ | Claude Code · Hermes · OpenClaw | 같은 MCP 서버를 각 클라이언트에 등록 ([`python-hwpx-automation`](https://github.com/airmang/python-hwpx-automation)) |
138
+
139
+ 실제로 일반 ChatGPT 대화에 `.hwpx`를 올리고 설치를 부탁했을 때 PyPI 설치와
140
+ 편집·되받기까지 동작하는 것을 확인했습니다. 파이썬 실행과 네트워크 허용 범위는
141
+ 플랜·설정에 따라 다르며, 네트워크가 막힌 환경에서는 wheel 파일을 함께 올리면
142
+ 오프라인 설치로 같은 여정이 됩니다.
143
+
144
+ ## 비교
145
+
146
+ | | python-hwpx | pyhwpx | pyhwp |
147
+ |---|---|---|---|
148
+ | **대상 포맷** | `.hwpx` (OWPML/OPC) | `.hwpx` | `.hwp` (v5 바이너리) |
149
+ | **한/글 설치** | 불필요 | 필요 (Windows COM) | 불필요 |
150
+ | **크로스 플랫폼** | ✅ Linux / macOS / Windows / CI | ❌ Windows 전용 | ✅ |
151
+ | **편집/생성 API** | ✅ | ✅ (COM) | ❌ 대부분 읽기 |
152
+ | **AI 에이전트 연동 (MCP)** | ✅ companion 경유 | ❌ | ❌ |
153
+
154
+ > HWP(v5 바이너리)는 지원하지 않습니다. 한컴오피스에서 HWPX로 변환 후 사용하세요.
155
+
156
+ ## 알려진 제약
157
+
158
+ - `add_shape()` / `add_control()`은 저수준 탈출구라, 그대로 저장하면 한/글이
159
+ 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 `add_line()` /
160
+ `add_rectangle()` / `add_ellipse()`를 쓰세요.
161
+ - 그림은 단순 개체 생성까지 지원합니다 (그룹·효과 미지원).
162
+ - 암호화된 HWPX는 지원하지 않습니다.
163
+
164
+ ## 기여하기
165
+
166
+ [help wanted](https://github.com/airmang/python-hwpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) ·
167
+ [로드맵](https://github.com/airmang/python-hwpx/milestones) ·
168
+ [Discussions](https://github.com/airmang/python-hwpx/discussions) ·
169
+ [CONTRIBUTING](CONTRIBUTING.md)
170
+
171
+ HWPX 내부 구조가 처음이라면 [내부 실전 가이드](docs/internals/)부터 — 실제 한/글
172
+ 동작에서 확인된 조판 캐시·목차 필드·OPC 재패킹 같은 실전 지식을 정리해 두었습니다.
173
+ 공개 API의 안정 범위는 [안정 API 표면](docs/stable-api.md), 계층 간 소유권은
174
+ [제품 경계 문서](docs/architecture/product-boundary.md)에 있습니다.
175
+
176
+ ## 감사의 말
177
+
178
+ 아래 공개 표준·프로젝트에 빚지고 있습니다.
179
+
180
+ - **[OWPML — 개방형 워드프로세서 마크업 언어 (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — HWPX가 기반하는 한국 산업 표준
181
+ - **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — OWPML 요소 구조 참조 모델 · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — 오라클 샘플 코퍼스
182
+ - **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — 멱등성·검증 게이트 설계 영감
183
+ - **범정부오피스** — 공무 문서 편집 워크플로 아이디어
184
+
185
+ ## License · Maintainer
186
+
187
+ Apache-2.0 ([LICENSE](LICENSE) · [NOTICE](NOTICE)) — **Kohkyuhyun** [@airmang](https://github.com/airmang) · [kokyuhyun@hotmail.com](mailto:kokyuhyun@hotmail.com)
@@ -1,11 +1,11 @@
1
1
  [build-system]
2
- requires = ["setuptools>=77.0.0", "wheel"]
2
+ requires = ["setuptools==83.0.0", "wheel==0.47.0"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx"
7
- version = "4.2.0"
8
- description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리"
7
+ version = "5.0.2"
8
+ description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
11
11
  license-files = ["LICENSE", "NOTICE"]
@@ -21,19 +21,21 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.10",
22
22
  "Programming Language :: Python :: 3.11",
23
23
  "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
24
26
  "Topic :: Software Development :: Libraries",
25
27
  "Topic :: Text Processing :: Markup :: XML",
28
+ "Typing :: Typed",
26
29
  ]
27
30
  dependencies = [
28
31
  "lxml>=4.9,<7",
29
32
  ]
30
33
 
31
34
  [project.optional-dependencies]
32
- visual = [
33
- "pymupdf>=1.24",
34
- "pillow>=10.0",
35
- "numpy>=1.26",
36
- ]
35
+ # 5.0에서 core는 PDF도 이미지도 읽지 않는다. 렌더 백엔드와 word-box 추출은
36
+ # 주입받는 계약이고, 그 스택은 companion 계층이 소유한다. 이름은 기존 설치가
37
+ # `python-hwpx[visual]`로 깨지지 않도록 남기되 아무것도 끌어오지 않는다.
38
+ visual = []
37
39
  # Excel(.xlsx/.xlsm) 명부 ingestion for mail_merge. Lazy-imported; absent => clear error.
38
40
  xlsx = [
39
41
  "openpyxl>=3.1",
@@ -52,9 +54,11 @@ dev = [
52
54
  test = [
53
55
  "build>=1.0",
54
56
  "numpy>=1.26",
57
+ "openpyxl>=3.1",
55
58
  "pillow>=10.0",
56
59
  "pytest>=7.4",
57
60
  "pytest-cov>=5.0",
61
+ "pyyaml>=6.0,<7",
58
62
  "ruff>=0.12",
59
63
  "latex2mathml>=3.77",
60
64
  ]
@@ -69,7 +73,11 @@ Documentation = "https://airmang.github.io/python-hwpx/"
69
73
  Issues = "https://github.com/airmang/python-hwpx/issues"
70
74
 
71
75
  [project.scripts]
72
- hwpx = "hwpx.agent.cli:main"
76
+ # The `hwpx` application CLI moved to python-hwpx-automation, which declares the
77
+ # name in the train that raises its core floor to 5.0 — so no valid install ever
78
+ # has two declarers of it. `hwpx-conformance` is gone too: its package is
79
+ # repository QA and no longer ships, and a console script pointing into an
80
+ # excluded package would install as a command that cannot start.
73
81
  hwpx-unpack = "hwpx.tools.archive_cli:unpack_main"
74
82
  hwpx-pack = "hwpx.tools.archive_cli:pack_main"
75
83
  hwpx-validate = "hwpx.tools.validator:main"
@@ -78,33 +86,52 @@ hwpx-page-guard = "hwpx.tools.page_guard:main"
78
86
  hwpx-analyze-template = "hwpx.tools.template_analyzer:main"
79
87
  hwpx-text-extract = "hwpx.tools.text_extract_cli:main"
80
88
  hwpx-repair = "hwpx.tools.repair:main"
81
- hwpx-conformance = "hwpx.conformance.runner:main"
82
89
 
83
90
  [tool.setuptools]
84
91
  package-dir = { "" = "src" }
85
92
  include-package-data = true
93
+ # Namespace-aware discovery. hwpx/opc and hwpx/data carry no __init__.py, so a
94
+ # plain find_packages misses them, and they only ever shipped because
95
+ # include-package-data swept them in as data files of hwpx. That same sweep kept
96
+ # hwpx/benchmark and hwpx/conformance in the wheel after they were excluded from
97
+ # the package list — one mechanism, helping in one place and hurting in the other.
98
+ # The list is deliberately exact. A wildcard made any new directory below
99
+ # ``src/hwpx`` publishable without a packaging review, which is the opposite of
100
+ # the library/application boundary. Namespace packages (``opc`` and ``data``)
101
+ # are valid explicit entries.
102
+ packages = [
103
+ "hwpx",
104
+ "hwpx._document",
105
+ "hwpx.data",
106
+ "hwpx.equation",
107
+ "hwpx.form_fit",
108
+ "hwpx.ingest",
109
+ "hwpx.layout",
110
+ "hwpx.opc",
111
+ "hwpx.oxml",
112
+ "hwpx.quality",
113
+ "hwpx.tools",
114
+ "hwpx.tools._schemas",
115
+ ]
86
116
 
87
- [tool.setuptools.packages.find]
88
- where = ["src"]
89
- include = ["hwpx*"]
117
+ # Only packages that ship. A key here for an excluded or deleted package pulls it
118
+ # back into the wheel: hwpx.conformance was excluded from packages.find and still
119
+ # shipped because this table named it, and hwpx.design no longer exists at all.
90
120
 
121
+ # Listed explicitly rather than discovered with an exclude pattern. The exclude
122
+ # form silently failed to keep hwpx.benchmark and hwpx.conformance out of the
123
+ # wheel, and a packaging rule that looks right while shipping the opposite is
124
+ # worse than a longer list. Adding a package here is a deliberate act.
91
125
  [tool.setuptools.package-data]
92
126
  "hwpx" = ["py.typed"]
93
127
  "hwpx.tools" = ["_schemas/*.xsd"]
94
128
  "hwpx.data" = ["Skeleton.hwpx"]
95
- "hwpx.visual" = ["_render_hwpx.ps1", "_render_hwpx_mac.applescript", "_refresh_hwpx_mac.applescript", "_hancom_open_rate.ps1"]
96
- "hwpx.design" = [
97
- "profiles/*/profile.json",
98
- "profiles/*/template.hwpx",
99
- "profiles/*/fragments/*.xml",
100
- ]
101
- "hwpx.conformance" = [
102
- "corpus/corpus.json",
103
- "corpus/*.hwpx",
104
- ]
105
129
 
106
130
  [tool.pytest.ini_options]
107
- pythonpath = ["src"]
131
+ # scripts/ carries repository QA harnesses that are not library surface — the
132
+ # fuzz loop moved there in 5.0 because it needs the MCP owner's builder, and
133
+ # core must never import a companion layer.
134
+ pythonpath = ["src", "scripts"]
108
135
  addopts = "-ra"
109
136
  testpaths = ["tests"]
110
137
 
@@ -124,6 +151,8 @@ files = [
124
151
  "src/hwpx/equation/mathml.py",
125
152
  "src/hwpx/equation/render.py",
126
153
  "src/hwpx/tools/document_viewer.py",
154
+ "src/hwpx/tools/mail_merge.py",
155
+ "src/hwpx/tools/redline.py",
127
156
  "src/hwpx/_document/_units.py",
128
157
  "src/hwpx/_document/fields.py",
129
158
  "src/hwpx/_document/layout.py",
@@ -132,9 +161,6 @@ files = [
132
161
  "src/hwpx/_document/persistence.py",
133
162
  "src/hwpx/_document/shapes.py",
134
163
  "src/hwpx/_document/tracked.py",
135
- "src/hwpx/agent/commands.py",
136
- "src/hwpx/agent/document.py",
137
- "src/hwpx/agent/story.py",
138
164
  "src/hwpx/oxml/_document_impl.py",
139
165
  "src/hwpx/oxml/_document_primitives.py",
140
166
  "src/hwpx/oxml/document.py",
@@ -153,6 +179,7 @@ files = [
153
179
  "src/hwpx/oxml/simple_parts.py",
154
180
  "src/hwpx/oxml/table.py",
155
181
  "src/hwpx/quality/policy.py",
182
+ "src/hwpx/quality/rendering.py",
156
183
  "src/hwpx/quality/report.py",
157
184
  "src/hwpx/tools/archive_cli.py",
158
185
  "src/hwpx/tools/package_validator.py",
@@ -160,7 +187,6 @@ files = [
160
187
  "src/hwpx/tools/template_analyzer.py",
161
188
  "src/hwpx/tools/text_extract_cli.py",
162
189
  "src/hwpx/tools/text_extractor.py",
163
- "src/hwpx/visual/oracle.py",
164
190
  "tests/template_automation/helpers.py",
165
191
  "tests/template_automation/generate_fixtures.py",
166
192
  ]
@@ -179,6 +205,8 @@ include = [
179
205
  "src/hwpx/equation/mathml.py",
180
206
  "src/hwpx/equation/render.py",
181
207
  "src/hwpx/tools/document_viewer.py",
208
+ "src/hwpx/tools/mail_merge.py",
209
+ "src/hwpx/tools/redline.py",
182
210
  "src/hwpx/_document/_units.py",
183
211
  "src/hwpx/_document/fields.py",
184
212
  "src/hwpx/_document/layout.py",
@@ -187,9 +215,6 @@ include = [
187
215
  "src/hwpx/_document/persistence.py",
188
216
  "src/hwpx/_document/shapes.py",
189
217
  "src/hwpx/_document/tracked.py",
190
- "src/hwpx/agent/commands.py",
191
- "src/hwpx/agent/document.py",
192
- "src/hwpx/agent/story.py",
193
218
  "src/hwpx/oxml/_document_impl.py",
194
219
  "src/hwpx/oxml/_document_primitives.py",
195
220
  "src/hwpx/oxml/document.py",
@@ -208,6 +233,7 @@ include = [
208
233
  "src/hwpx/oxml/simple_parts.py",
209
234
  "src/hwpx/oxml/table.py",
210
235
  "src/hwpx/quality/policy.py",
236
+ "src/hwpx/quality/rendering.py",
211
237
  "src/hwpx/quality/report.py",
212
238
  "src/hwpx/tools/archive_cli.py",
213
239
  "src/hwpx/tools/package_validator.py",
@@ -215,7 +241,6 @@ include = [
215
241
  "src/hwpx/tools/template_analyzer.py",
216
242
  "src/hwpx/tools/text_extract_cli.py",
217
243
  "src/hwpx/tools/text_extractor.py",
218
- "src/hwpx/visual/oracle.py",
219
244
  "tests/template_automation/helpers.py",
220
245
  "tests/template_automation/generate_fixtures.py",
221
246
  ]