python-hwpx-automation 7.2.0__tar.gz → 7.3.1__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_automation-7.2.0/src/python_hwpx_automation.egg-info → python_hwpx_automation-7.3.1}/PKG-INFO +18 -4
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/README.md +17 -3
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/pyproject.toml +1 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/form_fill.py +7 -2
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/identity.json +10 -10
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/native.py +12 -4
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/replay.py +4 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/cli.py +9 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/commands.py +13 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/document.py +12 -3
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/__init__.py +8 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/builder/core.py +153 -11
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/builder/report.py +5 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/template_analyzer.py +6 -1
- python_hwpx_automation-7.3.1/src/hwpx_automation/office/rendering/__main__.py +7 -0
- python_hwpx_automation-7.3.1/src/hwpx_automation/office/rendering/cli.py +400 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/mac_session.py +7 -1
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/oracle.py +5 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/package_validation.py +3 -5
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/public-modules.json +6 -4
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1/src/python_hwpx_automation.egg-info}/PKG-INFO +18 -4
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/python_hwpx_automation.egg-info/SOURCES.txt +2 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/LICENSE +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/MANIFEST.in +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/NOTICE +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/setup.cfg +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/__init__.pyi +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/__main__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/agent_document.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/api.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/blind_eval.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/capabilities.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/compat.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/configuration.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/content.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/context.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/diff.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/document.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/formatting.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/handles.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/locations.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/locator.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/plan.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/resources.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/search.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/transactions.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/core/txn.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/document_state.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/errors.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/execution_lock.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/fastmcp_adapter.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/form_output_models.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/_shared.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/agent_document.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/authoring.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/content_edit.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/form_fill.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/layout_style.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/quality_render.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/read_export.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/specialized.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/tracked_changes.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/handlers/workflow.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/hwp_converter.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/hwp_support.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/hwpx_ops.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/identity.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ingest_adapters.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/markdown_plan.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/mcp_cli.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/metadata/tools_meta.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/mixed_form.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/mutation_models.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/network_policy.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/_batch_publication.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/_batch_verification.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/_text_scope.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/bundle.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/catalog.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/dump.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/mapping.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/blueprint/model.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/catalog.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/form_plan.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/model.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/path.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/query.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/agent/story.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/advanced_generators.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/builder/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/_support.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/composer.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/harvest.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/plan.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profile.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/profile.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/profile.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/design/validator.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/presets/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/presets/proposal.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/report_parser.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/authoring/style_profile.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/charting/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/compliance/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/compliance/official_lint.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/compliance/pii.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/document_ops/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/document_ops/comparison.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/document_ops/mail_merge.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/document_ops/redline.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/evalplan/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/evalplan/runtime.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/compose.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/ir.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/measure.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/parser.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/exam/profile.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/classification.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fill_residue.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/apply.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/engine.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/measure.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/policy.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/report.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/seal.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/fit/wordbox.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/guidance.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/quality.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/split_run.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/form_fill/template_formfit.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/composition.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/data/bank.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/data/genres.json +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/gianmun.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/house_style/org_chart.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/quality/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/quality/page_guard.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/_hancom_open_rate.ps1 +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/_render_hwpx.ps1 +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/_render_hwpx_mac.applescript +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/block_splits.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/detectors.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/diff.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/fixture_corpus.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/page_qa.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/qa_contracts.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/qa_metrics.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/rendering/worker.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/utilities/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/office/utilities/table_compute.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/_border_fill.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/composition.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/content_layout.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/context.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/form_fields.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/media.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/memo_style.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/planning.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/preview_export.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/read_query.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/save_policy.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/tables.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/ops_services/transactions.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/preview_output_models.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/py.typed +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/quality.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/quality_generation.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/resources.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/runtime.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/runtime_services.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/server.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/storage.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/tool_bindings.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/tool_contract.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/upstream.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/utils/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/utils/helpers.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/utils/read_budget.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/visual_qa.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/__init__.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/adapters.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/dispatcher.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/evidence_lineage.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/models.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/policy.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/render_contracts.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/render_metrics.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/render_queue.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/render_security.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/render_transport.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/rendering.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/service.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/state_machine.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workflow/store.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/workspace.py +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/python_hwpx_automation.egg-info/dependency_links.txt +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/python_hwpx_automation.egg-info/entry_points.txt +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/python_hwpx_automation.egg-info/requires.txt +0 -0
- {python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/python_hwpx_automation.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-hwpx-automation
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.3.1
|
|
4
4
|
Summary: Task-oriented HWPX document automation for Python, with an optional MCP adapter.
|
|
5
5
|
Author: Kohkyuhyun
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -74,8 +74,9 @@ Dynamic: license-file
|
|
|
74
74
|
|
|
75
75
|
<!-- release-state: release-approved -->
|
|
76
76
|
> [!NOTE]
|
|
77
|
-
> 발행이 승인된 7.
|
|
78
|
-
> 공개 트레인: `python-hwpx 6.
|
|
77
|
+
> 발행이 승인된 7.3.1 후보 (release-approved; 원격 발행 대기): `python-hwpx 6.5.0 → python-hwpx-automation 7.3.1 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
|
|
78
|
+
> 공개 트레인: `python-hwpx 6.5.0 → python-hwpx-automation 7.2.0 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
|
|
79
|
+
> [발행·설치 영수증](docs/releases/2026-09-22-trustworthy-edit-engine.md)
|
|
79
80
|
> 공개 좌표는 core·automation PyPI와 plugin GitHub
|
|
80
81
|
> Release·marketplace·실제 marketplace 설치 관찰 후에만 승격합니다.
|
|
81
82
|
> [릴리스 runbook](docs/release-runbook.md)
|
|
@@ -132,7 +133,7 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
|
|
|
132
133
|
"command": "uvx",
|
|
133
134
|
"args": [
|
|
134
135
|
"--from",
|
|
135
|
-
"python-hwpx-automation[mcp]==7.
|
|
136
|
+
"python-hwpx-automation[mcp]==7.2.0",
|
|
136
137
|
"hwpx-automation-mcp"
|
|
137
138
|
],
|
|
138
139
|
"env": {
|
|
@@ -156,6 +157,19 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
|
|
|
156
157
|
기존 `hwpx-mcp-server` 배포·import·콘솔·설정 키도 6.x 동안 그대로 동작합니다 —
|
|
157
158
|
전체 목록과 유지 규칙: [6.x 호환 표면](docs/compatibility-6x.md)
|
|
158
159
|
|
|
160
|
+
## 한컴으로 PDF 렌더하기
|
|
161
|
+
|
|
162
|
+
한컴(macOS 한컴오피스 한글 또는 Windows 한글 COM)이 있는 컴퓨터에서 문서 하나를
|
|
163
|
+
PDF와 쪽 PNG로 렌더합니다. 별도 환경에서 실행하므로 호출하는 쪽의 python-hwpx
|
|
164
|
+
고정 버전을 건드리지 않습니다.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
uvx --from "python-hwpx-automation[oracle]==7.3.1" \
|
|
168
|
+
hwpx render-pdf in.hwpx out.pdf --png out/page --json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
JSON 결과·종료 코드·플랫폼 요구 사항: [render-pdf](docs/render-pdf.md)
|
|
172
|
+
|
|
159
173
|
## 무엇을 하나
|
|
160
174
|
|
|
161
175
|
기본 모드에서 다수의 HWPX 도구를 제공하며, 고급 모드(`HWPX_AUTOMATION_ADVANCED=1`)에서 점검·검증용 도구가 추가됩니다.
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
<!-- release-state: release-approved -->
|
|
16
16
|
> [!NOTE]
|
|
17
|
-
> 발행이 승인된 7.
|
|
18
|
-
> 공개 트레인: `python-hwpx 6.
|
|
17
|
+
> 발행이 승인된 7.3.1 후보 (release-approved; 원격 발행 대기): `python-hwpx 6.5.0 → python-hwpx-automation 7.3.1 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
|
|
18
|
+
> 공개 트레인: `python-hwpx 6.5.0 → python-hwpx-automation 7.2.0 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
|
|
19
|
+
> [발행·설치 영수증](docs/releases/2026-09-22-trustworthy-edit-engine.md)
|
|
19
20
|
> 공개 좌표는 core·automation PyPI와 plugin GitHub
|
|
20
21
|
> Release·marketplace·실제 marketplace 설치 관찰 후에만 승격합니다.
|
|
21
22
|
> [릴리스 runbook](docs/release-runbook.md)
|
|
@@ -72,7 +73,7 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
|
|
|
72
73
|
"command": "uvx",
|
|
73
74
|
"args": [
|
|
74
75
|
"--from",
|
|
75
|
-
"python-hwpx-automation[mcp]==7.
|
|
76
|
+
"python-hwpx-automation[mcp]==7.2.0",
|
|
76
77
|
"hwpx-automation-mcp"
|
|
77
78
|
],
|
|
78
79
|
"env": {
|
|
@@ -96,6 +97,19 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
|
|
|
96
97
|
기존 `hwpx-mcp-server` 배포·import·콘솔·설정 키도 6.x 동안 그대로 동작합니다 —
|
|
97
98
|
전체 목록과 유지 규칙: [6.x 호환 표면](docs/compatibility-6x.md)
|
|
98
99
|
|
|
100
|
+
## 한컴으로 PDF 렌더하기
|
|
101
|
+
|
|
102
|
+
한컴(macOS 한컴오피스 한글 또는 Windows 한글 COM)이 있는 컴퓨터에서 문서 하나를
|
|
103
|
+
PDF와 쪽 PNG로 렌더합니다. 별도 환경에서 실행하므로 호출하는 쪽의 python-hwpx
|
|
104
|
+
고정 버전을 건드리지 않습니다.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uvx --from "python-hwpx-automation[oracle]==7.3.1" \
|
|
108
|
+
hwpx render-pdf in.hwpx out.pdf --png out/page --json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
JSON 결과·종료 코드·플랫폼 요구 사항: [render-pdf](docs/render-pdf.md)
|
|
112
|
+
|
|
99
113
|
## 무엇을 하나
|
|
100
114
|
|
|
101
115
|
기본 모드에서 다수의 HWPX 도구를 제공하며, 고급 모드(`HWPX_AUTOMATION_ADVANCED=1`)에서 점검·검증용 도구가 추가됩니다.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-hwpx-automation"
|
|
7
|
-
version = "7.
|
|
7
|
+
version = "7.3.1"
|
|
8
8
|
description = "Task-oriented HWPX document automation for Python, with an optional MCP adapter."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Kohkyuhyun" }]
|
{python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/form_fill.py
RENAMED
|
@@ -1122,10 +1122,15 @@ def _runtime_validation(path: str) -> dict[str, Any]:
|
|
|
1122
1122
|
{
|
|
1123
1123
|
"part": getattr(issue, "part_name", None),
|
|
1124
1124
|
"message": getattr(issue, "message", str(issue)),
|
|
1125
|
+
"severity": getattr(issue, "severity", "error"),
|
|
1125
1126
|
}
|
|
1126
1127
|
for issue in getattr(document_report, "issues", ())
|
|
1127
1128
|
]
|
|
1128
|
-
|
|
1129
|
+
# a schema warning from python-hwpx does not block the hand-off; only its errors do
|
|
1130
|
+
structure = {
|
|
1131
|
+
"ok": not any(issue["severity"] == "error" for issue in structure_issues),
|
|
1132
|
+
"issues": structure_issues,
|
|
1133
|
+
}
|
|
1129
1134
|
if validate_package is None:
|
|
1130
1135
|
package = _dependency_unavailable_report(
|
|
1131
1136
|
"python-hwpx>=2.10.3 is required for HWPX package validation",
|
|
@@ -1208,7 +1213,7 @@ def _issue_payload(issue: Any) -> dict[str, Any]:
|
|
|
1208
1213
|
return {
|
|
1209
1214
|
"part": getattr(issue, "part_name", None),
|
|
1210
1215
|
"message": getattr(issue, "message", str(issue)),
|
|
1211
|
-
"level": getattr(issue, "
|
|
1216
|
+
"level": getattr(issue, "severity", "error"),
|
|
1212
1217
|
}
|
|
1213
1218
|
|
|
1214
1219
|
|
{python_hwpx_automation-7.2.0 → python_hwpx_automation-7.3.1}/src/hwpx_automation/identity.json
RENAMED
|
@@ -7,26 +7,26 @@
|
|
|
7
7
|
"candidate": {
|
|
8
8
|
"pythonHwpx": "6.5.0",
|
|
9
9
|
"canonicalDistribution": "python-hwpx-automation",
|
|
10
|
-
"canonicalAutomation": "7.
|
|
10
|
+
"canonicalAutomation": "7.3.1",
|
|
11
11
|
"compatibilityDistribution": "hwpx-mcp-server",
|
|
12
|
-
"compatibility": "7.
|
|
12
|
+
"compatibility": "7.3.1",
|
|
13
13
|
"plugin": "2.3.0",
|
|
14
14
|
"contractHash": "5e5c23651f92785a"
|
|
15
15
|
},
|
|
16
16
|
"currentPublic": {
|
|
17
|
-
"pythonHwpx": "6.
|
|
17
|
+
"pythonHwpx": "6.5.0",
|
|
18
18
|
"primaryDistribution": "python-hwpx-automation",
|
|
19
|
-
"primaryApplication": "7.
|
|
20
|
-
"plugin": "2.
|
|
21
|
-
"contractHash": "
|
|
19
|
+
"primaryApplication": "7.2.0",
|
|
20
|
+
"plugin": "2.3.0",
|
|
21
|
+
"contractHash": "5e5c23651f92785a"
|
|
22
22
|
},
|
|
23
23
|
"promotionGate": "Three states are mandatory: unreleased-candidate while auditing; release-approved only after separate owner approval and while currentPublic still names the previously observed coherent stack; released only in a follow-up commit after remote truth is observed for core, canonical automation, the compatibility distribution, the plugin GitHub release, the marketplace entry, and a real marketplace install. The automation tag workflow publishes only release-approved, leaves currentPublic unchanged, and hands an attached receipt to plugin publication.",
|
|
24
24
|
"previousPublic": {
|
|
25
|
-
"pythonHwpx": "6.
|
|
25
|
+
"pythonHwpx": "6.5.0",
|
|
26
26
|
"primaryDistribution": "python-hwpx-automation",
|
|
27
|
-
"primaryApplication": "7.
|
|
28
|
-
"plugin": "2.
|
|
29
|
-
"contractHash": "
|
|
27
|
+
"primaryApplication": "7.2.0",
|
|
28
|
+
"plugin": "2.3.0",
|
|
29
|
+
"contractHash": "5e5c23651f92785a"
|
|
30
30
|
},
|
|
31
31
|
"publicationEvidence": null
|
|
32
32
|
},
|
|
@@ -19,6 +19,15 @@ from ..model import AgentContractError
|
|
|
19
19
|
|
|
20
20
|
_HWP_UNITS_PER_MM = 7200 / 25.4
|
|
21
21
|
_INLINE_KINDS = frozenset({"table", "picture", "shape", "footnote", "endnote"})
|
|
22
|
+
#: Names a blueprint may give a click-here field. Hancom opens the field only
|
|
23
|
+
#: under its own type name, CLICK_HERE.
|
|
24
|
+
_CLICK_HERE_NAMES = frozenset({"FORM", "CLICKHERE", "NURUMTUL", "누름틀"})
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def hancom_field_type(field_type: object) -> str | None:
|
|
28
|
+
"""CLICK_HERE for any name of a click-here field, None for another field type."""
|
|
29
|
+
normalized = str(field_type or "ClickHere").replace("_", "").replace("-", "").upper()
|
|
30
|
+
return "CLICK_HERE" if normalized in _CLICK_HERE_NAMES else None
|
|
22
31
|
|
|
23
32
|
|
|
24
33
|
@dataclass(frozen=True, slots=True)
|
|
@@ -463,9 +472,8 @@ class TypedNativeBridge:
|
|
|
463
472
|
def _create_form_field(self, node_id: str, paragraph: Any) -> None:
|
|
464
473
|
node = self.nodes[node_id]
|
|
465
474
|
properties = node["properties"]
|
|
466
|
-
field_type =
|
|
467
|
-
|
|
468
|
-
if normalized_type not in {"FORM", "CLICKHERE", "NURUMTUL", "누름틀"}:
|
|
475
|
+
field_type = hancom_field_type(properties.get("fieldType"))
|
|
476
|
+
if field_type is None:
|
|
469
477
|
raise AgentContractError("unsupported_content", "only FORM fields are replayable", target=node_id)
|
|
470
478
|
host_id = self.references.get((node_id, "hostRun"))
|
|
471
479
|
end_id = self.references.get((node_id, "endRun"))
|
|
@@ -486,7 +494,7 @@ class TypedNativeBridge:
|
|
|
486
494
|
"id": begin_id,
|
|
487
495
|
"fieldid": field_id,
|
|
488
496
|
"name": str(properties.get("name") or "")[:512],
|
|
489
|
-
"type":
|
|
497
|
+
"type": field_type,
|
|
490
498
|
"editable": "0" if properties.get("readOnly") else "1",
|
|
491
499
|
"dirty": "0",
|
|
492
500
|
"zorder": "-1",
|
|
@@ -23,7 +23,7 @@ from .bundle import BlueprintBundle, read_blueprint_bundle
|
|
|
23
23
|
from .dump import dump_document_blueprint
|
|
24
24
|
from .mapping import ReplayPlan, materialize_dependencies, plan_replay
|
|
25
25
|
from .model import BlueprintReplayResult, validate_replay_request
|
|
26
|
-
from .native import CreatedBinding, TypedNativeBridge
|
|
26
|
+
from .native import CreatedBinding, TypedNativeBridge, hancom_field_type
|
|
27
27
|
|
|
28
28
|
IdempotencyStore = MutableMapping[str, Any]
|
|
29
29
|
FaultInjector = Callable[[str, int | None], None]
|
|
@@ -80,6 +80,9 @@ def _semantic_nodes(
|
|
|
80
80
|
identity = mapped.get("identity") if mapped else None
|
|
81
81
|
if isinstance(identity, Mapping) and identity.get("name"):
|
|
82
82
|
properties["style"] = identity["name"]
|
|
83
|
+
if node["kind"] == "form-field" and "fieldType" in properties:
|
|
84
|
+
# A click-here field is written, and so read back, as CLICK_HERE whatever the blueprint called it.
|
|
85
|
+
properties["fieldType"] = hancom_field_type(properties["fieldType"]) or properties["fieldType"]
|
|
83
86
|
normalized.append(
|
|
84
87
|
{
|
|
85
88
|
"blueprintId": str(node["blueprintId"]),
|
|
@@ -591,10 +591,18 @@ def main(
|
|
|
591
591
|
stdin = cast(TextIO, stdin or sys.stdin)
|
|
592
592
|
stdout = cast(TextIO, stdout or sys.stdout)
|
|
593
593
|
stderr = cast(TextIO, stderr or sys.stderr)
|
|
594
|
+
arguments = list(argv) if argv is not None else sys.argv[1:]
|
|
595
|
+
# Dispatched ahead of the parser, whose help and choice errors stay
|
|
596
|
+
# byte-identical to the frozen core CLI. The rendering owner is imported
|
|
597
|
+
# only here, so every other command stays free of the Hancom side.
|
|
598
|
+
if arguments[:1] == ["render-pdf"]:
|
|
599
|
+
from ..rendering.cli import main as render_main
|
|
600
|
+
|
|
601
|
+
return render_main(arguments, prog="hwpx", stdout=stdout, stderr=stderr)
|
|
594
602
|
parser = build_parser()
|
|
595
603
|
try:
|
|
596
604
|
with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
|
|
597
|
-
args = parser.parse_args(
|
|
605
|
+
args = parser.parse_args(arguments)
|
|
598
606
|
if args.command == "help":
|
|
599
607
|
if args.kind == "blueprint":
|
|
600
608
|
if args.json_help:
|
|
@@ -73,6 +73,8 @@ _PARAGRAPH_ALIGNMENTS = frozenset(
|
|
|
73
73
|
_TABLE_ALIGNMENTS = frozenset({"LEFT", "CENTER", "RIGHT", "INSIDE", "OUTSIDE"})
|
|
74
74
|
_VERTICAL_ALIGNMENTS = frozenset({"TOP", "CENTER", "BOTTOM"})
|
|
75
75
|
_INLINE_KINDS = frozenset({"table", "picture", "shape", "footnote", "endnote"})
|
|
76
|
+
#: Notes sit in an hp:ctrl: Hancom drops a note outside one and crashes on an empty one.
|
|
77
|
+
_NOTE_TAGS = frozenset({"footNote", "endNote"})
|
|
76
78
|
_SHAPE_KINDS = frozenset({"line", "rect", "ellipse", "arc", "polygon", "curve", "connectLine"})
|
|
77
79
|
|
|
78
80
|
# Creation needs a few geometry fields that are readable, rather than editable,
|
|
@@ -450,7 +452,12 @@ def _insert_inline(
|
|
|
450
452
|
_tag_like(paragraph.element, "run"),
|
|
451
453
|
{"charPrIDRef": paragraph.char_pr_id_ref or "0"},
|
|
452
454
|
)
|
|
453
|
-
|
|
455
|
+
if _local_name(element) in _NOTE_TAGS:
|
|
456
|
+
control = run.makeelement(_tag_like(paragraph.element, "ctrl"), {})
|
|
457
|
+
control.append(element)
|
|
458
|
+
run.append(control)
|
|
459
|
+
else:
|
|
460
|
+
run.append(element)
|
|
454
461
|
if index >= len(siblings):
|
|
455
462
|
paragraph.element.append(run)
|
|
456
463
|
else:
|
|
@@ -458,6 +465,8 @@ def _insert_inline(
|
|
|
458
465
|
anchor_run = anchor.getparent() if hasattr(anchor, "getparent") else next(
|
|
459
466
|
child for child in paragraph.element if anchor in list(child)
|
|
460
467
|
)
|
|
468
|
+
if _local_name(anchor_run) == "ctrl":
|
|
469
|
+
anchor_run = anchor_run.getparent()
|
|
461
470
|
paragraph.element.insert(list(paragraph.element).index(anchor_run), run)
|
|
462
471
|
paragraph.section.mark_dirty()
|
|
463
472
|
return run
|
|
@@ -468,6 +477,9 @@ def _remove_inline_element(element: Any, paragraph: Any) -> None:
|
|
|
468
477
|
child for child in paragraph.element if element in list(child)
|
|
469
478
|
)
|
|
470
479
|
run.remove(element)
|
|
480
|
+
if _local_name(run) == "ctrl" and not list(run):
|
|
481
|
+
control, run = run, run.getparent()
|
|
482
|
+
run.remove(control)
|
|
471
483
|
if not list(run) and run in list(paragraph.element):
|
|
472
484
|
paragraph.element.remove(run)
|
|
473
485
|
paragraph.section.mark_dirty()
|
|
@@ -55,6 +55,15 @@ def _mm(value: object) -> float | None:
|
|
|
55
55
|
return round(units / _HWP_UNITS_PER_MM, 3)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def _drawn_page_size(page_size: Any) -> tuple[int, int]:
|
|
59
|
+
"""Width and height as Hancom draws the page: as stored for
|
|
60
|
+
``landscape="WIDELY"`` (portrait), turned otherwise (``NARROWLY``)."""
|
|
61
|
+
|
|
62
|
+
if page_size.orientation == "WIDELY":
|
|
63
|
+
return page_size.width, page_size.height
|
|
64
|
+
return page_size.height, page_size.width
|
|
65
|
+
|
|
66
|
+
|
|
58
67
|
def _first_child(element: Any, local_name: str) -> Any | None:
|
|
59
68
|
for child in element:
|
|
60
69
|
if _local_name(child) == local_name:
|
|
@@ -249,7 +258,7 @@ class HwpxAgentDocument:
|
|
|
249
258
|
self._add_record(root)
|
|
250
259
|
for section_index, section in enumerate(self.document.sections, start=1):
|
|
251
260
|
section_path = SemanticPath().child(indexed_segment("section", section_index))
|
|
252
|
-
|
|
261
|
+
page_width, page_height = _drawn_page_size(section.properties.page_size)
|
|
253
262
|
section_record = NodeRecord(
|
|
254
263
|
kind="section",
|
|
255
264
|
path=section_path.canonical,
|
|
@@ -259,8 +268,8 @@ class HwpxAgentDocument:
|
|
|
259
268
|
"index": section_index,
|
|
260
269
|
"partId": Path(section.part_name).name,
|
|
261
270
|
"paragraphCount": len(section.paragraphs),
|
|
262
|
-
"pageWidthMm": _mm(
|
|
263
|
-
"pageHeightMm": _mm(
|
|
271
|
+
"pageWidthMm": _mm(page_width),
|
|
272
|
+
"pageHeightMm": _mm(page_height),
|
|
264
273
|
},
|
|
265
274
|
native=section,
|
|
266
275
|
parent_path="/",
|
|
@@ -2030,6 +2030,10 @@ def _normalize_v2_page(value: Any) -> BuilderPageSize | None:
|
|
|
2030
2030
|
width = _float_value(value.get("widthMm", value.get("width_mm")), default=210)
|
|
2031
2031
|
height = _float_value(value.get("heightMm", value.get("height_mm")), default=297)
|
|
2032
2032
|
orientation = str(value.get("orientation") or "PORTRAIT").strip() or "PORTRAIT"
|
|
2033
|
+
# Hancom's own landscape values, as read from a document: WIDELY is
|
|
2034
|
+
# portrait, NARROWLY landscape. Hand the builder the plain names so every
|
|
2035
|
+
# python-hwpx version draws the same page.
|
|
2036
|
+
orientation = {"WIDELY": "PORTRAIT", "NARROWLY": "LANDSCAPE"}.get(orientation.upper(), orientation)
|
|
2033
2037
|
return BuilderPageSize(width_mm=width, height_mm=height, orientation=orientation)
|
|
2034
2038
|
|
|
2035
2039
|
|
|
@@ -2681,7 +2685,10 @@ def _flow_oversized_authored_table(table: Any) -> None:
|
|
|
2681
2685
|
section = table.paragraph.section
|
|
2682
2686
|
page = section.properties.page_size
|
|
2683
2687
|
margins = section.properties.page_margins
|
|
2684
|
-
|
|
2688
|
+
# Hancom turns every page but landscape="WIDELY", so a landscape page's
|
|
2689
|
+
# drawn height is its stored width.
|
|
2690
|
+
drawn_height = page.height if page.orientation == "WIDELY" else page.width
|
|
2691
|
+
body_height = drawn_height - margins.top - margins.bottom
|
|
2685
2692
|
if body_height > 0 and table.height > body_height:
|
|
2686
2693
|
table.set_treat_as_char(False)
|
|
2687
2694
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
from contextvars import ContextVar
|
|
4
5
|
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime, timezone
|
|
5
7
|
from importlib import import_module
|
|
8
|
+
from inspect import signature
|
|
6
9
|
from os import PathLike
|
|
7
10
|
from pathlib import Path
|
|
8
11
|
from typing import Any, ClassVar, Mapping, Sequence, cast
|
|
@@ -23,6 +26,29 @@ BuilderChild = (
|
|
|
23
26
|
)
|
|
24
27
|
_HWP_UNITS_PER_MM = 7200 / 25.4
|
|
25
28
|
_A4_HWP_SIZE = (59528, 84188)
|
|
29
|
+
#: ``hp:pagePr@landscape`` as Hancom writes it: WIDELY draws the page as stored
|
|
30
|
+
#: (portrait), NARROWLY turns it (landscape).
|
|
31
|
+
_HANCOM_PAGE_ORIENTATION = {
|
|
32
|
+
"PORTRAIT": "WIDELY",
|
|
33
|
+
"WIDELY": "WIDELY",
|
|
34
|
+
"LANDSCAPE": "NARROWLY",
|
|
35
|
+
"NARROWLY": "NARROWLY",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _hancom_page_form(orientation: str, width: int, height: int) -> tuple[str, int, int]:
|
|
40
|
+
"""Return *orientation* and the page size in Hancom's own form.
|
|
41
|
+
|
|
42
|
+
Hancom stores both orientations with the paper's portrait size and turns
|
|
43
|
+
the page for NARROWLY. python-hwpx 6.5 writes the value as given and later
|
|
44
|
+
versions write the same form, so every core draws the same page. Unknown
|
|
45
|
+
values pass through unchanged.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
value = _HANCOM_PAGE_ORIENTATION.get(orientation.strip().upper())
|
|
49
|
+
if value is None:
|
|
50
|
+
return orientation, width, height
|
|
51
|
+
return value, min(width, height), max(width, height)
|
|
26
52
|
|
|
27
53
|
# Builder presets hook at Document.lower(), where a single
|
|
28
54
|
# preset context can be passed into Heading/Run/Bullet lowering without
|
|
@@ -257,6 +283,8 @@ class Paragraph:
|
|
|
257
283
|
children: Sequence[Run | PageNumber] = field(default_factory=tuple)
|
|
258
284
|
align: str | None = None
|
|
259
285
|
style: str | None = None
|
|
286
|
+
#: Optional caller name; ``save_to_path`` reports where it was written.
|
|
287
|
+
key: str | None = None
|
|
260
288
|
|
|
261
289
|
def _alignment_para_pr(self, document: HwpxDocument) -> str | None:
|
|
262
290
|
"""Resolve ``align`` into the paragraph-property reference that emits it.
|
|
@@ -503,6 +531,8 @@ class Table:
|
|
|
503
531
|
merges: Sequence[str] = field(default_factory=tuple)
|
|
504
532
|
header_shading: str | None = None
|
|
505
533
|
column_widths: Sequence[int | float] = field(default_factory=tuple)
|
|
534
|
+
#: Optional caller name; ``save_to_path`` reports the paragraph holding it.
|
|
535
|
+
key: str | None = None
|
|
506
536
|
|
|
507
537
|
def lower(
|
|
508
538
|
self,
|
|
@@ -794,13 +824,117 @@ def _merge_flags(*flag_sets: dict[str, bool]) -> dict[str, bool]:
|
|
|
794
824
|
return merged
|
|
795
825
|
|
|
796
826
|
|
|
827
|
+
# Keyed Paragraph/Table nodes -> the paragraph they were written to. While a
|
|
828
|
+
# Document collects anchors, each keyed child records the paragraph counts
|
|
829
|
+
# before it is lowered; after its section is lowered, the first paragraph added
|
|
830
|
+
# since then is its element. Positions are read at the very end, by identity,
|
|
831
|
+
# so a native TOC inserted afterwards cannot shift them.
|
|
832
|
+
_AnchorEntry = tuple[str, tuple[int, ...], Any]
|
|
833
|
+
_ANCHOR_SINK: ContextVar[list[_AnchorEntry] | None] = ContextVar(
|
|
834
|
+
"hwpx_builder_anchor_sink", default=None
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def _paragraph_counts(document: HwpxDocument) -> tuple[int, ...]:
|
|
839
|
+
return tuple(len(section.paragraphs) for section in document.oxml.sections)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def _anchor_mark(document: HwpxDocument, child: object) -> None:
|
|
843
|
+
sink = _ANCHOR_SINK.get()
|
|
844
|
+
key = getattr(child, "key", None)
|
|
845
|
+
if sink is not None and key is not None:
|
|
846
|
+
sink.append((key, _paragraph_counts(document), None))
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _anchor_resolve(document: HwpxDocument) -> None:
|
|
850
|
+
sink = _ANCHOR_SINK.get()
|
|
851
|
+
if not sink:
|
|
852
|
+
return
|
|
853
|
+
sections = document.oxml.sections
|
|
854
|
+
for position, (key, before, element) in enumerate(sink):
|
|
855
|
+
if element is not None:
|
|
856
|
+
continue
|
|
857
|
+
for index, section in enumerate(sections):
|
|
858
|
+
start = before[index] if index < len(before) else 0
|
|
859
|
+
paragraphs = section.paragraphs
|
|
860
|
+
if len(paragraphs) > start:
|
|
861
|
+
sink[position] = (key, before, paragraphs[start].element)
|
|
862
|
+
break
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
def _anchor_positions(
|
|
866
|
+
document: HwpxDocument, sink: Sequence[_AnchorEntry]
|
|
867
|
+
) -> dict[str, dict[str, int]]:
|
|
868
|
+
positions: dict[int, dict[str, int]] = {}
|
|
869
|
+
for section_index, section in enumerate(document.oxml.sections):
|
|
870
|
+
for paragraph_index, paragraph in enumerate(section.paragraphs):
|
|
871
|
+
positions[id(paragraph.element)] = {
|
|
872
|
+
"section": section_index,
|
|
873
|
+
"paragraph": paragraph_index,
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
key: dict(positions[id(element)])
|
|
877
|
+
for key, _before, element in sink
|
|
878
|
+
if element is not None and id(element) in positions
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def _check_unique_keys(sections: Sequence["Section"]) -> None:
|
|
883
|
+
seen: set[str] = set()
|
|
884
|
+
for section in sections:
|
|
885
|
+
for child in section.children:
|
|
886
|
+
key = getattr(child, "key", None)
|
|
887
|
+
if key is None:
|
|
888
|
+
continue
|
|
889
|
+
if key in seen:
|
|
890
|
+
raise ValueError(f"duplicate builder node key: {key!r}")
|
|
891
|
+
seen.add(key)
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def _write_package_metadata(document: HwpxDocument, metadata: Metadata) -> None:
|
|
895
|
+
"""Write *metadata* to ``content.hpf`` (Hancom: File > Document Info).
|
|
896
|
+
|
|
897
|
+
OPF has no organization field, so ``organization`` stays in the save
|
|
898
|
+
report's ``metadata`` only. Created/modified are stamped at build time
|
|
899
|
+
instead of keeping the blank template's dates.
|
|
900
|
+
"""
|
|
901
|
+
|
|
902
|
+
stamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
903
|
+
document.parts.set_document_metadata(
|
|
904
|
+
title=metadata.title or None,
|
|
905
|
+
creator=metadata.author or None,
|
|
906
|
+
created_date=stamp,
|
|
907
|
+
modified_date=stamp,
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
_SCHEMA_CHECK_NOT_RUN = "OWPML schema check could not run"
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def _schema_lint(document_report: object) -> str:
|
|
915
|
+
"""``schema_lint`` from python-hwpx's own validation, never assumed.
|
|
916
|
+
|
|
917
|
+
python-hwpx 6.6+ checks headers and sections against the full OWPML schema
|
|
918
|
+
and reports violations as warnings. Older cores only check a lax section
|
|
919
|
+
stub, so nothing was schema-checked: report ``not_checked``, not ``pass``.
|
|
920
|
+
"""
|
|
921
|
+
|
|
922
|
+
if "full_schema" not in signature(validate_document).parameters:
|
|
923
|
+
return "not_checked"
|
|
924
|
+
issues = getattr(document_report, "issues", ())
|
|
925
|
+
if any(_SCHEMA_CHECK_NOT_RUN in str(getattr(issue, "message", "")) for issue in issues):
|
|
926
|
+
return "not_checked"
|
|
927
|
+
if getattr(document_report, "errors", ()):
|
|
928
|
+
return "fail"
|
|
929
|
+
return "warning" if getattr(document_report, "warnings", ()) else "pass"
|
|
930
|
+
|
|
931
|
+
|
|
797
932
|
def _hard_gates(
|
|
798
933
|
package_report: object,
|
|
799
934
|
document_report: object,
|
|
800
935
|
reopen_report: ReopenReport,
|
|
801
936
|
editor_open_safety_report: object | None = None,
|
|
802
937
|
) -> dict[str, str]:
|
|
803
|
-
document_warnings = getattr(document_report, "warnings", ())
|
|
804
938
|
editor_open_safety_ok = (
|
|
805
939
|
True
|
|
806
940
|
if editor_open_safety_report is None
|
|
@@ -809,7 +943,7 @@ def _hard_gates(
|
|
|
809
943
|
return {
|
|
810
944
|
"package_validation": "pass" if getattr(package_report, "ok", False) else "fail",
|
|
811
945
|
"document_errors": "pass" if getattr(document_report, "ok", False) else "fail",
|
|
812
|
-
"schema_lint":
|
|
946
|
+
"schema_lint": _schema_lint(document_report),
|
|
813
947
|
"reopen": "pass" if reopen_report.ok else "fail",
|
|
814
948
|
"editor_open_safety": "pass" if editor_open_safety_ok else "fail",
|
|
815
949
|
"id_integrity": "unavailable",
|
|
@@ -840,10 +974,11 @@ class Section:
|
|
|
840
974
|
else:
|
|
841
975
|
width = _mm_to_hwp_units(self.page.width_mm)
|
|
842
976
|
height = _mm_to_hwp_units(self.page.height_mm)
|
|
977
|
+
orientation, width, height = _hancom_page_form(self.page.orientation, width, height)
|
|
843
978
|
document.page.set_size(
|
|
844
979
|
width=width,
|
|
845
980
|
height=height,
|
|
846
|
-
orientation=
|
|
981
|
+
orientation=orientation,
|
|
847
982
|
section_index=section_index,
|
|
848
983
|
)
|
|
849
984
|
if self.margins is not None:
|
|
@@ -863,6 +998,7 @@ class Section:
|
|
|
863
998
|
self.footer.lower(document, section_index=section_index, preset=preset)
|
|
864
999
|
pending_native_tocs = native_toc_sink if native_toc_sink is not None else []
|
|
865
1000
|
for child in self.children:
|
|
1001
|
+
_anchor_mark(document, child)
|
|
866
1002
|
if isinstance(child, (Paragraph, PageBreak)):
|
|
867
1003
|
if isinstance(child, Paragraph):
|
|
868
1004
|
child.lower(document, preset=preset)
|
|
@@ -901,6 +1037,7 @@ class Section:
|
|
|
901
1037
|
)
|
|
902
1038
|
continue
|
|
903
1039
|
raise NotImplementedError(f"{type(child).__name__} lowering is not implemented yet")
|
|
1040
|
+
_anchor_resolve(document)
|
|
904
1041
|
if native_toc_sink is None:
|
|
905
1042
|
# standalone Section.lower call: resolve deferred TOCs now
|
|
906
1043
|
_apply_native_tocs(document, pending_native_tocs)
|
|
@@ -924,16 +1061,11 @@ class Document:
|
|
|
924
1061
|
return flags
|
|
925
1062
|
|
|
926
1063
|
def lower(self) -> HwpxDocument:
|
|
1064
|
+
_check_unique_keys(self.sections)
|
|
927
1065
|
document = HwpxDocument.new()
|
|
928
1066
|
preset = _builder_preset(self.preset)
|
|
929
1067
|
if self.metadata is not None:
|
|
930
|
-
|
|
931
|
-
("제목", self.metadata.title),
|
|
932
|
-
("작성자", self.metadata.author),
|
|
933
|
-
("기관", self.metadata.organization),
|
|
934
|
-
):
|
|
935
|
-
if value:
|
|
936
|
-
document.add_paragraph(f"{label}: {value}", inherit_style=False)
|
|
1068
|
+
_write_package_metadata(document, self.metadata)
|
|
937
1069
|
pending_native_tocs: list[tuple[int, NativeToc]] = []
|
|
938
1070
|
for index, section in enumerate(self.sections):
|
|
939
1071
|
section.lower(
|
|
@@ -945,8 +1077,17 @@ class Document:
|
|
|
945
1077
|
_apply_native_tocs(document, pending_native_tocs)
|
|
946
1078
|
return document
|
|
947
1079
|
|
|
1080
|
+
def _lower_with_anchors(self) -> tuple[HwpxDocument, dict[str, dict[str, int]]]:
|
|
1081
|
+
sink: list[_AnchorEntry] = []
|
|
1082
|
+
token = _ANCHOR_SINK.set(sink)
|
|
1083
|
+
try:
|
|
1084
|
+
document = self.lower()
|
|
1085
|
+
finally:
|
|
1086
|
+
_ANCHOR_SINK.reset(token)
|
|
1087
|
+
return document, _anchor_positions(document, sink)
|
|
1088
|
+
|
|
948
1089
|
def save_to_path(self, path: str | PathLike[str]) -> BuilderSaveReport:
|
|
949
|
-
document = self.
|
|
1090
|
+
document, anchors = self._lower_with_anchors()
|
|
950
1091
|
# Funnel the write through the single SavePipeline and keep its uniform
|
|
951
1092
|
# report (plan §2 Phase B). Transparent policy -> behaviour-identical to
|
|
952
1093
|
# the prior ``document.save_to_path`` for a from-scratch (new) document.
|
|
@@ -982,6 +1123,7 @@ class Document:
|
|
|
982
1123
|
feature_flags=feature_flags,
|
|
983
1124
|
editor_open_safety=editor_open_safety_report,
|
|
984
1125
|
visual_complete=visual_complete,
|
|
1126
|
+
anchors=anchors,
|
|
985
1127
|
)
|
|
986
1128
|
return report
|
|
987
1129
|
|
|
@@ -62,6 +62,10 @@ class BuilderSaveReport:
|
|
|
62
62
|
# through (plan §2 Phase B). Additive: ``None`` only if a caller builds a
|
|
63
63
|
# report by hand without going through ``Document.save_to_path``.
|
|
64
64
|
visual_complete: VisualCompleteReport | None = None
|
|
65
|
+
# ``key`` of each keyed Paragraph/Table -> ``{"section": i, "paragraph": j}``
|
|
66
|
+
# in the saved file (for a Table, the paragraph that holds it), so later
|
|
67
|
+
# edits need not find the node again by its text.
|
|
68
|
+
anchors: dict[str, dict[str, int]] = field(default_factory=dict)
|
|
65
69
|
|
|
66
70
|
def __post_init__(self) -> None:
|
|
67
71
|
hard_gates = dict(self.hard_gates)
|
|
@@ -80,6 +84,7 @@ class BuilderSaveReport:
|
|
|
80
84
|
"path": str(self.path),
|
|
81
85
|
"metadata": dict(self.metadata or {}),
|
|
82
86
|
"hard_gates": dict(self.hard_gates),
|
|
87
|
+
"anchors": {key: dict(value) for key, value in self.anchors.items()},
|
|
83
88
|
"fidelity_contract": {
|
|
84
89
|
"proves": list(FIDELITY_CONTRACT["proves"]),
|
|
85
90
|
"does_not_prove": list(FIDELITY_CONTRACT["does_not_prove"]),
|
|
@@ -333,6 +333,11 @@ def _extract_section_layout(section_path: str, section_xml: ET.Element) -> Secti
|
|
|
333
333
|
margin = page_pr.find(f"{_HP_TAG}margin") if page_pr is not None else None
|
|
334
334
|
margins = _margin_summary(margin)
|
|
335
335
|
page_width = _int_attr(page_pr, "width")
|
|
336
|
+
page_height = _int_attr(page_pr, "height")
|
|
337
|
+
# Hancom draws a page as stored only for landscape="WIDELY" (portrait);
|
|
338
|
+
# NARROWLY, the schema default, turns it. Report the page as drawn.
|
|
339
|
+
if page_pr is not None and page_pr.get("landscape") != "WIDELY":
|
|
340
|
+
page_width, page_height = page_height, page_width
|
|
336
341
|
body_width = None
|
|
337
342
|
if page_width is not None:
|
|
338
343
|
body_width = max(
|
|
@@ -345,7 +350,7 @@ def _extract_section_layout(section_path: str, section_xml: ET.Element) -> Secti
|
|
|
345
350
|
return SectionLayoutSummary(
|
|
346
351
|
section_path=section_path,
|
|
347
352
|
page_width=page_width,
|
|
348
|
-
page_height=
|
|
353
|
+
page_height=page_height,
|
|
349
354
|
margins=margins,
|
|
350
355
|
computed_body_width=body_width,
|
|
351
356
|
)
|