python-hwpx 3.6.0__tar.gz → 3.8.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.
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/NOTICE +13 -1
- {python_hwpx-3.6.0/src/python_hwpx.egg-info → python_hwpx-3.8.0}/PKG-INFO +101 -11
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/README.md +96 -10
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/pyproject.toml +22 -1
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/__init__.py +12 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/persistence.py +196 -15
- python_hwpx-3.8.0/src/hwpx/agent/_batch_verification.py +383 -0
- python_hwpx-3.8.0/src/hwpx/agent/blueprint/replay.py +622 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/commands.py +269 -483
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/form_plan.py +167 -92
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/model.py +56 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/authoring.py +206 -113
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/body_patch.py +16 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/document.py +102 -8
- python_hwpx-3.8.0/src/hwpx/equation/__init__.py +46 -0
- python_hwpx-3.8.0/src/hwpx/equation/eqedit.py +317 -0
- python_hwpx-3.8.0/src/hwpx/equation/mathml.py +69 -0
- python_hwpx-3.8.0/src/hwpx/equation/render.py +83 -0
- python_hwpx-3.8.0/src/hwpx/equation/tokens.py +256 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/evalplan_fill.py +347 -173
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/fill_residue.py +73 -36
- python_hwpx-3.8.0/src/hwpx/mutation_report.py +524 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/opc/package.py +7 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/table.py +216 -128
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/patch.py +24 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/table_patch.py +16 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/__init__.py +8 -0
- python_hwpx-3.8.0/src/hwpx/tools/document_viewer.py +182 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/catalog.py +105 -84
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/layout_preview.py +95 -16
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/markdown_export.py +76 -54
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/object_finder.py +100 -84
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/toc_fidelity.py +87 -56
- python_hwpx-3.8.0/src/hwpx/visual/fixture_corpus.py +210 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0/src/python_hwpx.egg-info}/PKG-INFO +101 -11
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/python_hwpx.egg-info/SOURCES.txt +15 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/python_hwpx.egg-info/requires.txt +5 -0
- python_hwpx-3.8.0/tests/test_authoring_v2_block_validation.py +180 -0
- python_hwpx-3.8.0/tests/test_document_viewer.py +136 -0
- python_hwpx-3.8.0/tests/test_equation_converter.py +139 -0
- python_hwpx-3.8.0/tests/test_equation_render.py +69 -0
- python_hwpx-3.8.0/tests/test_fuzz_catalog_derive_expected.py +162 -0
- python_hwpx-3.8.0/tests/test_mutation_report.py +323 -0
- python_hwpx-3.8.0/tests/test_mutation_report_projections.py +318 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_oxml_modularization.py +5 -1
- python_hwpx-3.6.0/src/hwpx/agent/blueprint/replay.py +0 -452
- python_hwpx-3.6.0/src/hwpx/visual/fixture_corpus.py +0 -175
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/LICENSE +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/setup.cfg +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/_units.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/fields.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/layout.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/media.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/memos.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/shapes.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/_document/tracked.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/bundle.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/catalog.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/dump.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/mapping.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/model.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/blueprint/native.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/catalog.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/cli.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/document.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/path.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/query.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/agent/story.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/benchmark/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/benchmark/blind_eval.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/builder/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/builder/core.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/builder/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/badges.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/corpus/corpus.json +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/corpus/notice.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/corpus/report_table.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/corpus.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/roundtrip_batch.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/conformance/runner.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/data/Skeleton.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/_support.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/composer.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/harvest.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/plan.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/fragments/body.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/fragments/heading.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/fragments/info_table.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/fragments/title.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/profile.json +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/application_form/template.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/home_notice/fragments/body.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/home_notice/fragments/heading.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/home_notice/fragments/title.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/home_notice/profile.json +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/home_notice/template.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/fragments/body.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/fragments/heading.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/fragments/title.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/profile.json +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/official_notice/template.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/fragments/body.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/fragments/heading.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/fragments/info_table.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/fragments/title.xml +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/profile.json +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/profiles/report/template.hwpx +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/design/validator.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/compose.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/ir.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/measure.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/parser.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/exam/profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fill.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/apply.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/engine.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/measure.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/policy.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/seal.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/form_fit/wordbox.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/formfill_quality.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/guidance_scan.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/ingest/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/ingest/base.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/ingest/hwpx_converter.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/layout/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/layout/lint.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/layout/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/opc/relationships.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/opc/security.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/opc/xml_utils.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/_document_impl.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/_document_primitives.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/body.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/canonical_defaults.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/common.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/document.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/document_parts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/header.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/header_part.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/memo.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/namespaces.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/numbering.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/objects.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/paragraph.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/parser.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/run.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/schema.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/section.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/section_format.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/section_story.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/simple_parts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/oxml/utils.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/package.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/presets/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/presets/proposal.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/py.typed +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/quality/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/quality/ledger.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/quality/policy.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/quality/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/quality/save_pipeline.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/template_formfit.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/templates.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/_schemas/header.xsd +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/_schemas/section.xsd +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/advanced_generators.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/archive_cli.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/doc_diff.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/exporter.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/__main__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/generator.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/minimize.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/fuzz/runner.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/generic_inventory.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/id_integrity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/idempotence.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/ir_equality.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/mail_merge.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/official_lint.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/package_reconcile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/package_validator.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/page_guard.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/pii.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/read_fidelity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/recover.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/redline.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/repair.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/report_parser.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/report_utils.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/roundtrip_diff.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/style_profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/table_cleanup.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/table_compute.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/table_navigation.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/template_analyzer.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/text_extract_cli.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/text_extractor.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/toc_author.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/tools/validator.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/__init__.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/_hancom_open_rate.ps1 +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/_refresh_hwpx_mac.applescript +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/_render_hwpx.ps1 +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/_render_hwpx_mac.applescript +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/detectors.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/diff.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/hancom_worker.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/masks.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/oracle.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/page_qa.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/qa_contracts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/qa_metrics.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/hwpx/visual/report.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/python_hwpx.egg-info/entry_points.txt +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/src/python_hwpx.egg-info/top_level.txt +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_advanced_generators.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_blueprint_contracts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_blueprint_dump.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_blueprint_replay.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_catalog.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_cli.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_commands.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_contracts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_document.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_mixed_form.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_path_query.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_agent_story_commands.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_authoring_native_toc.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_authoring_profile_routing.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_authoring_profile_routing_oracle.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_authoring_quality_corpus.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_authoring_render_check.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_blind_eval_fixture.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_body_patch.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_builder_core.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_builder_plan_v2.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_builder_vertical_slice.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_byte_patch_identity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_cell_line_spacing.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_comment_node_robustness.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_conformance.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_corpus_read_fidelity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_corpus_toc_verify.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_coverage_promotion.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_coverage_targets.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_design_builder.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_deviations_registry.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_doc_diff.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_context_manager.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_facade_surface.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_formatting.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_plan.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_plan_computed_fields.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_document_save_api.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_evalplan_fill.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_compose.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_compose_oracle.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_fixtures.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_ir.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_measure.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_parser.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_exam_profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_existing_document_format_editing.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_fill_residue.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fields.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fill_split_run.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fit.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fit_integration.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fit_seal.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fit_seal_placement.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_form_fit_wordbox.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_formfill_differential_driver.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_formfill_quality.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_fuzz_loop.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_fuzz_regressions.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_gap_closure_tools.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_government_report_preset.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_government_table_profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_guidance_scan.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_hancom_render_worker.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_hp_tab_support.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_hwpxlib_corpus_read.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_id_generator_range.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_id_integrity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_image_object_workflow.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_ingest.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_inline_models.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_integration_hwpx_compatibility.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_integration_roundtrip.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_kordoc_absorption.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_layout_cache_scope.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_layout_lint.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_layout_preview.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_m9_p0_spike_tools.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_mail_merge_fit.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_mail_merge_table_compute.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_mail_merge_xlsx.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_markdown_export.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_memo_and_style_editing.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_mixed_form_plan.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_namespace_handling.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_new_features.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_official_document_style.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_official_lint_gongmun_gate.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_official_lint_tableaware.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_opc_package.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_open_safety_corpus.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_openrate.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_oxml_parsing.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_packaging_license_metadata.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_packaging_py_typed.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_paragraph_keep_together.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_paragraph_section_management.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_pii.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_pii_leak_sweep.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_pii_structural.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_pii_wiring.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_proposal_preset.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_public_runtime_boundary.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_question_split_detector.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_read_fidelity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_recover_broken_zip.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_redline_authoring.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_redline_verify.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_repair_repack.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_report_parser.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_report_utils.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_repr_snapshots.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_rhwp_t1_gates.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_rhwp_t2_verification.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_roundtrip_fidelity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_row_heights.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_save_pipeline.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_save_pipeline_no_bypass.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_section_headers.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_set_paragraph_format_keep.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_skeleton_template_ids.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_split_cell.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_split_merged_cell.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_style_profile.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_table_cleanup.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_table_navigation.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_table_patch.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_table_patch_dryrun.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_table_patch_m105.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_tables_default_border.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_template_analyzer_enrichment.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_template_formfit.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_text_extractor_annotations.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_toc_author.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_toc_fidelity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_validation_severity.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_validator_comment_nodes.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_version_metadata.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_visual_fixture_corpus.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_visual_oracle.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_visual_oracle_budget.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_visual_qa_contracts.py +0 -0
- {python_hwpx-3.6.0 → python_hwpx-3.8.0}/tests/test_visual_qa_metrics.py +0 -0
|
@@ -29,10 +29,22 @@ reimplemented from public references, without copying source code:
|
|
|
29
29
|
from public model headers only. hwpxlib sample packages were used only as
|
|
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.
|
|
36
|
+
|
|
37
|
+
These form-fill and equation references were used as behavior evidence only. The
|
|
33
38
|
implementation is a clean-room Python implementation; no source code was
|
|
34
39
|
copied, translated, vendored, or linked.
|
|
35
40
|
|
|
41
|
+
## Optional dependency: latex2mathml
|
|
42
|
+
|
|
43
|
+
The optional ``preview`` extra depends on roniemartinez/latex2mathml (MIT), used
|
|
44
|
+
to convert LaTeX to MathML for the document preview viewer. It is an unmodified
|
|
45
|
+
third-party dependency installed via pip; its MIT license travels with the
|
|
46
|
+
package.
|
|
47
|
+
|
|
36
48
|
## Test fixtures: hwpxlib sample corpus
|
|
37
49
|
|
|
38
50
|
tests/fixtures/hwpxlib_corpus/ contains .hwpx sample files vendored from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-hwpx
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.0
|
|
4
4
|
Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
|
|
5
5
|
Author: python-hwpx Maintainers
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -27,10 +27,13 @@ Requires-Dist: pillow>=10.0; extra == "visual"
|
|
|
27
27
|
Requires-Dist: numpy>=1.26; extra == "visual"
|
|
28
28
|
Provides-Extra: xlsx
|
|
29
29
|
Requires-Dist: openpyxl>=3.1; extra == "xlsx"
|
|
30
|
+
Provides-Extra: preview
|
|
31
|
+
Requires-Dist: latex2mathml>=3.77; extra == "preview"
|
|
30
32
|
Provides-Extra: dev
|
|
31
33
|
Requires-Dist: build>=1.0; extra == "dev"
|
|
32
34
|
Requires-Dist: twine>=4.0; extra == "dev"
|
|
33
35
|
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
36
|
+
Requires-Dist: latex2mathml>=3.77; extra == "dev"
|
|
34
37
|
Provides-Extra: test
|
|
35
38
|
Requires-Dist: build>=1.0; extra == "test"
|
|
36
39
|
Requires-Dist: numpy>=1.26; extra == "test"
|
|
@@ -38,6 +41,7 @@ Requires-Dist: pillow>=10.0; extra == "test"
|
|
|
38
41
|
Requires-Dist: pytest>=7.4; extra == "test"
|
|
39
42
|
Requires-Dist: pytest-cov>=5.0; extra == "test"
|
|
40
43
|
Requires-Dist: ruff>=0.12; extra == "test"
|
|
44
|
+
Requires-Dist: latex2mathml>=3.77; extra == "test"
|
|
41
45
|
Provides-Extra: typecheck
|
|
42
46
|
Requires-Dist: mypy>=1.10; extra == "typecheck"
|
|
43
47
|
Requires-Dist: pyright>=1.1.390; extra == "typecheck"
|
|
@@ -46,16 +50,35 @@ Dynamic: license-file
|
|
|
46
50
|
<p align="center">
|
|
47
51
|
<h1 align="center">python-hwpx</h1>
|
|
48
52
|
<p align="center">
|
|
49
|
-
<strong
|
|
53
|
+
<strong>한컴 없이 HWPX를 안전하게 자동화하는 Python 계층 — 최소 범위 편집, 검증된 저작, 모든 쓰기에 영수증.</strong>
|
|
50
54
|
</p>
|
|
51
55
|
<p align="center">
|
|
52
56
|
<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>
|
|
53
57
|
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
|
|
54
58
|
<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>
|
|
55
59
|
<a href="https://airmang.github.io/python-hwpx/"><img src="https://img.shields.io/badge/docs-Sphinx-8CA1AF" alt="Docs"></a>
|
|
60
|
+
<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>
|
|
61
|
+
<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>
|
|
56
62
|
</p>
|
|
57
63
|
</p>
|
|
58
64
|
|
|
65
|
+
<p align="center">한국어 | <a href="README_EN.md">English</a></p>
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
> **python-hwpx는 한컴 없이 HWPX를 안전하게 자동화하는 Python 계층입니다.** 기존
|
|
70
|
+
> 문서는 최소 범위만 수정하고, 새 문서는 실제 한컴 수용이 검증된 형태로 생성하며,
|
|
71
|
+
> 모든 쓰기에 변경·보존·검증 영수증을 남기고, 완전한 해석과 렌더링은 전문 백엔드에
|
|
72
|
+
> 위임할 수 있습니다.
|
|
73
|
+
|
|
74
|
+
- **최소 범위 편집** — 미수정 part는 저장 시 바이트 그대로 유지됩니다(patch 경로
|
|
75
|
+
바이트 보존 497/497, 동결 코퍼스 v2 · 2026-07-19).
|
|
76
|
+
- **검증된 저작** — 밑바닥 생성도 실제 한컴이 받아들이는 형태로 냅니다(산출물 한컴
|
|
77
|
+
오픈 476/476 all-pass, 실저작 품질 게이트 58/58).
|
|
78
|
+
- **모든 쓰기에 영수증** — 대표 저장 경로는 [Safe Write Contract](docs/safe-write-contract.md)의
|
|
79
|
+
`MutationReport`(`hwpx.mutation-report/v1`)로 실제 쓰기 모드·보존 등급·검증 결과를
|
|
80
|
+
**측정해** 반환합니다.
|
|
81
|
+
|
|
59
82
|
---
|
|
60
83
|
|
|
61
84
|
## 🧩 HWPX Stack (3종)
|
|
@@ -66,11 +89,10 @@ Dynamic: license-file
|
|
|
66
89
|
| 🔌 MCP 서버 | [`hwpx-mcp-server`](https://github.com/airmang/hwpx-mcp-server) | MCP 클라이언트(Claude Desktop, VS Code 등)에서 HWPX 조작 |
|
|
67
90
|
| 🎯 에이전트 스킬 | [`hwpx-plugin`](https://github.com/airmang/hwpx-plugins) | 에이전트가 HWPX를 바로 쓰게 해주는 first-party 플러그인·스킬 번들 |
|
|
68
91
|
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
“first-party”는 프로젝트 유지보수 관계를 뜻하며, 한컴 또는 제3자의 공식 인증을 뜻하지 않습니다.
|
|
92
|
+
`hwpx-mcp-server`와 `hwpx-plugin`은 같은 프로젝트가 직접 유지보수하는 first-party 연동
|
|
93
|
+
구성요소입니다(유지보수 관계를 뜻하며, 한컴 등 외부 기관의 인증을 뜻하지 않습니다).
|
|
72
94
|
|
|
73
|
-
현재 PyPI 공개 릴리스는 `python-hwpx 3.
|
|
95
|
+
현재 PyPI 공개 릴리스는 `python-hwpx 3.8.0`입니다. 일반
|
|
74
96
|
`pip install python-hwpx`로 이 릴리스를 설치할 수 있습니다.
|
|
75
97
|
현재 패키지 분류는 `Development Status :: 3 - Alpha`입니다. 이 분류는 API와 제품의
|
|
76
98
|
성숙도를 나타내며, 공개 버전이나 플러그인의 최소 호환 버전을 대신하지 않습니다.
|
|
@@ -80,13 +102,22 @@ Dynamic: license-file
|
|
|
80
102
|
## 실측으로 말합니다 — Published Corpus
|
|
81
103
|
|
|
82
104
|
이 스택의 산출물은 주장 대신 **실제 한컴오피스 전수 측정**으로 검증됩니다
|
|
83
|
-
(동결 코퍼스 N=497
|
|
105
|
+
(동결 코퍼스 v2, N=497 산출물, 2026-07-19, 실한컴 12.0.0.3288 COM/GUI 오라클;
|
|
106
|
+
상세·주의사항은
|
|
84
107
|
[실측 코퍼스 메트릭](https://airmang.github.io/python-hwpx/corpus-metrics.html)):
|
|
85
108
|
|
|
86
|
-
- **한컴 오픈 수용률
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
|
|
109
|
+
- **한컴 오픈 수용률 476/476 all-pass** (동결 코퍼스 v2 · 2026-07-19 · 실한컴 COM
|
|
110
|
+
`Open()` 판정 · rule-of-three 하한 99.37%) · 파싱 96.2%(458/476)
|
|
111
|
+
- **미수정 영역 바이트 보존 497/497** (patch 경로 한정, zip-part diff · 오라클 불요)
|
|
112
|
+
· **개인정보 0-leak** (35문서/합성 140값)
|
|
113
|
+
- 렌더 검증 416/476 (실한컴 `SaveAs("PDF")`) + 정직 버킷 43건(변경추적 문서의 PDF
|
|
114
|
+
export는 한컴 자체가 거부 — 실측 한계로 발행) + 미검증 17건
|
|
115
|
+
- wild 공개 양식 채움은 구조결함 픽스 후 **무음 서식파괴 16.7%**(판정 66조합, 못 담는 타깃은 typed 거부 35건·산출분 pass 17/28) — **낮은 숫자도 그대로 발행**하고 잔여(페이지 리플·표 shape)를 명명합니다
|
|
116
|
+
|
|
117
|
+
<p align="center">
|
|
118
|
+
<img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/images/redline-hancom.png" alt="python-hwpx가 남긴 변경추적과 AI 에이전트 메모가 실제 한/글에서 열린 화면" width="720">
|
|
119
|
+
</p>
|
|
120
|
+
<p align="center"><sub>python-hwpx로 작성한 변경추적(취소선·삽입)과 AI 에이전트 메모 — 실제 한/글에서 연 화면입니다.</sub></p>
|
|
90
121
|
|
|
91
122
|
> 이 숫자들은 *생성물 수용률* 축입니다(우리가 만든 파일을 실제 한컴이 받아들이는가).
|
|
92
123
|
> 문서 *파싱 recall*과는 다른 축이므로 파서 프로젝트 수치와 병치 비교하지 마세요.
|
|
@@ -195,6 +226,59 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
195
226
|
|
|
196
227
|
> 전체 기능·클래스·메서드 목록은 [사용 가이드](docs/usage.md)와 [API 레퍼런스](https://airmang.github.io/python-hwpx/api_reference.html)를 참고하세요.
|
|
197
228
|
|
|
229
|
+
## 안전한 쓰기 계약 (Safe Write Contract)
|
|
230
|
+
|
|
231
|
+
대표 저장 경로(`save_to_path` · `save_to_stream` · `to_bytes`)는 **요청한 보존 등급을
|
|
232
|
+
쓰기 전에 판정하고, 실제로 무엇을 바꿨는지 측정한 영수증**을 돌려줍니다.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from hwpx.mutation_report import PreservationDowngradeError
|
|
236
|
+
|
|
237
|
+
# 영수증과 함께 저장 — 달성 가능한 가장 강한 보존 등급 자동 선택(mode="auto" 기본)
|
|
238
|
+
report = doc.save_to_path("결과.hwpx", return_report=True)
|
|
239
|
+
print(report.actual_mode) # "patch" | "rebuild"
|
|
240
|
+
print(report.preservation.untouched_part_payloads.to_dict()) # {"verified": 17, "changed": 0}
|
|
241
|
+
|
|
242
|
+
# patch 등급 강제 — 미달이면 아무것도 쓰지 않고 예외(fail-closed)
|
|
243
|
+
try:
|
|
244
|
+
doc.save_to_path("결과.hwpx", mode="patch", fallback="error")
|
|
245
|
+
except PreservationDowngradeError as exc:
|
|
246
|
+
print(exc.offending_parts, exc.suggestion)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- `mode="patch" | "rebuild" | "auto"`(기본 `auto`) · `fallback="error" | "rebuild"`(기본 `error`)
|
|
250
|
+
- `mode="patch"` + `fallback="error"`에서 미수정 part의 바이트 동일성을 지킬 수 없으면
|
|
251
|
+
**아무것도 쓰지 않고** `PreservationDowngradeError`를 던집니다(무음 rebuild 없음).
|
|
252
|
+
- `MutationReport`는 `requestedMode`/`actualMode`/`fallbackUsed`, 변경 part와 좌표 명시
|
|
253
|
+
범위, 보존 3층(part 페이로드·ZIP 레코드·전체 패키지), 검증 3항목(`passed`/`failed`/`not_performed`)을
|
|
254
|
+
**측정해** 반환합니다.
|
|
255
|
+
|
|
256
|
+
> 파라미터 전체와 `MutationReport` 스키마는 [안전한 쓰기 계약 문서](docs/safe-write-contract.md)를 참고하세요.
|
|
257
|
+
|
|
258
|
+
## 지원 매트릭스
|
|
259
|
+
|
|
260
|
+
능력 영역별 실제 등급입니다(동결 코퍼스 v2 · 2026-07-19 · 실한컴 12.0.0.3288 오라클).
|
|
261
|
+
등급 어휘: **Parse / Preserve / Edit / Create / Render-verified /
|
|
262
|
+
Unsupported-but-preserved / Unsupported-and-rejected**.
|
|
263
|
+
|
|
264
|
+
| 능력 영역 | 상태 | 증거 |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| 문단·표 저작/편집 | Parse·Preserve·Edit·Create·Render-verified | 오픈 476/476 · 실저작 게이트 58/58 · 렌더 416 |
|
|
267
|
+
| 표 구조 변경(행·열·표, 오토핏) | Preserve·Edit | `hwpx.table_patch` · 바이트 보존 497/497 |
|
|
268
|
+
| 양식 채움(byte-splice) | Preserve·Edit | `hwpx.patch`·`table_patch`·`body_patch` · 보존 497/497 (wild 무음 서식파괴 16.7%·typed 거부 35/66, 잔여 명명) |
|
|
269
|
+
| 그림 삽입/치환 | Edit·Create | `add_picture`·`replace_picture` (복잡 개체는 한컴 확인 권장) |
|
|
270
|
+
| 차트 | Unsupported-but-preserved | 생성 API 없음 · 기존 차트 part는 patch 보존 |
|
|
271
|
+
| 수식 | Parse·Unsupported-but-preserved | 저작 API 없음 · 기존 수식 파싱·patch 보존 |
|
|
272
|
+
| 변경추적(redline) | Edit·Create | `add_tracked_*` · 실한컴 `IsTrackChange=1` (한컴이 PDF export 거부 → `render_unavailable` 정직 집계) |
|
|
273
|
+
| 메모(코멘트) | Edit·Create·Render-verified | `add_memo*` · 실 Windows 한컴 검증 |
|
|
274
|
+
| 각주/미주 | Edit·Create | `add_footnote`·`add_endnote` (렌더 독립 게이트 미측정) |
|
|
275
|
+
| 네이티브 목차/상호참조 | Create·Render-verified | `add_native_toc`·`toc_verify` · 구조 15/15 · 페이지 정합 5/5 |
|
|
276
|
+
| 암호화 HWPX | Unsupported-and-rejected | 복호화 없음 · 암호화 part는 파싱 단계 예외로 거부 |
|
|
277
|
+
| HWP 5.x 바이너리 | Unsupported-and-rejected | ZIP 아님 → 열기 시 `BadZipFile` (HWPX로 변환 후 사용) |
|
|
278
|
+
| 누름틀(form field) 생성 | Parse·Edit | 기존 필드 조회·서식보존 채움 · **신규 누름틀 생성 도구는 미제공** |
|
|
279
|
+
|
|
280
|
+
> 각 등급의 판정 근거와 상세 증거 포인터는 [지원 매트릭스 문서](docs/support-matrix.md)를 참고하세요.
|
|
281
|
+
|
|
198
282
|
## 대항 라이브러리 비교
|
|
199
283
|
|
|
200
284
|
| | python-hwpx | pyhwpx | pyhwp |
|
|
@@ -219,6 +303,7 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
219
303
|
- **[🚀 빠른 시작](docs/quickstart.md)** · **[📚 사용 가이드](docs/usage.md)** — 첫 파일 열기부터 문단·표·메모·섹션 편집, 텍스트 추출·검증까지
|
|
220
304
|
- **[💡 예제 모음](docs/examples.md)** · [`examples/`](examples/) — `build_release_checklist.py`(메모·스타일 편집 HWPX 생성), `extract_text.py`(CLI 텍스트 추출), `find_objects.py`(OWPML 노드 추적) 등
|
|
221
305
|
- **[📐 스키마 개요](docs/schema-overview.md)** · **[🔧 설치 검증](docs/installation.md)**
|
|
306
|
+
- **[🔬 HWPX 내부 실전 가이드](docs/internals/)** — 실제 한/글 동작에서 확인된 HWPUNIT·조판 캐시·목차 필드·OPC 재패킹·메모·오라클 한계
|
|
222
307
|
- **[📖 전체 문서 (Sphinx)](https://airmang.github.io/python-hwpx/)** — API 레퍼런스·50+ 실전 패턴·FAQ
|
|
223
308
|
- **[📝 CHANGELOG](CHANGELOG.md)** · **[🤝 CONTRIBUTING](CONTRIBUTING.md)** · **[👥 CONTRIBUTORS](CONTRIBUTORS.md)**
|
|
224
309
|
|
|
@@ -226,6 +311,11 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
226
311
|
|
|
227
312
|
버그 리포트, 기능 제안, PR 모두 환영합니다.
|
|
228
313
|
|
|
314
|
+
- **[help wanted 이슈](https://github.com/airmang/python-hwpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)** — 지금 들어오기 좋은 입구입니다. 시작 전에 이슈에 코멘트로 방향을 남겨주세요.
|
|
315
|
+
- **[마일스톤](https://github.com/airmang/python-hwpx/milestones)** — 공개 로드맵입니다. 프로젝트가 어디로 가는지 여기서 보입니다.
|
|
316
|
+
- **[Discussions](https://github.com/airmang/python-hwpx/discussions)** — 질문·아이디어는 이슈 대신 여기로.
|
|
317
|
+
- **[내부 실전 가이드](docs/internals/)** — HWPX 내부 구조가 처음이라면 여기부터. 개발 흐름은 [CONTRIBUTING.md](CONTRIBUTING.md)에 있습니다.
|
|
318
|
+
|
|
229
319
|
```bash
|
|
230
320
|
git clone https://github.com/airmang/python-hwpx.git
|
|
231
321
|
cd python-hwpx
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<h1 align="center">python-hwpx</h1>
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong
|
|
4
|
+
<strong>한컴 없이 HWPX를 안전하게 자동화하는 Python 계층 — 최소 범위 편집, 검증된 저작, 모든 쓰기에 영수증.</strong>
|
|
5
5
|
</p>
|
|
6
6
|
<p align="center">
|
|
7
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
8
|
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
|
|
9
9
|
<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>
|
|
10
10
|
<a href="https://airmang.github.io/python-hwpx/"><img src="https://img.shields.io/badge/docs-Sphinx-8CA1AF" alt="Docs"></a>
|
|
11
|
+
<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>
|
|
12
|
+
<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>
|
|
11
13
|
</p>
|
|
12
14
|
</p>
|
|
13
15
|
|
|
16
|
+
<p align="center">한국어 | <a href="README_EN.md">English</a></p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
> **python-hwpx는 한컴 없이 HWPX를 안전하게 자동화하는 Python 계층입니다.** 기존
|
|
21
|
+
> 문서는 최소 범위만 수정하고, 새 문서는 실제 한컴 수용이 검증된 형태로 생성하며,
|
|
22
|
+
> 모든 쓰기에 변경·보존·검증 영수증을 남기고, 완전한 해석과 렌더링은 전문 백엔드에
|
|
23
|
+
> 위임할 수 있습니다.
|
|
24
|
+
|
|
25
|
+
- **최소 범위 편집** — 미수정 part는 저장 시 바이트 그대로 유지됩니다(patch 경로
|
|
26
|
+
바이트 보존 497/497, 동결 코퍼스 v2 · 2026-07-19).
|
|
27
|
+
- **검증된 저작** — 밑바닥 생성도 실제 한컴이 받아들이는 형태로 냅니다(산출물 한컴
|
|
28
|
+
오픈 476/476 all-pass, 실저작 품질 게이트 58/58).
|
|
29
|
+
- **모든 쓰기에 영수증** — 대표 저장 경로는 [Safe Write Contract](docs/safe-write-contract.md)의
|
|
30
|
+
`MutationReport`(`hwpx.mutation-report/v1`)로 실제 쓰기 모드·보존 등급·검증 결과를
|
|
31
|
+
**측정해** 반환합니다.
|
|
32
|
+
|
|
14
33
|
---
|
|
15
34
|
|
|
16
35
|
## 🧩 HWPX Stack (3종)
|
|
@@ -21,11 +40,10 @@
|
|
|
21
40
|
| 🔌 MCP 서버 | [`hwpx-mcp-server`](https://github.com/airmang/hwpx-mcp-server) | MCP 클라이언트(Claude Desktop, VS Code 등)에서 HWPX 조작 |
|
|
22
41
|
| 🎯 에이전트 스킬 | [`hwpx-plugin`](https://github.com/airmang/hwpx-plugins) | 에이전트가 HWPX를 바로 쓰게 해주는 first-party 플러그인·스킬 번들 |
|
|
23
42
|
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
“first-party”는 프로젝트 유지보수 관계를 뜻하며, 한컴 또는 제3자의 공식 인증을 뜻하지 않습니다.
|
|
43
|
+
`hwpx-mcp-server`와 `hwpx-plugin`은 같은 프로젝트가 직접 유지보수하는 first-party 연동
|
|
44
|
+
구성요소입니다(유지보수 관계를 뜻하며, 한컴 등 외부 기관의 인증을 뜻하지 않습니다).
|
|
27
45
|
|
|
28
|
-
현재 PyPI 공개 릴리스는 `python-hwpx 3.
|
|
46
|
+
현재 PyPI 공개 릴리스는 `python-hwpx 3.8.0`입니다. 일반
|
|
29
47
|
`pip install python-hwpx`로 이 릴리스를 설치할 수 있습니다.
|
|
30
48
|
현재 패키지 분류는 `Development Status :: 3 - Alpha`입니다. 이 분류는 API와 제품의
|
|
31
49
|
성숙도를 나타내며, 공개 버전이나 플러그인의 최소 호환 버전을 대신하지 않습니다.
|
|
@@ -35,13 +53,22 @@
|
|
|
35
53
|
## 실측으로 말합니다 — Published Corpus
|
|
36
54
|
|
|
37
55
|
이 스택의 산출물은 주장 대신 **실제 한컴오피스 전수 측정**으로 검증됩니다
|
|
38
|
-
(동결 코퍼스 N=497
|
|
56
|
+
(동결 코퍼스 v2, N=497 산출물, 2026-07-19, 실한컴 12.0.0.3288 COM/GUI 오라클;
|
|
57
|
+
상세·주의사항은
|
|
39
58
|
[실측 코퍼스 메트릭](https://airmang.github.io/python-hwpx/corpus-metrics.html)):
|
|
40
59
|
|
|
41
|
-
- **한컴 오픈 수용률
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
60
|
+
- **한컴 오픈 수용률 476/476 all-pass** (동결 코퍼스 v2 · 2026-07-19 · 실한컴 COM
|
|
61
|
+
`Open()` 판정 · rule-of-three 하한 99.37%) · 파싱 96.2%(458/476)
|
|
62
|
+
- **미수정 영역 바이트 보존 497/497** (patch 경로 한정, zip-part diff · 오라클 불요)
|
|
63
|
+
· **개인정보 0-leak** (35문서/합성 140값)
|
|
64
|
+
- 렌더 검증 416/476 (실한컴 `SaveAs("PDF")`) + 정직 버킷 43건(변경추적 문서의 PDF
|
|
65
|
+
export는 한컴 자체가 거부 — 실측 한계로 발행) + 미검증 17건
|
|
66
|
+
- wild 공개 양식 채움은 구조결함 픽스 후 **무음 서식파괴 16.7%**(판정 66조합, 못 담는 타깃은 typed 거부 35건·산출분 pass 17/28) — **낮은 숫자도 그대로 발행**하고 잔여(페이지 리플·표 shape)를 명명합니다
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<img src="https://raw.githubusercontent.com/airmang/python-hwpx/main/docs/images/redline-hancom.png" alt="python-hwpx가 남긴 변경추적과 AI 에이전트 메모가 실제 한/글에서 열린 화면" width="720">
|
|
70
|
+
</p>
|
|
71
|
+
<p align="center"><sub>python-hwpx로 작성한 변경추적(취소선·삽입)과 AI 에이전트 메모 — 실제 한/글에서 연 화면입니다.</sub></p>
|
|
45
72
|
|
|
46
73
|
> 이 숫자들은 *생성물 수용률* 축입니다(우리가 만든 파일을 실제 한컴이 받아들이는가).
|
|
47
74
|
> 문서 *파싱 recall*과는 다른 축이므로 파서 프로젝트 수치와 병치 비교하지 마세요.
|
|
@@ -150,6 +177,59 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
150
177
|
|
|
151
178
|
> 전체 기능·클래스·메서드 목록은 [사용 가이드](docs/usage.md)와 [API 레퍼런스](https://airmang.github.io/python-hwpx/api_reference.html)를 참고하세요.
|
|
152
179
|
|
|
180
|
+
## 안전한 쓰기 계약 (Safe Write Contract)
|
|
181
|
+
|
|
182
|
+
대표 저장 경로(`save_to_path` · `save_to_stream` · `to_bytes`)는 **요청한 보존 등급을
|
|
183
|
+
쓰기 전에 판정하고, 실제로 무엇을 바꿨는지 측정한 영수증**을 돌려줍니다.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from hwpx.mutation_report import PreservationDowngradeError
|
|
187
|
+
|
|
188
|
+
# 영수증과 함께 저장 — 달성 가능한 가장 강한 보존 등급 자동 선택(mode="auto" 기본)
|
|
189
|
+
report = doc.save_to_path("결과.hwpx", return_report=True)
|
|
190
|
+
print(report.actual_mode) # "patch" | "rebuild"
|
|
191
|
+
print(report.preservation.untouched_part_payloads.to_dict()) # {"verified": 17, "changed": 0}
|
|
192
|
+
|
|
193
|
+
# patch 등급 강제 — 미달이면 아무것도 쓰지 않고 예외(fail-closed)
|
|
194
|
+
try:
|
|
195
|
+
doc.save_to_path("결과.hwpx", mode="patch", fallback="error")
|
|
196
|
+
except PreservationDowngradeError as exc:
|
|
197
|
+
print(exc.offending_parts, exc.suggestion)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- `mode="patch" | "rebuild" | "auto"`(기본 `auto`) · `fallback="error" | "rebuild"`(기본 `error`)
|
|
201
|
+
- `mode="patch"` + `fallback="error"`에서 미수정 part의 바이트 동일성을 지킬 수 없으면
|
|
202
|
+
**아무것도 쓰지 않고** `PreservationDowngradeError`를 던집니다(무음 rebuild 없음).
|
|
203
|
+
- `MutationReport`는 `requestedMode`/`actualMode`/`fallbackUsed`, 변경 part와 좌표 명시
|
|
204
|
+
범위, 보존 3층(part 페이로드·ZIP 레코드·전체 패키지), 검증 3항목(`passed`/`failed`/`not_performed`)을
|
|
205
|
+
**측정해** 반환합니다.
|
|
206
|
+
|
|
207
|
+
> 파라미터 전체와 `MutationReport` 스키마는 [안전한 쓰기 계약 문서](docs/safe-write-contract.md)를 참고하세요.
|
|
208
|
+
|
|
209
|
+
## 지원 매트릭스
|
|
210
|
+
|
|
211
|
+
능력 영역별 실제 등급입니다(동결 코퍼스 v2 · 2026-07-19 · 실한컴 12.0.0.3288 오라클).
|
|
212
|
+
등급 어휘: **Parse / Preserve / Edit / Create / Render-verified /
|
|
213
|
+
Unsupported-but-preserved / Unsupported-and-rejected**.
|
|
214
|
+
|
|
215
|
+
| 능력 영역 | 상태 | 증거 |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| 문단·표 저작/편집 | Parse·Preserve·Edit·Create·Render-verified | 오픈 476/476 · 실저작 게이트 58/58 · 렌더 416 |
|
|
218
|
+
| 표 구조 변경(행·열·표, 오토핏) | Preserve·Edit | `hwpx.table_patch` · 바이트 보존 497/497 |
|
|
219
|
+
| 양식 채움(byte-splice) | Preserve·Edit | `hwpx.patch`·`table_patch`·`body_patch` · 보존 497/497 (wild 무음 서식파괴 16.7%·typed 거부 35/66, 잔여 명명) |
|
|
220
|
+
| 그림 삽입/치환 | Edit·Create | `add_picture`·`replace_picture` (복잡 개체는 한컴 확인 권장) |
|
|
221
|
+
| 차트 | Unsupported-but-preserved | 생성 API 없음 · 기존 차트 part는 patch 보존 |
|
|
222
|
+
| 수식 | Parse·Unsupported-but-preserved | 저작 API 없음 · 기존 수식 파싱·patch 보존 |
|
|
223
|
+
| 변경추적(redline) | Edit·Create | `add_tracked_*` · 실한컴 `IsTrackChange=1` (한컴이 PDF export 거부 → `render_unavailable` 정직 집계) |
|
|
224
|
+
| 메모(코멘트) | Edit·Create·Render-verified | `add_memo*` · 실 Windows 한컴 검증 |
|
|
225
|
+
| 각주/미주 | Edit·Create | `add_footnote`·`add_endnote` (렌더 독립 게이트 미측정) |
|
|
226
|
+
| 네이티브 목차/상호참조 | Create·Render-verified | `add_native_toc`·`toc_verify` · 구조 15/15 · 페이지 정합 5/5 |
|
|
227
|
+
| 암호화 HWPX | Unsupported-and-rejected | 복호화 없음 · 암호화 part는 파싱 단계 예외로 거부 |
|
|
228
|
+
| HWP 5.x 바이너리 | Unsupported-and-rejected | ZIP 아님 → 열기 시 `BadZipFile` (HWPX로 변환 후 사용) |
|
|
229
|
+
| 누름틀(form field) 생성 | Parse·Edit | 기존 필드 조회·서식보존 채움 · **신규 누름틀 생성 도구는 미제공** |
|
|
230
|
+
|
|
231
|
+
> 각 등급의 판정 근거와 상세 증거 포인터는 [지원 매트릭스 문서](docs/support-matrix.md)를 참고하세요.
|
|
232
|
+
|
|
153
233
|
## 대항 라이브러리 비교
|
|
154
234
|
|
|
155
235
|
| | python-hwpx | pyhwpx | pyhwp |
|
|
@@ -174,6 +254,7 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
174
254
|
- **[🚀 빠른 시작](docs/quickstart.md)** · **[📚 사용 가이드](docs/usage.md)** — 첫 파일 열기부터 문단·표·메모·섹션 편집, 텍스트 추출·검증까지
|
|
175
255
|
- **[💡 예제 모음](docs/examples.md)** · [`examples/`](examples/) — `build_release_checklist.py`(메모·스타일 편집 HWPX 생성), `extract_text.py`(CLI 텍스트 추출), `find_objects.py`(OWPML 노드 추적) 등
|
|
176
256
|
- **[📐 스키마 개요](docs/schema-overview.md)** · **[🔧 설치 검증](docs/installation.md)**
|
|
257
|
+
- **[🔬 HWPX 내부 실전 가이드](docs/internals/)** — 실제 한/글 동작에서 확인된 HWPUNIT·조판 캐시·목차 필드·OPC 재패킹·메모·오라클 한계
|
|
177
258
|
- **[📖 전체 문서 (Sphinx)](https://airmang.github.io/python-hwpx/)** — API 레퍼런스·50+ 실전 패턴·FAQ
|
|
178
259
|
- **[📝 CHANGELOG](CHANGELOG.md)** · **[🤝 CONTRIBUTING](CONTRIBUTING.md)** · **[👥 CONTRIBUTORS](CONTRIBUTORS.md)**
|
|
179
260
|
|
|
@@ -181,6 +262,11 @@ hwpx-analyze-template 보고서.hwpx
|
|
|
181
262
|
|
|
182
263
|
버그 리포트, 기능 제안, PR 모두 환영합니다.
|
|
183
264
|
|
|
265
|
+
- **[help wanted 이슈](https://github.com/airmang/python-hwpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)** — 지금 들어오기 좋은 입구입니다. 시작 전에 이슈에 코멘트로 방향을 남겨주세요.
|
|
266
|
+
- **[마일스톤](https://github.com/airmang/python-hwpx/milestones)** — 공개 로드맵입니다. 프로젝트가 어디로 가는지 여기서 보입니다.
|
|
267
|
+
- **[Discussions](https://github.com/airmang/python-hwpx/discussions)** — 질문·아이디어는 이슈 대신 여기로.
|
|
268
|
+
- **[내부 실전 가이드](docs/internals/)** — HWPX 내부 구조가 처음이라면 여기부터. 개발 흐름은 [CONTRIBUTING.md](CONTRIBUTING.md)에 있습니다.
|
|
269
|
+
|
|
184
270
|
```bash
|
|
185
271
|
git clone https://github.com/airmang/python-hwpx.git
|
|
186
272
|
cd python-hwpx
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-hwpx"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.8.0"
|
|
8
8
|
description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -38,10 +38,16 @@ visual = [
|
|
|
38
38
|
xlsx = [
|
|
39
39
|
"openpyxl>=3.1",
|
|
40
40
|
]
|
|
41
|
+
# Document preview viewer equation rendering: LaTeX -> MathML. Optional; without
|
|
42
|
+
# it the preview fails closed to a LaTeX code block instead of dropping equations.
|
|
43
|
+
preview = [
|
|
44
|
+
"latex2mathml>=3.77",
|
|
45
|
+
]
|
|
41
46
|
dev = [
|
|
42
47
|
"build>=1.0",
|
|
43
48
|
"twine>=4.0",
|
|
44
49
|
"pytest>=7.4",
|
|
50
|
+
"latex2mathml>=3.77",
|
|
45
51
|
]
|
|
46
52
|
test = [
|
|
47
53
|
"build>=1.0",
|
|
@@ -50,6 +56,7 @@ test = [
|
|
|
50
56
|
"pytest>=7.4",
|
|
51
57
|
"pytest-cov>=5.0",
|
|
52
58
|
"ruff>=0.12",
|
|
59
|
+
"latex2mathml>=3.77",
|
|
53
60
|
]
|
|
54
61
|
typecheck = [
|
|
55
62
|
"mypy>=1.10",
|
|
@@ -109,6 +116,13 @@ python_version = "3.10"
|
|
|
109
116
|
follow_imports = "skip"
|
|
110
117
|
files = [
|
|
111
118
|
"src/hwpx/document.py",
|
|
119
|
+
"src/hwpx/mutation_report.py",
|
|
120
|
+
"src/hwpx/equation/__init__.py",
|
|
121
|
+
"src/hwpx/equation/tokens.py",
|
|
122
|
+
"src/hwpx/equation/eqedit.py",
|
|
123
|
+
"src/hwpx/equation/mathml.py",
|
|
124
|
+
"src/hwpx/equation/render.py",
|
|
125
|
+
"src/hwpx/tools/document_viewer.py",
|
|
112
126
|
"src/hwpx/_document/_units.py",
|
|
113
127
|
"src/hwpx/_document/fields.py",
|
|
114
128
|
"src/hwpx/_document/layout.py",
|
|
@@ -156,6 +170,13 @@ ignore_missing_imports = true
|
|
|
156
170
|
# both central facades and every extracted/touched runtime owner to the gate.
|
|
157
171
|
include = [
|
|
158
172
|
"src/hwpx/document.py",
|
|
173
|
+
"src/hwpx/mutation_report.py",
|
|
174
|
+
"src/hwpx/equation/__init__.py",
|
|
175
|
+
"src/hwpx/equation/tokens.py",
|
|
176
|
+
"src/hwpx/equation/eqedit.py",
|
|
177
|
+
"src/hwpx/equation/mathml.py",
|
|
178
|
+
"src/hwpx/equation/render.py",
|
|
179
|
+
"src/hwpx/tools/document_viewer.py",
|
|
159
180
|
"src/hwpx/_document/_units.py",
|
|
160
181
|
"src/hwpx/_document/fields.py",
|
|
161
182
|
"src/hwpx/_document/layout.py",
|
|
@@ -75,6 +75,10 @@ from .tools.layout_preview import (
|
|
|
75
75
|
PreviewPage,
|
|
76
76
|
render_layout_preview,
|
|
77
77
|
)
|
|
78
|
+
from .tools.document_viewer import (
|
|
79
|
+
DocumentViewer,
|
|
80
|
+
render_document_viewer,
|
|
81
|
+
)
|
|
78
82
|
from .ingest import (
|
|
79
83
|
ConversionAttempt,
|
|
80
84
|
DocumentConverter,
|
|
@@ -85,6 +89,10 @@ from .ingest import (
|
|
|
85
89
|
HwpxMarkdownConverter,
|
|
86
90
|
UnsupportedDocumentFormat,
|
|
87
91
|
)
|
|
92
|
+
from .mutation_report import (
|
|
93
|
+
MutationReport,
|
|
94
|
+
PreservationDowngradeError,
|
|
95
|
+
)
|
|
88
96
|
from .patch import (
|
|
89
97
|
BytePreservingPatchResult,
|
|
90
98
|
ParagraphTextPatch,
|
|
@@ -146,6 +154,8 @@ __all__ = [
|
|
|
146
154
|
"ParagraphInfo",
|
|
147
155
|
"PackageValidationReport",
|
|
148
156
|
"BytePreservingPatchResult",
|
|
157
|
+
"MutationReport",
|
|
158
|
+
"PreservationDowngradeError",
|
|
149
159
|
"PlanValidationReport",
|
|
150
160
|
"ParagraphTextPatch",
|
|
151
161
|
"PatchApplied",
|
|
@@ -200,6 +210,8 @@ __all__ = [
|
|
|
200
210
|
"validate_package",
|
|
201
211
|
"paragraph_patch",
|
|
202
212
|
"render_layout_preview",
|
|
213
|
+
"render_document_viewer",
|
|
214
|
+
"DocumentViewer",
|
|
203
215
|
"register_template",
|
|
204
216
|
"table_compute",
|
|
205
217
|
]
|