python-hwpx 5.6.0__tar.gz → 5.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_hwpx-5.6.0/src/python_hwpx.egg-info → python_hwpx-5.7.0}/PKG-INFO +1 -1
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/pyproject.toml +1 -1
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/fields.py +115 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/document.py +62 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/paragraph.py +85 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0/src/python_hwpx.egg-info}/PKG-INFO +1 -1
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/python_hwpx.egg-info/SOURCES.txt +1 -0
- python_hwpx-5.7.0/tests/test_check_box_authoring.py +154 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/LICENSE +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/NOTICE +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/README.md +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/setup.cfg +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/_units.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/layout.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/media.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/memos.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/persistence.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/shapes.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/_document/tracked.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/body_patch.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/capabilities.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/data/Skeleton.hwpx +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/data/contract_docs/known-traps.md +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/data/contract_docs/mutation-semantics.md +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/data/contract_docs/recipes-traversal.md +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/data/contract_docs/support-matrix.md +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/authoring.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/eqedit.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/mathml.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/render.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/equation/tokens.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/errors.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/experimental.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/apply.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/engine.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/measure.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/policy.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/form_fit/report.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/ingest/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/ingest/base.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/ingest/hwpx_converter.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/layout/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/layout/lint.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/layout/report.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/mutation_report.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/opc/package.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/opc/relationships.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/opc/security.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/opc/xml_utils.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/_document_impl.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/_document_primitives.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/body.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/canonical_defaults.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/color.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/common.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/document.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/document_parts.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/header.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/header_part.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/memo.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/namespaces.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/numbering.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/objects.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/parser.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/run.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/schema.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/section.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/section_format.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/section_story.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/simple_parts.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/table.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/oxml/utils.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/package.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/patch.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/plan/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/plan/_execute.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/plan/_model.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/plan/_schema.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/plan/_validate.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/py.typed +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/ledger.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/policy.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/rendering.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/report.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/quality/save_pipeline.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/table_patch.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/templates.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/__init__.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/_schemas/header.xsd +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/_schemas/section.xsd +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/archive_cli.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/doc_diff.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/document_viewer.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/exporter.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/generic_inventory.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/id_integrity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/idempotence.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/ir_equality.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/layout_preview.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/mail_merge.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/markdown_export.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/object_finder.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/package_reconcile.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/package_validator.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/page_guard.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/read_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/recover.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/redline.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/repair.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/report_utils.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/roundtrip_diff.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/table_cleanup.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/table_navigation.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/template_analyzer.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/text_extract_cli.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/text_extractor.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/toc_author.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/toc_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/hwpx/tools/validator.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/python_hwpx.egg-info/entry_points.txt +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/python_hwpx.egg-info/requires.txt +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/src/python_hwpx.egg-info/top_level.txt +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_add_control.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_adoption_metrics_snapshot.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_authoring_defaults.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_authoring_quality_corpus.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_blind_eval_fixture.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_byte_patch_identity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_capabilities_surface.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_cell_line_spacing.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_chart_authoring.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_comment_node_robustness.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_contract_docs_sync.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_corpus_read_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_coverage_promotion.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_coverage_targets.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_deviations_registry.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_doc_diff.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_context_manager.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_facade_surface.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_formatting.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_ops_runtime_boundary.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_save_api.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_document_viewer.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_documentation_code_fences.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_edit_plan.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_equation_authoring.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_equation_converter.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_equation_render.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_error_messages.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_evalplan_core_primitives.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_existing_document_format_editing.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_form_field_authoring.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_form_fields.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_form_fill_core_primitives.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_form_fit.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_form_fit_integration.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_fuzz_catalog_derive_expected.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_fuzz_loop.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_fuzz_regressions.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_gap_closure_tools.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_golden_api.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_hp_tab_support.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_hwpxlib_corpus_read.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_id_generator_range.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_image_object_workflow.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_ingest.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_inline_models.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_integration_hwpx_compatibility.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_integration_roundtrip.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_layout_cache_scope.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_layout_lint.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_layout_preview.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_library_boundary_closure.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_llms_txt.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_mail_merge_xlsx.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_markdown_export.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_memo_and_style_editing.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_moved_surface_hints.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_mutation_report.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_namespace_handling.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_new_features.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_note_authoring_contract.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_opc_package.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_open_safety_corpus.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_oxml_modularization.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_oxml_parsing.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_packaging_license_metadata.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_packaging_py_typed.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_paragraph_keep_together.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_paragraph_section_management.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_product_boundary.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_public_artifact_hygiene.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_public_runtime_boundary.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_read_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_recover_broken_zip.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_redline_authoring.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_redline_verify.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_release_workflow_safety.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_repair_repack.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_report_utils.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_repr_snapshots.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_roundtrip_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_row_heights.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_save_pipeline.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_section_headers.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_set_paragraph_format_keep.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_shape_geometry_contract.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_skeleton_template_ids.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_split_cell.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_split_merged_cell.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_stable_surface.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_table_cleanup.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_table_navigation.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_table_patch.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_table_patch_dryrun.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_table_patch_m105.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_tables_default_border.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_template_analyzer_enrichment.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_text_extractor_annotations.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_toc_author.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_toc_fidelity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_validation_severity.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_validator_comment_nodes.py +0 -0
- {python_hwpx-5.6.0 → python_hwpx-5.7.0}/tests/test_version_metadata.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-hwpx"
|
|
7
|
-
version = "5.
|
|
7
|
+
version = "5.7.0"
|
|
8
8
|
description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -386,6 +386,121 @@ def add_form_field(
|
|
|
386
386
|
)
|
|
387
387
|
|
|
388
388
|
|
|
389
|
+
_CHECK_BOX_TAG = f"{_HP}checkBtn"
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _iter_check_boxes(doc: "HwpxDocument") -> list[dict[str, Any]]:
|
|
393
|
+
"""Every ``<hp:checkBtn>`` in document order, with its owning element."""
|
|
394
|
+
|
|
395
|
+
found: list[dict[str, Any]] = []
|
|
396
|
+
index = 0
|
|
397
|
+
for section_index, section in enumerate(doc.sections):
|
|
398
|
+
for element in section.element.iter(_CHECK_BOX_TAG):
|
|
399
|
+
found.append(
|
|
400
|
+
{
|
|
401
|
+
"index": index,
|
|
402
|
+
"sectionIndex": section_index,
|
|
403
|
+
"name": element.get("name", ""),
|
|
404
|
+
"caption": element.get("caption", ""),
|
|
405
|
+
"checked": element.get("value", "").upper() == "CHECKED",
|
|
406
|
+
"value": element.get("value", ""),
|
|
407
|
+
"_element": element,
|
|
408
|
+
"_section": section,
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
index += 1
|
|
412
|
+
return found
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def list_check_boxes(doc: "HwpxDocument") -> list[dict[str, Any]]:
|
|
416
|
+
"""Return check-box form objects in document order."""
|
|
417
|
+
|
|
418
|
+
return [
|
|
419
|
+
{key: value for key, value in match.items() if not key.startswith("_")}
|
|
420
|
+
for match in _iter_check_boxes(doc)
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def add_check_box(
|
|
425
|
+
doc: "HwpxDocument",
|
|
426
|
+
caption: str,
|
|
427
|
+
*,
|
|
428
|
+
checked: bool = False,
|
|
429
|
+
name: str | None = None,
|
|
430
|
+
paragraph: Any | None = None,
|
|
431
|
+
section: Any | None = None,
|
|
432
|
+
section_index: int | None = None,
|
|
433
|
+
) -> dict[str, Any]:
|
|
434
|
+
"""Create a check-box form object and return its payload.
|
|
435
|
+
|
|
436
|
+
The emitted XML follows the check-box contract measured against real
|
|
437
|
+
Hancom (specs/060): ``value`` selects ☑/□ and the ``<hp:formCharPr>`` child
|
|
438
|
+
is mandatory — without it Hancom refuses the document. The created object is
|
|
439
|
+
re-read through the standard :func:`list_check_boxes` reader, so creation
|
|
440
|
+
fails loudly if the ordinary consumer would not see it.
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
if not str(caption).strip():
|
|
444
|
+
raise ValueError("check box caption must be a non-empty string")
|
|
445
|
+
if paragraph is None:
|
|
446
|
+
paragraph = doc.add_paragraph(
|
|
447
|
+
"", section=section, section_index=section_index, include_run=False,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
control = paragraph.add_check_box(caption, checked=checked, name=name)
|
|
451
|
+
_clear_form_field_layout_cache(paragraph.element)
|
|
452
|
+
|
|
453
|
+
created_name = control.element.get("name", "")
|
|
454
|
+
for match in _iter_check_boxes(doc):
|
|
455
|
+
if match["_element"] is control.element:
|
|
456
|
+
return {
|
|
457
|
+
key: value for key, value in match.items() if not key.startswith("_")
|
|
458
|
+
}
|
|
459
|
+
raise RuntimeError(
|
|
460
|
+
f"created check box {created_name!r} was not recognized by the standard reader"
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def set_check_box(
|
|
465
|
+
doc: "HwpxDocument",
|
|
466
|
+
checked: bool,
|
|
467
|
+
*,
|
|
468
|
+
index: int | None = None,
|
|
469
|
+
name: str | None = None,
|
|
470
|
+
) -> dict[str, Any]:
|
|
471
|
+
"""Set a check box's state, selecting it by ``index`` or ``name``.
|
|
472
|
+
|
|
473
|
+
Exactly one selector is required — an ambiguous or missing selector is a
|
|
474
|
+
typed refusal, never a guess.
|
|
475
|
+
"""
|
|
476
|
+
|
|
477
|
+
if (index is None) == (name is None):
|
|
478
|
+
raise ValueError("provide exactly one of index or name")
|
|
479
|
+
matches = _iter_check_boxes(doc)
|
|
480
|
+
if index is not None:
|
|
481
|
+
chosen = [m for m in matches if m["index"] == index]
|
|
482
|
+
if not chosen:
|
|
483
|
+
raise ValueError(f"check box index not found: {index}")
|
|
484
|
+
else:
|
|
485
|
+
wanted = str(name).strip()
|
|
486
|
+
chosen = [m for m in matches if m["name"] == wanted]
|
|
487
|
+
if not chosen:
|
|
488
|
+
raise ValueError(f"check box name not found: {wanted}")
|
|
489
|
+
if len(chosen) > 1:
|
|
490
|
+
raise ValueError(
|
|
491
|
+
f"check box name is ambiguous ({len(chosen)} matches): {wanted}"
|
|
492
|
+
)
|
|
493
|
+
match = chosen[0]
|
|
494
|
+
match["_element"].set("value", "CHECKED" if checked else "UNCHECKED")
|
|
495
|
+
match["_section"].mark_dirty()
|
|
496
|
+
return {
|
|
497
|
+
key: value
|
|
498
|
+
for key, value in {**match, "checked": bool(checked),
|
|
499
|
+
"value": "CHECKED" if checked else "UNCHECKED"}.items()
|
|
500
|
+
if not key.startswith("_")
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
389
504
|
def _select_form_field(
|
|
390
505
|
doc: "HwpxDocument",
|
|
391
506
|
matches: Sequence[dict[str, Any]],
|
|
@@ -1462,6 +1462,68 @@ class HwpxDocument:
|
|
|
1462
1462
|
section_index=section_index,
|
|
1463
1463
|
)
|
|
1464
1464
|
|
|
1465
|
+
def list_check_boxes(self) -> list[dict[str, Any]]:
|
|
1466
|
+
"""Return check-box form objects (체크박스) in document order.
|
|
1467
|
+
|
|
1468
|
+
Each entry carries ``index``/``name``/``caption``/``checked``.
|
|
1469
|
+
"""
|
|
1470
|
+
|
|
1471
|
+
return _fields.list_check_boxes(self)
|
|
1472
|
+
|
|
1473
|
+
def add_check_box(
|
|
1474
|
+
self,
|
|
1475
|
+
caption: str,
|
|
1476
|
+
*,
|
|
1477
|
+
checked: bool = False,
|
|
1478
|
+
name: str | None = None,
|
|
1479
|
+
paragraph: HwpxOxmlParagraph | None = None,
|
|
1480
|
+
section: HwpxOxmlSection | None = None,
|
|
1481
|
+
section_index: int | None = None,
|
|
1482
|
+
) -> dict[str, Any]:
|
|
1483
|
+
"""Create a check-box form object (체크박스). **Experimental contract.**
|
|
1484
|
+
|
|
1485
|
+
Real Hancom draws ☑ when *checked* and □ otherwise, with *caption* beside
|
|
1486
|
+
the box and present in the rendered text layer. The created object is
|
|
1487
|
+
read back through :meth:`list_check_boxes` with no special-casing.
|
|
1488
|
+
|
|
1489
|
+
Note:
|
|
1490
|
+
Korean government forms specify a text ``[ ]`` + √ convention rather
|
|
1491
|
+
than this form object (시행규칙 별표 4 제10호), so this primitive is
|
|
1492
|
+
for forms that genuinely use Hancom check boxes — not for 공문서.
|
|
1493
|
+
|
|
1494
|
+
Args:
|
|
1495
|
+
caption: Label drawn beside the box (non-empty).
|
|
1496
|
+
checked: Initial state.
|
|
1497
|
+
name: Object name; generated when omitted.
|
|
1498
|
+
paragraph: Target paragraph (e.g. a table cell). A new paragraph is
|
|
1499
|
+
appended to *section* when omitted.
|
|
1500
|
+
"""
|
|
1501
|
+
|
|
1502
|
+
return _fields.add_check_box(
|
|
1503
|
+
self,
|
|
1504
|
+
caption,
|
|
1505
|
+
checked=checked,
|
|
1506
|
+
name=name,
|
|
1507
|
+
paragraph=paragraph,
|
|
1508
|
+
section=section,
|
|
1509
|
+
section_index=section_index,
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
def set_check_box(
|
|
1513
|
+
self,
|
|
1514
|
+
checked: bool,
|
|
1515
|
+
*,
|
|
1516
|
+
index: int | None = None,
|
|
1517
|
+
name: str | None = None,
|
|
1518
|
+
) -> dict[str, Any]:
|
|
1519
|
+
"""Set a check box's state, selecting it by ``index`` or ``name``.
|
|
1520
|
+
|
|
1521
|
+
Exactly one selector is required; an ambiguous name is refused rather
|
|
1522
|
+
than guessed.
|
|
1523
|
+
"""
|
|
1524
|
+
|
|
1525
|
+
return _fields.set_check_box(self, checked, index=index, name=name)
|
|
1526
|
+
|
|
1465
1527
|
|
|
1466
1528
|
def add_chart(
|
|
1467
1529
|
self,
|
|
@@ -1147,6 +1147,91 @@ class HwpxOxmlParagraph:
|
|
|
1147
1147
|
self.section.mark_dirty()
|
|
1148
1148
|
return HwpxOxmlInlineObject(ctrl1, self)
|
|
1149
1149
|
|
|
1150
|
+
def add_check_box(
|
|
1151
|
+
self,
|
|
1152
|
+
caption: str,
|
|
1153
|
+
*,
|
|
1154
|
+
checked: bool = False,
|
|
1155
|
+
name: str | None = None,
|
|
1156
|
+
width: int = 9921,
|
|
1157
|
+
height: int = 1984,
|
|
1158
|
+
char_pr_id_ref: str | int | None = None,
|
|
1159
|
+
) -> HwpxOxmlInlineObject:
|
|
1160
|
+
"""Insert a check-box form object (체크박스) at the end of this paragraph.
|
|
1161
|
+
|
|
1162
|
+
Emits the real-Hancom ``<hp:checkBtn>`` shape. Unlike the click-here
|
|
1163
|
+
field this is **not** wrapped in ``<hp:ctrl>`` — it is a direct child of
|
|
1164
|
+
the run, beside the ``<hp:t>`` node.
|
|
1165
|
+
|
|
1166
|
+
Hancom refuses to open a document whose ``checkBtn`` lacks
|
|
1167
|
+
``<hp:formCharPr>`` (measured: the same file with the element present
|
|
1168
|
+
opens, without it Hancom reports a read error and produces nothing), so
|
|
1169
|
+
the child block is always emitted. ``caption`` is drawn next to the box
|
|
1170
|
+
and appears in the rendered text layer; ``checked`` selects the
|
|
1171
|
+
``CHECKED``/``UNCHECKED`` value that draws ☑ or □.
|
|
1172
|
+
|
|
1173
|
+
Args:
|
|
1174
|
+
caption: Label drawn beside the box.
|
|
1175
|
+
checked: Initial state.
|
|
1176
|
+
name: Object name. Defaults to a generated ``CheckBox<N>``.
|
|
1177
|
+
width: Box + caption width in HWPUNIT.
|
|
1178
|
+
height: Box height in HWPUNIT.
|
|
1179
|
+
"""
|
|
1180
|
+
|
|
1181
|
+
run = self._create_run_for_object(char_pr_id_ref=char_pr_id_ref)
|
|
1182
|
+
check = _append_child(run, f"{_HP}checkBtn", {
|
|
1183
|
+
"caption": _sanitize_text(caption),
|
|
1184
|
+
"value": "CHECKED" if checked else "UNCHECKED",
|
|
1185
|
+
"radioGroupName": "",
|
|
1186
|
+
"triState": "0",
|
|
1187
|
+
"backStyle": "OPAQUE",
|
|
1188
|
+
"name": _sanitize_text(name) if name else f"CheckBox{_object_id()}",
|
|
1189
|
+
"foreColor": "#000000",
|
|
1190
|
+
"backColor": "#FFFFFF",
|
|
1191
|
+
"groupName": "",
|
|
1192
|
+
"tabStop": "1",
|
|
1193
|
+
"editable": "1",
|
|
1194
|
+
"tabOrder": "0",
|
|
1195
|
+
"enabled": "1",
|
|
1196
|
+
"borderTypeIDRef": "0",
|
|
1197
|
+
"drawFrame": "1",
|
|
1198
|
+
"printable": "1",
|
|
1199
|
+
"command": "",
|
|
1200
|
+
})
|
|
1201
|
+
_append_child(check, f"{_HP}formCharPr", {
|
|
1202
|
+
"charPrIDRef": str(char_pr_id_ref if char_pr_id_ref is not None else 0),
|
|
1203
|
+
"followContext": "0",
|
|
1204
|
+
"autoSz": "0",
|
|
1205
|
+
"wordWrap": "0",
|
|
1206
|
+
})
|
|
1207
|
+
_append_child(check, f"{_HP}sz", {
|
|
1208
|
+
"width": str(int(width)),
|
|
1209
|
+
"widthRelTo": "ABSOLUTE",
|
|
1210
|
+
"height": str(int(height)),
|
|
1211
|
+
"heightRelTo": "ABSOLUTE",
|
|
1212
|
+
"protect": "0",
|
|
1213
|
+
})
|
|
1214
|
+
_append_child(check, f"{_HP}pos", {
|
|
1215
|
+
"treatAsChar": "1",
|
|
1216
|
+
"affectLSpacing": "0",
|
|
1217
|
+
"flowWithText": "1",
|
|
1218
|
+
"allowOverlap": "1",
|
|
1219
|
+
"holdAnchorAndSO": "0",
|
|
1220
|
+
"vertRelTo": "PARA",
|
|
1221
|
+
"horzRelTo": "COLUMN",
|
|
1222
|
+
"vertAlign": "TOP",
|
|
1223
|
+
"horzAlign": "LEFT",
|
|
1224
|
+
"vertOffset": "0",
|
|
1225
|
+
"horzOffset": "0",
|
|
1226
|
+
})
|
|
1227
|
+
_append_child(check, f"{_HP}outMargin", {
|
|
1228
|
+
"left": "0", "right": "0", "top": "0", "bottom": "0",
|
|
1229
|
+
})
|
|
1230
|
+
_append_child(run, f"{_HP}t", {})
|
|
1231
|
+
|
|
1232
|
+
self.section.mark_dirty()
|
|
1233
|
+
return HwpxOxmlInlineObject(check, self)
|
|
1234
|
+
|
|
1150
1235
|
@property
|
|
1151
1236
|
def bookmarks(self) -> list[str]:
|
|
1152
1237
|
"""Return the names of all bookmarks in this paragraph."""
|
|
@@ -133,6 +133,7 @@ tests/test_byte_patch_identity.py
|
|
|
133
133
|
tests/test_capabilities_surface.py
|
|
134
134
|
tests/test_cell_line_spacing.py
|
|
135
135
|
tests/test_chart_authoring.py
|
|
136
|
+
tests/test_check_box_authoring.py
|
|
136
137
|
tests/test_comment_node_robustness.py
|
|
137
138
|
tests/test_contract_docs_sync.py
|
|
138
139
|
tests/test_corpus_read_fidelity.py
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""체크박스 양식개체 저작 계약 테스트 (specs/060 P0 실측 계약).
|
|
3
|
+
|
|
4
|
+
실한컴 실측으로 확정된 불변식을 고정한다:
|
|
5
|
+
- ``value`` 어휘는 ``CHECKED``/``UNCHECKED``(각각 ☑/□로 렌더)
|
|
6
|
+
- **``<hp:formCharPr>``는 필수 자식** — 없으면 한컴이 문서를 거부한다
|
|
7
|
+
(단일 변인 프로브로 확정). 구조 검증(open-safety/ID 무결성)은 이 결함을
|
|
8
|
+
잡지 못하므로 방출 형상 자체를 테스트로 고정한다.
|
|
9
|
+
- ``<hp:checkBtn>``은 ``<hp:ctrl>`` 래핑 없이 run 직속 자식(누름틀과 다름)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import zipfile
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from hwpx.document import HwpxDocument
|
|
19
|
+
|
|
20
|
+
HP = "{http://www.hancom.co.kr/hwpml/2011/paragraph}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _section_xml(path) -> str:
|
|
24
|
+
with zipfile.ZipFile(path) as archive:
|
|
25
|
+
return "".join(
|
|
26
|
+
archive.read(name).decode("utf-8")
|
|
27
|
+
for name in sorted(archive.namelist())
|
|
28
|
+
if name.endswith("section0.xml")
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_add_check_box_emits_the_measured_hancom_shape(tmp_path) -> None:
|
|
33
|
+
doc = HwpxDocument.new()
|
|
34
|
+
doc.add_check_box("동의합니다", checked=True, name="Agree")
|
|
35
|
+
path = tmp_path / "checkbox.hwpx"
|
|
36
|
+
doc.save_to_path(path)
|
|
37
|
+
doc.close()
|
|
38
|
+
|
|
39
|
+
xml = _section_xml(path)
|
|
40
|
+
assert '<hp:checkBtn' in xml
|
|
41
|
+
assert 'value="CHECKED"' in xml
|
|
42
|
+
assert 'caption="동의합니다"' in xml
|
|
43
|
+
assert 'name="Agree"' in xml
|
|
44
|
+
# 필수 자식 3종 — formCharPr 누락은 실한컴 거부 사유다.
|
|
45
|
+
for child in ("formCharPr", "sz", "pos"):
|
|
46
|
+
assert f"<hp:{child}" in xml, f"missing mandatory child: {child}"
|
|
47
|
+
# 누름틀과 달리 ctrl 래핑을 쓰지 않는다.
|
|
48
|
+
assert "<hp:ctrl><hp:checkBtn" not in xml
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_check_box_children_are_inside_the_element(tmp_path) -> None:
|
|
52
|
+
doc = HwpxDocument.new()
|
|
53
|
+
doc.add_check_box("자식 위치", checked=False)
|
|
54
|
+
path = tmp_path / "children.hwpx"
|
|
55
|
+
doc.save_to_path(path)
|
|
56
|
+
doc.close()
|
|
57
|
+
|
|
58
|
+
reopened = HwpxDocument.open(path)
|
|
59
|
+
checks = [
|
|
60
|
+
element
|
|
61
|
+
for section in reopened.sections
|
|
62
|
+
for element in section.element.iter(f"{HP}checkBtn")
|
|
63
|
+
]
|
|
64
|
+
assert len(checks) == 1
|
|
65
|
+
names = [child.tag.split("}")[-1] for child in checks[0]]
|
|
66
|
+
assert "formCharPr" in names and "sz" in names and "pos" in names
|
|
67
|
+
reopened.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_unchecked_is_the_default_and_round_trips(tmp_path) -> None:
|
|
71
|
+
doc = HwpxDocument.new()
|
|
72
|
+
doc.add_check_box("기본 상태")
|
|
73
|
+
path = tmp_path / "default.hwpx"
|
|
74
|
+
doc.save_to_path(path)
|
|
75
|
+
doc.close()
|
|
76
|
+
|
|
77
|
+
assert 'value="UNCHECKED"' in _section_xml(path)
|
|
78
|
+
reopened = HwpxDocument.open(path)
|
|
79
|
+
boxes = reopened.list_check_boxes()
|
|
80
|
+
assert [b["caption"] for b in boxes] == ["기본 상태"]
|
|
81
|
+
assert boxes[0]["checked"] is False
|
|
82
|
+
reopened.close()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_list_and_set_by_index_and_name(tmp_path) -> None:
|
|
86
|
+
doc = HwpxDocument.new()
|
|
87
|
+
doc.add_check_box("첫째", name="First")
|
|
88
|
+
doc.add_check_box("둘째", checked=True, name="Second")
|
|
89
|
+
|
|
90
|
+
listed = doc.list_check_boxes()
|
|
91
|
+
assert [(b["index"], b["name"], b["checked"]) for b in listed] == [
|
|
92
|
+
(0, "First", False),
|
|
93
|
+
(1, "Second", True),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
assert doc.set_check_box(True, index=0)["checked"] is True
|
|
97
|
+
assert doc.set_check_box(False, name="Second")["value"] == "UNCHECKED"
|
|
98
|
+
assert [b["checked"] for b in doc.list_check_boxes()] == [True, False]
|
|
99
|
+
|
|
100
|
+
path = tmp_path / "toggled.hwpx"
|
|
101
|
+
doc.save_to_path(path)
|
|
102
|
+
doc.close()
|
|
103
|
+
reopened = HwpxDocument.open(path)
|
|
104
|
+
assert [b["checked"] for b in reopened.list_check_boxes()] == [True, False]
|
|
105
|
+
reopened.close()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_selector_and_caption_refusals_are_typed() -> None:
|
|
109
|
+
doc = HwpxDocument.new()
|
|
110
|
+
doc.add_check_box("하나", name="Dup")
|
|
111
|
+
doc.add_check_box("둘", name="Dup")
|
|
112
|
+
|
|
113
|
+
with pytest.raises(ValueError, match="exactly one of index or name"):
|
|
114
|
+
doc.set_check_box(True)
|
|
115
|
+
with pytest.raises(ValueError, match="exactly one of index or name"):
|
|
116
|
+
doc.set_check_box(True, index=0, name="Dup")
|
|
117
|
+
with pytest.raises(ValueError, match="ambiguous"):
|
|
118
|
+
doc.set_check_box(True, name="Dup")
|
|
119
|
+
with pytest.raises(ValueError, match="not found"):
|
|
120
|
+
doc.set_check_box(True, name="없는이름")
|
|
121
|
+
with pytest.raises(ValueError, match="index not found"):
|
|
122
|
+
doc.set_check_box(True, index=99)
|
|
123
|
+
with pytest.raises(ValueError, match="non-empty"):
|
|
124
|
+
doc.add_check_box(" ")
|
|
125
|
+
doc.close()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_check_box_inside_a_table_cell(tmp_path) -> None:
|
|
129
|
+
doc = HwpxDocument.new()
|
|
130
|
+
table = doc.add_table(2, 2)
|
|
131
|
+
cell_paragraph = table.rows[0].cells[1].paragraphs[0]
|
|
132
|
+
cell_paragraph.add_check_box("셀 안", checked=True)
|
|
133
|
+
|
|
134
|
+
path = tmp_path / "cell.hwpx"
|
|
135
|
+
doc.save_to_path(path)
|
|
136
|
+
doc.close()
|
|
137
|
+
|
|
138
|
+
reopened = HwpxDocument.open(path)
|
|
139
|
+
boxes = reopened.list_check_boxes()
|
|
140
|
+
assert [(b["caption"], b["checked"]) for b in boxes] == [("셀 안", True)]
|
|
141
|
+
reopened.close()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_authored_document_passes_open_safety(tmp_path) -> None:
|
|
145
|
+
from hwpx.tools.package_validator import validate_editor_open_safety
|
|
146
|
+
|
|
147
|
+
doc = HwpxDocument.new()
|
|
148
|
+
doc.add_check_box("열림 안전", checked=True)
|
|
149
|
+
path = tmp_path / "safe.hwpx"
|
|
150
|
+
doc.save_to_path(path)
|
|
151
|
+
doc.close()
|
|
152
|
+
|
|
153
|
+
report = validate_editor_open_safety(path).to_dict()
|
|
154
|
+
assert report["ok"] is True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|