python-hwpx-automation 6.4.2__tar.gz → 6.5.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-6.4.2/src/python_hwpx_automation.egg-info → python_hwpx_automation-6.5.1}/PKG-INFO +8 -7
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/README.md +6 -5
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/pyproject.toml +2 -2
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/specialized.py +90 -1
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/identity.json +9 -9
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/dump.py +7 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/native.py +19 -4
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/commands.py +52 -17
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/document.py +57 -15
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/__init__.py +18 -2
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/builder/core.py +56 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/house_style/composition.py +9 -11
- python_hwpx_automation-6.5.1/src/hwpx_automation/office/house_style/org_chart.py +273 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/public-modules.json +5 -4
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/server.py +3 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/tool_bindings.py +3 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/tool_contract.py +12 -10
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1/src/python_hwpx_automation.egg-info}/PKG-INFO +8 -7
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/python_hwpx_automation.egg-info/SOURCES.txt +1 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/python_hwpx_automation.egg-info/requires.txt +1 -1
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/LICENSE +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/MANIFEST.in +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/NOTICE +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/setup.cfg +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/__init__.pyi +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/__main__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/agent_document.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/api.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/blind_eval.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/capabilities.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/compat.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/configuration.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/content.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/context.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/diff.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/document.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/formatting.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/handles.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/locations.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/locator.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/plan.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/resources.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/search.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/transactions.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/core/txn.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/document_state.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/errors.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/execution_lock.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/fastmcp_adapter.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/form_fill.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/form_output_models.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/_shared.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/agent_document.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/authoring.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/content_edit.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/form_fill.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/layout_style.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/quality_render.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/read_export.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/tracked_changes.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/handlers/workflow.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/hwp_converter.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/hwp_support.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/hwpx_ops.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/identity.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ingest_adapters.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/markdown_plan.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/mcp_cli.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/metadata/tools_meta.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/mixed_form.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/mutation_models.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/network_policy.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/_batch_verification.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/bundle.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/catalog.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/mapping.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/model.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/blueprint/replay.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/catalog.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/cli.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/form_plan.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/model.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/path.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/query.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/agent/story.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/advanced_generators.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/builder/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/builder/report.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/_support.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/composer.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/harvest.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/plan.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profile.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/profile.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/profile.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/design/validator.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/presets/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/presets/proposal.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/report_parser.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/style_profile.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/authoring/template_analyzer.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/charting/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/compliance/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/compliance/official_lint.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/compliance/pii.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/document_ops/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/document_ops/comparison.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/document_ops/mail_merge.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/document_ops/redline.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/evalplan/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/evalplan/runtime.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/compose.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/ir.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/measure.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/parser.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/exam/profile.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/classification.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fill_residue.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/apply.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/engine.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/measure.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/policy.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/report.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/seal.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/fit/wordbox.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/guidance.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/quality.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/split_run.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/form_fill/template_formfit.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/house_style/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/house_style/data/bank.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/house_style/data/genres.json +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/quality/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/quality/page_guard.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/_hancom_open_rate.ps1 +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/_render_hwpx.ps1 +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/_render_hwpx_mac.applescript +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/block_splits.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/detectors.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/diff.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/fixture_corpus.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/oracle.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/page_qa.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/qa_contracts.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/qa_metrics.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/rendering/worker.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/utilities/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/office/utilities/table_compute.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/_border_fill.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/composition.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/content_layout.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/context.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/form_fields.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/media.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/memo_style.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/package_validation.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/planning.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/preview_export.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/read_query.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/save_policy.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/tables.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/ops_services/transactions.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/preview_output_models.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/py.typed +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/quality.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/quality_generation.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/runtime.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/runtime_services.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/storage.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/upstream.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/utils/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/utils/helpers.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/visual_qa.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/__init__.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/adapters.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/dispatcher.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/models.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/policy.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/render_contracts.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/render_metrics.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/render_queue.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/render_security.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/render_transport.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/rendering.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/service.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/state_machine.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workflow/store.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/workspace.py +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/python_hwpx_automation.egg-info/dependency_links.txt +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/python_hwpx_automation.egg-info/entry_points.txt +0 -0
- {python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.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: 6.
|
|
3
|
+
Version: 6.5.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
|
|
@@ -20,7 +20,7 @@ Requires-Python: >=3.10
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
License-File: NOTICE
|
|
23
|
-
Requires-Dist: python-hwpx<6,>=5.
|
|
23
|
+
Requires-Dist: python-hwpx<6,>=5.5.0
|
|
24
24
|
Requires-Dist: pydantic<3,>=2.11
|
|
25
25
|
Requires-Dist: cryptography>=42.0
|
|
26
26
|
Requires-Dist: anyio>=4.0
|
|
@@ -74,11 +74,12 @@ Dynamic: license-file
|
|
|
74
74
|
|
|
75
75
|
<!-- release-state: release-approved -->
|
|
76
76
|
> [!NOTE]
|
|
77
|
-
> 이 저장소의 main은 아직 공개되지 않은 6.
|
|
78
|
-
>
|
|
79
|
-
>
|
|
80
|
-
>
|
|
81
|
-
>
|
|
77
|
+
> 이 저장소의 main은 아직 공개되지 않은 6.5.1 후보 — release-approved 상태(운영계획 장르 저작 —
|
|
78
|
+
> 표-기반 박스 조직도 `add_boxed_org_chart`·섹션칩 `compose_section_chip`·
|
|
79
|
+
> 장르 문법 `get_genre_grammar`, 계약 `f61d2c60c0aa0413`, 133 도구)입니다.
|
|
80
|
+
> 현재 공개 트레인은
|
|
81
|
+
> `python-hwpx 5.5.0 → python-hwpx-automation 6.4.2 → hwpx-plugin 1.4.1`
|
|
82
|
+
> (계약 `dbdbdfaac26148b7`, 2026-08-02 released)입니다. 공개 트레인은
|
|
82
83
|
> core·automation PyPI와 plugin GitHub
|
|
83
84
|
> Release·marketplace·실제 marketplace 설치까지 확인한 뒤 승격했으며, 기존
|
|
84
85
|
> 5.x 설치는 별도 발행된 `hwpx-mcp-server 5.1.1`이 보호합니다. 절차와 상태
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
<!-- release-state: release-approved -->
|
|
16
16
|
> [!NOTE]
|
|
17
|
-
> 이 저장소의 main은 아직 공개되지 않은 6.
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
17
|
+
> 이 저장소의 main은 아직 공개되지 않은 6.5.1 후보 — release-approved 상태(운영계획 장르 저작 —
|
|
18
|
+
> 표-기반 박스 조직도 `add_boxed_org_chart`·섹션칩 `compose_section_chip`·
|
|
19
|
+
> 장르 문법 `get_genre_grammar`, 계약 `f61d2c60c0aa0413`, 133 도구)입니다.
|
|
20
|
+
> 현재 공개 트레인은
|
|
21
|
+
> `python-hwpx 5.5.0 → python-hwpx-automation 6.4.2 → hwpx-plugin 1.4.1`
|
|
22
|
+
> (계약 `dbdbdfaac26148b7`, 2026-08-02 released)입니다. 공개 트레인은
|
|
22
23
|
> core·automation PyPI와 plugin GitHub
|
|
23
24
|
> Release·marketplace·실제 marketplace 설치까지 확인한 뒤 승격했으며, 기존
|
|
24
25
|
> 5.x 설치는 별도 발행된 `hwpx-mcp-server 5.1.1`이 보호합니다. 절차와 상태
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-hwpx-automation"
|
|
7
|
-
version = "6.
|
|
7
|
+
version = "6.5.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" }]
|
|
@@ -29,7 +29,7 @@ dependencies = [
|
|
|
29
29
|
# the bounded major here is what makes "no valid install has two declarers"
|
|
30
30
|
# true rather than hopeful, and prevents the next core major from silently
|
|
31
31
|
# breaking an already-published automation line.
|
|
32
|
-
"python-hwpx>=5.
|
|
32
|
+
"python-hwpx>=5.5.0,<6", # HWPX object model, OXML/OPC, format-native primitives
|
|
33
33
|
|
|
34
34
|
"pydantic>=2.11,<3",
|
|
35
35
|
"cryptography>=42.0",
|
|
@@ -7,7 +7,7 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import tempfile
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any, Literal, cast
|
|
11
11
|
|
|
12
12
|
from ..core.content import (
|
|
13
13
|
collect_full_text,
|
|
@@ -23,6 +23,11 @@ from ..office.authoring.advanced_generators import (
|
|
|
23
23
|
build_organization_chart as build_hwpx_organization_chart,
|
|
24
24
|
)
|
|
25
25
|
from ..office.compliance import DEFAULT_POLICY, detect_pii, mask_value
|
|
26
|
+
from ..office.house_style import load_bank, load_genres
|
|
27
|
+
from ..office.house_style.composition import (
|
|
28
|
+
compose_section_chip as compose_house_section_chip,
|
|
29
|
+
)
|
|
30
|
+
from ..office.house_style.org_chart import lower_boxed_org_chart
|
|
26
31
|
from ..office.document_ops import (
|
|
27
32
|
build_mail_merge as build_hwpx_mail_merge,
|
|
28
33
|
)
|
|
@@ -217,6 +222,90 @@ def build_organization_chart(
|
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
|
|
225
|
+
def add_boxed_org_chart(
|
|
226
|
+
filename: str,
|
|
227
|
+
hierarchy: dict,
|
|
228
|
+
accent_color: str = None,
|
|
229
|
+
dry_run: bool = False,
|
|
230
|
+
expected_revision: str = None,
|
|
231
|
+
) -> dict:
|
|
232
|
+
"""계층 트리를 실물 관례의 박스 조직도(표 캔버스+병합 박스+셀 테두리
|
|
233
|
+
커넥터)로 문서 끝에 그립니다. 각 노드는 {"label", "sublabel"?,
|
|
234
|
+
"children"?[]} 매핑이며 깊이 4·박스 40 초과는 typed 거부.
|
|
235
|
+
accent_color는 루트 박스 배경색. dry_run=True이면 저장하지 않습니다."""
|
|
236
|
+
path = resolve_path(filename)
|
|
237
|
+
guard = _revision_guard(path, expected_revision)
|
|
238
|
+
if guard is not None:
|
|
239
|
+
return guard
|
|
240
|
+
doc = open_doc(path)
|
|
241
|
+
summary = lower_boxed_org_chart(doc, hierarchy or {}, accent_color=accent_color)
|
|
242
|
+
result = {"orgChart": summary}
|
|
243
|
+
if dry_run:
|
|
244
|
+
return _with_dry_run_verification(result, doc, path)
|
|
245
|
+
verification = _save_doc_verification(doc, path)
|
|
246
|
+
return _with_save_verification(result, verification)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def compose_section_chip(
|
|
250
|
+
number: str,
|
|
251
|
+
title: str,
|
|
252
|
+
style: str = "box",
|
|
253
|
+
accent_color: str = None,
|
|
254
|
+
) -> dict:
|
|
255
|
+
"""운영계획 장르의 섹션 구분자(섹션칩)를 범용 document_plan block으로
|
|
256
|
+
합성합니다. style="box"(1×3 accent 칩) 또는 "inline"(번호 제목 헤딩).
|
|
257
|
+
파일은 쓰지 않습니다 — 반환 block을 document_plan에 끼워 넣으십시오."""
|
|
258
|
+
if style not in ("box", "inline"):
|
|
259
|
+
raise ValueError('style must be "box" or "inline"')
|
|
260
|
+
chip_style = cast('Literal["box", "inline"]', style)
|
|
261
|
+
block = compose_house_section_chip(
|
|
262
|
+
number, title, style=chip_style, accent_color=accent_color
|
|
263
|
+
)
|
|
264
|
+
result: dict = {"block": block, "next_tool": "validate_document_plan"}
|
|
265
|
+
if style == "box" and accent_color:
|
|
266
|
+
result["styling"] = {
|
|
267
|
+
"accentColor": accent_color,
|
|
268
|
+
"how": (
|
|
269
|
+
"문서 생성 후 이 칩 표의 번호 셀에 "
|
|
270
|
+
"format_table(fill_color=accentColor, row=0, col=0)을 적용"
|
|
271
|
+
),
|
|
272
|
+
}
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_genre_grammar(genre: str) -> dict:
|
|
277
|
+
"""장르 구조 문법·타이포 역할(하우스 스타일 뱅크)을 반환하는 read-only
|
|
278
|
+
조회입니다. 스킬이 per-문서 인스턴스화(번호 체계·칩 렌더·블록 선택·색)
|
|
279
|
+
판단에 사용합니다. 미보유 장르는 사용 가능 목록과 함께 typed 거부."""
|
|
280
|
+
catalog = load_genres()
|
|
281
|
+
genres = catalog.genres if hasattr(catalog, "genres") else {}
|
|
282
|
+
if genre not in genres:
|
|
283
|
+
raise ValueError(
|
|
284
|
+
f"unknown genre {genre!r}; available: {sorted(genres)}"
|
|
285
|
+
)
|
|
286
|
+
bank = load_bank()
|
|
287
|
+
entry = genres[genre]
|
|
288
|
+
payload = entry if isinstance(entry, dict) else entry.model_dump()
|
|
289
|
+
inherits = None
|
|
290
|
+
typography = payload.get("typography") if isinstance(payload, dict) else None
|
|
291
|
+
if isinstance(typography, dict):
|
|
292
|
+
inherits = typography.get("inherits")
|
|
293
|
+
profiles = bank.profiles if hasattr(bank, "profiles") else {}
|
|
294
|
+
inherited_profile = None
|
|
295
|
+
if inherits and inherits in profiles:
|
|
296
|
+
base = profiles[inherits]
|
|
297
|
+
inherited_profile = base if isinstance(base, dict) else base.model_dump()
|
|
298
|
+
return {
|
|
299
|
+
"genre": genre,
|
|
300
|
+
"grammar": payload,
|
|
301
|
+
"inheritedProfile": {"name": inherits, "profile": inherited_profile}
|
|
302
|
+
if inherited_profile
|
|
303
|
+
else None,
|
|
304
|
+
"sectionChipTool": "compose_section_chip",
|
|
305
|
+
"orgChartTool": "add_boxed_org_chart",
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
220
309
|
def scan_personal_info(filename: str | None = None, text: str | None = None) -> dict:
|
|
221
310
|
"""문서/텍스트의 개인정보(PII)를 탐지하는 read-only 감사 (원본값 미노출).
|
|
222
311
|
|
{python_hwpx_automation-6.4.2 → python_hwpx_automation-6.5.1}/src/hwpx_automation/identity.json
RENAMED
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
"releaseState": {
|
|
6
6
|
"status": "release-approved",
|
|
7
7
|
"candidate": {
|
|
8
|
-
"pythonHwpx": "5.
|
|
8
|
+
"pythonHwpx": "5.5.0",
|
|
9
9
|
"canonicalDistribution": "python-hwpx-automation",
|
|
10
|
-
"canonicalAutomation": "6.
|
|
10
|
+
"canonicalAutomation": "6.5.1",
|
|
11
11
|
"compatibilityDistribution": "hwpx-mcp-server",
|
|
12
|
-
"compatibility": "6.
|
|
13
|
-
"plugin": "1.
|
|
14
|
-
"contractHash": "
|
|
12
|
+
"compatibility": "6.5.1",
|
|
13
|
+
"plugin": "1.5.0",
|
|
14
|
+
"contractHash": "f61d2c60c0aa0413"
|
|
15
15
|
},
|
|
16
16
|
"currentPublic": {
|
|
17
|
-
"pythonHwpx": "5.
|
|
17
|
+
"pythonHwpx": "5.5.0",
|
|
18
18
|
"primaryDistribution": "python-hwpx-automation",
|
|
19
|
-
"primaryApplication": "6.
|
|
20
|
-
"plugin": "1.
|
|
21
|
-
"contractHash": "
|
|
19
|
+
"primaryApplication": "6.4.2",
|
|
20
|
+
"plugin": "1.4.1",
|
|
21
|
+
"contractHash": "dbdbdfaac26148b7"
|
|
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
|
},
|
|
@@ -276,6 +276,13 @@ def _host_run_path(record: NodeRecord, selected: Mapping[str, NodeRecord]) -> st
|
|
|
276
276
|
run_element = candidate.native.element
|
|
277
277
|
if parent is run_element or (element is not None and any(child is element for child in run_element)):
|
|
278
278
|
return candidate.path
|
|
279
|
+
# core 5.5.0+: notes are ctrl-wrapped inside the run (the real-Hancom
|
|
280
|
+
# contract) — the run hosts the wrapper, one hop above the note.
|
|
281
|
+
if element is not None and any(
|
|
282
|
+
child.tag.endswith("}ctrl") and any(grand is element for grand in child)
|
|
283
|
+
for child in run_element
|
|
284
|
+
):
|
|
285
|
+
return candidate.path
|
|
279
286
|
return None
|
|
280
287
|
|
|
281
288
|
|
|
@@ -115,15 +115,30 @@ def create_numbering(document: HwpxDocument, properties: dict[str, Any]) -> str:
|
|
|
115
115
|
def _move_to_host_run(paragraph: Any, native: Any, host_run: Any) -> None:
|
|
116
116
|
element = getattr(native, "element", native)
|
|
117
117
|
temporary_run = None
|
|
118
|
+
movable = element
|
|
118
119
|
for run in list(paragraph.element):
|
|
119
|
-
if _local_name(run)
|
|
120
|
-
|
|
120
|
+
if _local_name(run) != "run":
|
|
121
|
+
continue
|
|
122
|
+
for child in run:
|
|
123
|
+
if child is element:
|
|
124
|
+
temporary_run = run
|
|
125
|
+
movable = element
|
|
126
|
+
break
|
|
127
|
+
# core 5.5.0+: notes are ctrl-wrapped inside the run (the
|
|
128
|
+
# real-Hancom contract) — relocate the wrapper, not the note.
|
|
129
|
+
if _local_name(child) == "ctrl" and any(
|
|
130
|
+
grandchild is element for grandchild in child
|
|
131
|
+
):
|
|
132
|
+
temporary_run = run
|
|
133
|
+
movable = child
|
|
134
|
+
break
|
|
135
|
+
if temporary_run is not None:
|
|
121
136
|
break
|
|
122
137
|
if temporary_run is not None and temporary_run is not host_run.element:
|
|
123
|
-
temporary_run.remove(
|
|
138
|
+
temporary_run.remove(movable)
|
|
124
139
|
if len(temporary_run) == 0:
|
|
125
140
|
paragraph.element.remove(temporary_run)
|
|
126
|
-
host_run.element.append(
|
|
141
|
+
host_run.element.append(movable)
|
|
127
142
|
paragraph.section.mark_dirty()
|
|
128
143
|
_remove_empty_text_placeholders(host_run)
|
|
129
144
|
|
|
@@ -1020,6 +1020,57 @@ def _identity_kind(local: str) -> str:
|
|
|
1020
1020
|
}.get(local, "shape" if local in _SHAPE_KINDS else local)
|
|
1021
1021
|
|
|
1022
1022
|
|
|
1023
|
+
def _identity_attribute_names(local: str, node: Any) -> tuple[str, ...]:
|
|
1024
|
+
if local in {"p", "tbl", "pic", "memo", "fieldBegin", *_SHAPE_KINDS}:
|
|
1025
|
+
return tuple(name for name in ("id", "instid", "instId") if node.get(name) is not None)
|
|
1026
|
+
if local in {"footNote", "endNote"}:
|
|
1027
|
+
return tuple(name for name in ("instId", "id") if node.get(name) is not None)
|
|
1028
|
+
return ()
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def _refresh_one_identity(
|
|
1032
|
+
node: Any,
|
|
1033
|
+
local: str,
|
|
1034
|
+
allocate: Any,
|
|
1035
|
+
identity_map: list[dict[str, str]],
|
|
1036
|
+
field_ids: dict[str, str],
|
|
1037
|
+
) -> None:
|
|
1038
|
+
names = _identity_attribute_names(local, node)
|
|
1039
|
+
paired_identity = allocate() if len(names) > 1 and local in {"pic", *_SHAPE_KINDS} else None
|
|
1040
|
+
for name in names:
|
|
1041
|
+
old = str(node.get(name))
|
|
1042
|
+
new = paired_identity or allocate()
|
|
1043
|
+
node.set(name, new)
|
|
1044
|
+
identity_map.append(
|
|
1045
|
+
{"kind": _identity_kind(local), "attribute": name, "old": old, "new": new}
|
|
1046
|
+
)
|
|
1047
|
+
if local == "fieldBegin" and name == "id":
|
|
1048
|
+
field_ids[old] = new
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def _walk_refresh_identities(
|
|
1052
|
+
node: Any,
|
|
1053
|
+
inside_note: bool,
|
|
1054
|
+
allocate: Any,
|
|
1055
|
+
identity_map: list[dict[str, str]],
|
|
1056
|
+
field_ids: dict[str, str],
|
|
1057
|
+
) -> None:
|
|
1058
|
+
"""Refresh copyable identities in document order.
|
|
1059
|
+
|
|
1060
|
+
Real-Hancom note bodies keep paragraph id="0" inside their subList (the
|
|
1061
|
+
gold contract) — those are not document-order identities, so nothing
|
|
1062
|
+
inside a note is refreshed. Single-pass recursion keeps this correct on
|
|
1063
|
+
both element backends (lxml proxies break id()-sets).
|
|
1064
|
+
"""
|
|
1065
|
+
|
|
1066
|
+
local = _local_name(node)
|
|
1067
|
+
if not inside_note:
|
|
1068
|
+
_refresh_one_identity(node, local, allocate, identity_map, field_ids)
|
|
1069
|
+
child_inside = inside_note or local in {"footNote", "endNote"}
|
|
1070
|
+
for child in node:
|
|
1071
|
+
_walk_refresh_identities(child, child_inside, allocate, identity_map, field_ids)
|
|
1072
|
+
|
|
1073
|
+
|
|
1023
1074
|
def _refresh_copy_identities(document: HwpxDocument, clone: Any) -> list[dict[str, str]]:
|
|
1024
1075
|
used: set[str] = set()
|
|
1025
1076
|
max_numeric = 0
|
|
@@ -1043,23 +1094,7 @@ def _refresh_copy_identities(document: HwpxDocument, clone: Any) -> list[dict[st
|
|
|
1043
1094
|
|
|
1044
1095
|
identity_map: list[dict[str, str]] = []
|
|
1045
1096
|
field_ids: dict[str, str] = {}
|
|
1046
|
-
|
|
1047
|
-
local = _local_name(node)
|
|
1048
|
-
names: tuple[str, ...] = ()
|
|
1049
|
-
if local in {"p", "tbl", "pic", "memo", "fieldBegin", *_SHAPE_KINDS}:
|
|
1050
|
-
names = tuple(name for name in ("id", "instid", "instId") if node.get(name) is not None)
|
|
1051
|
-
elif local in {"footNote", "endNote"}:
|
|
1052
|
-
names = tuple(name for name in ("instId", "id") if node.get(name) is not None)
|
|
1053
|
-
paired_identity = allocate() if len(names) > 1 and local in {"pic", *_SHAPE_KINDS} else None
|
|
1054
|
-
for name in names:
|
|
1055
|
-
old = str(node.get(name))
|
|
1056
|
-
new = paired_identity or allocate()
|
|
1057
|
-
node.set(name, new)
|
|
1058
|
-
identity_map.append(
|
|
1059
|
-
{"kind": _identity_kind(local), "attribute": name, "old": old, "new": new}
|
|
1060
|
-
)
|
|
1061
|
-
if local == "fieldBegin" and name == "id":
|
|
1062
|
-
field_ids[old] = new
|
|
1097
|
+
_walk_refresh_identities(clone, False, allocate, identity_map, field_ids)
|
|
1063
1098
|
if field_ids:
|
|
1064
1099
|
for node in clone.iter():
|
|
1065
1100
|
if _local_name(node) != "fieldEnd":
|
|
@@ -419,24 +419,66 @@ class HwpxAgentDocument:
|
|
|
419
419
|
note = next(candidate for candidate in notes if candidate.element is child)
|
|
420
420
|
self._project_note(note, path, kind, object_indexes[kind])
|
|
421
421
|
elif local == "ctrl":
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
self._project_form_field(match, path, object_indexes["form-field"])
|
|
427
|
-
elif len(child) > 0 and all(
|
|
428
|
-
_local_name(control_child) in {"fieldEnd", "colPr", "secPr"}
|
|
429
|
-
for control_child in child
|
|
430
|
-
):
|
|
431
|
-
# A matched FORM field is projected from its fieldBegin. Its paired
|
|
432
|
-
# fieldEnd and section/column controls carry no additional subtree
|
|
433
|
-
# semantics and must not make an otherwise complete block unsupported.
|
|
434
|
-
continue
|
|
435
|
-
else:
|
|
436
|
-
record.mark_unsupported("ctrl")
|
|
422
|
+
self._project_ctrl_child(
|
|
423
|
+
child, paragraph, path, object_indexes,
|
|
424
|
+
fields_by_position, run_index, child_index, record,
|
|
425
|
+
)
|
|
437
426
|
elif local not in _IGNORED_INLINE_KINDS:
|
|
438
427
|
record.mark_unsupported(local)
|
|
439
428
|
|
|
429
|
+
def _project_ctrl_child(
|
|
430
|
+
self,
|
|
431
|
+
child: Any,
|
|
432
|
+
paragraph: Any,
|
|
433
|
+
path: SemanticPath,
|
|
434
|
+
object_indexes: Any,
|
|
435
|
+
fields_by_position: Any,
|
|
436
|
+
run_index: int,
|
|
437
|
+
child_index: int,
|
|
438
|
+
record: Any,
|
|
439
|
+
) -> None:
|
|
440
|
+
if self._project_wrapped_notes(child, paragraph, path, object_indexes):
|
|
441
|
+
return
|
|
442
|
+
matches = fields_by_position.get((run_index - 1, child_index), [])
|
|
443
|
+
if matches:
|
|
444
|
+
for match in matches:
|
|
445
|
+
object_indexes["form-field"] += 1
|
|
446
|
+
self._project_form_field(match, path, object_indexes["form-field"])
|
|
447
|
+
elif len(child) > 0 and all(
|
|
448
|
+
_local_name(control_child) in {"fieldEnd", "colPr", "secPr"}
|
|
449
|
+
for control_child in child
|
|
450
|
+
):
|
|
451
|
+
# A matched FORM field is projected from its fieldBegin. Its paired
|
|
452
|
+
# fieldEnd and section/column controls carry no additional subtree
|
|
453
|
+
# semantics and must not make an otherwise complete block unsupported.
|
|
454
|
+
return
|
|
455
|
+
else:
|
|
456
|
+
record.mark_unsupported("ctrl")
|
|
457
|
+
|
|
458
|
+
def _project_wrapped_notes(
|
|
459
|
+
self, ctrl: Any, paragraph: Any, path: SemanticPath, object_indexes: Any
|
|
460
|
+
) -> bool:
|
|
461
|
+
"""Project ctrl-wrapped footnotes/endnotes (the real-Hancom contract).
|
|
462
|
+
|
|
463
|
+
Returns True when the ctrl hosted at least one note — core 5.5.0+
|
|
464
|
+
wraps notes in ``<hp:ctrl>`` inside the run.
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
projected = False
|
|
468
|
+
for control_child in ctrl:
|
|
469
|
+
note_local = _local_name(control_child)
|
|
470
|
+
if note_local not in {"footNote", "endNote"}:
|
|
471
|
+
continue
|
|
472
|
+
kind = "footnote" if note_local == "footNote" else "endnote"
|
|
473
|
+
object_indexes[kind] += 1
|
|
474
|
+
notes = paragraph.footnotes if kind == "footnote" else paragraph.endnotes
|
|
475
|
+
note = next(
|
|
476
|
+
candidate for candidate in notes if candidate.element is control_child
|
|
477
|
+
)
|
|
478
|
+
self._project_note(note, path, kind, object_indexes[kind])
|
|
479
|
+
projected = True
|
|
480
|
+
return projected
|
|
481
|
+
|
|
440
482
|
def _project_table(self, paragraph: Any, element: Any, parent: SemanticPath, index: int) -> None:
|
|
441
483
|
table = next(candidate for candidate in paragraph.tables if candidate.element is element)
|
|
442
484
|
identity = _identity(element, "id")
|
|
@@ -1894,6 +1894,7 @@ def _normalize_block(raw_block: Any, *, index: int) -> DocumentBlock:
|
|
|
1894
1894
|
"caption": caption,
|
|
1895
1895
|
"columns": columns,
|
|
1896
1896
|
"rows": rows,
|
|
1897
|
+
**_plan_table_header_extras(raw_block),
|
|
1897
1898
|
}
|
|
1898
1899
|
if unit:
|
|
1899
1900
|
table_data["unit"] = unit
|
|
@@ -2268,9 +2269,14 @@ def _block_to_builder_nodes(block: DocumentBlock) -> tuple[Any, ...]:
|
|
|
2268
2269
|
caption = str(block.data.get("caption") or "").strip()
|
|
2269
2270
|
if caption:
|
|
2270
2271
|
nodes.append(BuilderParagraph(text=caption, style="heading"))
|
|
2272
|
+
show_header = block.data.get("showHeader") is not False
|
|
2271
2273
|
nodes.append(
|
|
2272
2274
|
BuilderTable(
|
|
2273
|
-
header=
|
|
2275
|
+
header=(
|
|
2276
|
+
tuple(str(column["label"]) for column in columns)
|
|
2277
|
+
if show_header
|
|
2278
|
+
else ()
|
|
2279
|
+
),
|
|
2274
2280
|
rows=tuple(
|
|
2275
2281
|
tuple(str(row.get(column["key"], "")) for column in columns)
|
|
2276
2282
|
for row in rows
|
|
@@ -2310,6 +2316,14 @@ def _plan_table_column_widths(columns: list[dict[str, Any]]) -> list[int]:
|
|
|
2310
2316
|
return widths
|
|
2311
2317
|
|
|
2312
2318
|
|
|
2319
|
+
def _plan_table_header_extras(raw_block: Mapping[str, Any]) -> dict[str, Any]:
|
|
2320
|
+
"""Carry the additive header-suppression flag (section chips, title bands)."""
|
|
2321
|
+
|
|
2322
|
+
if raw_block.get("showHeader") is False:
|
|
2323
|
+
return {"showHeader": False}
|
|
2324
|
+
return {}
|
|
2325
|
+
|
|
2326
|
+
|
|
2313
2327
|
def _normalize_columns(value: Any, *, index: int) -> list[dict[str, Any]]:
|
|
2314
2328
|
if not isinstance(value, list) or not value:
|
|
2315
2329
|
raise ValueError(f"blocks[{index}].columns must be a non-empty list")
|
|
@@ -2578,7 +2592,9 @@ def _add_builder_table(
|
|
|
2578
2592
|
table_node: BuilderTable,
|
|
2579
2593
|
tokens: Mapping[str, str],
|
|
2580
2594
|
) -> None:
|
|
2581
|
-
rows =
|
|
2595
|
+
rows = (
|
|
2596
|
+
[list(table_node.header)] if table_node.header else []
|
|
2597
|
+
) + [list(row) for row in table_node.rows]
|
|
2582
2598
|
if not rows:
|
|
2583
2599
|
raise ValueError("table must contain a header or at least one row")
|
|
2584
2600
|
column_count = max(len(row) for row in rows)
|
|
@@ -126,8 +126,64 @@ class _BuilderPreset:
|
|
|
126
126
|
return default_chars[level % len(default_chars)]
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
def _genre_preset(genre: str) -> "_BuilderPreset":
|
|
130
|
+
"""Preset driven by the house-style bank — no literal typography here."""
|
|
131
|
+
|
|
132
|
+
from ...house_style import load_bank, load_genres
|
|
133
|
+
|
|
134
|
+
catalog = load_genres()
|
|
135
|
+
genres = catalog.genres if hasattr(catalog, "genres") else {}
|
|
136
|
+
if genre not in genres:
|
|
137
|
+
raise ValueError(f"unknown genre preset: {genre!r}")
|
|
138
|
+
entry = genres[genre]
|
|
139
|
+
payload = entry if isinstance(entry, dict) else entry.model_dump()
|
|
140
|
+
typography = payload.get("typography") or {}
|
|
141
|
+
inherits = str(typography.get("inherits") or "")
|
|
142
|
+
bank = load_bank()
|
|
143
|
+
profiles = bank.profiles if hasattr(bank, "profiles") else {}
|
|
144
|
+
base = profiles.get(inherits) if inherits else None
|
|
145
|
+
base_payload = base if isinstance(base, dict) or base is None else base.model_dump()
|
|
146
|
+
return _GenreBankPreset(
|
|
147
|
+
name=f"genre:{genre}",
|
|
148
|
+
roles={**((base_payload or {}).get("roles") or {}), **(typography.get("roles") or {})},
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class _GenreBankPreset(_BuilderPreset):
|
|
154
|
+
"""Typography sourced from the packaged bank/genre data."""
|
|
155
|
+
|
|
156
|
+
roles: Mapping[str, Any] = field(default_factory=dict)
|
|
157
|
+
|
|
158
|
+
def _role(self, *names: str) -> Mapping[str, Any]:
|
|
159
|
+
for name in names:
|
|
160
|
+
value = self.roles.get(name)
|
|
161
|
+
if isinstance(value, Mapping):
|
|
162
|
+
return value
|
|
163
|
+
return {}
|
|
164
|
+
|
|
165
|
+
def heading_style(self, level: int) -> dict[str, Any]:
|
|
166
|
+
role = self._role("section_header", "section_chip_title", "title")
|
|
167
|
+
return {
|
|
168
|
+
"bold": True,
|
|
169
|
+
"size": int(role.get("sizePt") or (16 - (level - 1) * 2)),
|
|
170
|
+
"font": str(role.get("font") or "함초롬바탕"),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
def run_style(self, run: "Run") -> dict[str, Any]:
|
|
174
|
+
styles = super().run_style(run)
|
|
175
|
+
body = self._role("body")
|
|
176
|
+
if styles.get("font") is None and body.get("font"):
|
|
177
|
+
styles["font"] = str(body["font"])
|
|
178
|
+
if styles.get("size") is None and body.get("sizePt"):
|
|
179
|
+
styles["size"] = int(body["sizePt"])
|
|
180
|
+
return styles
|
|
181
|
+
|
|
182
|
+
|
|
129
183
|
def _builder_preset(value: str | None) -> _BuilderPreset:
|
|
130
184
|
normalized = (value or "default").strip().lower().replace("-", "_")
|
|
185
|
+
if normalized.startswith("genre:"):
|
|
186
|
+
return _genre_preset(normalized.split(":", 1)[1])
|
|
131
187
|
if normalized in {"", "default", "standard", "standard_korean_business"}:
|
|
132
188
|
return _BuilderPreset()
|
|
133
189
|
if normalized in {"government_report", "gov_report", "공문보고서"}:
|
|
@@ -26,25 +26,23 @@ class SectionChipPlan(BaseModel):
|
|
|
26
26
|
return normalized
|
|
27
27
|
|
|
28
28
|
def to_document_plan_block(self) -> dict[str, Any]:
|
|
29
|
-
metadata: dict[str, Any] = {
|
|
30
|
-
"generator": "section_chip",
|
|
31
|
-
"style": self.style,
|
|
32
|
-
"number": self.number,
|
|
33
|
-
}
|
|
34
|
-
if self.accent_color:
|
|
35
|
-
metadata["accentColor"] = self.accent_color
|
|
36
29
|
if self.style == "inline":
|
|
37
30
|
return {
|
|
38
31
|
"type": "heading",
|
|
39
32
|
"level": 1,
|
|
40
33
|
"text": f"{self.number} {self.title}",
|
|
41
|
-
"metadata": metadata,
|
|
42
34
|
}
|
|
43
35
|
return {
|
|
44
36
|
"type": "table",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
37
|
+
"showHeader": False,
|
|
38
|
+
"columns": [
|
|
39
|
+
{"key": "number", "label": "번호", "widthWeight": 2},
|
|
40
|
+
{"key": "gap", "label": "여백", "widthWeight": 1},
|
|
41
|
+
{"key": "title", "label": "제목", "widthWeight": 15},
|
|
42
|
+
],
|
|
43
|
+
"rows": [
|
|
44
|
+
{"number": self.number, "gap": "", "title": self.title}
|
|
45
|
+
],
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
|