python-hwpx 6.8.0__tar.gz → 6.9.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.
Files changed (475) hide show
  1. {python_hwpx-6.8.0/src/python_hwpx.egg-info → python_hwpx-6.9.0}/PKG-INFO +9 -4
  2. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/README.md +8 -3
  3. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/pyproject.toml +7 -1
  4. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/fields.py +1 -1
  5. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/layout.py +46 -25
  6. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/media.py +45 -8
  7. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/memos.py +3 -1
  8. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/shapes.py +2 -2
  9. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/tables.py +49 -3
  10. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/shapes.py +15 -7
  11. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/body_patch.py +27 -12
  12. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/capabilities.py +1 -1
  13. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/data/contract_docs/support-matrix.md +4 -4
  14. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/document.py +35 -1
  15. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/errors.py +20 -1
  16. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/_glyph_table.py +3328 -142
  17. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/measure.py +836 -94
  18. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/docinfo_writer.py +4 -3
  19. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/header_xml.py +4 -3
  20. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/package.py +70 -3
  21. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/records.py +2 -0
  22. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/section_writer.py +6 -5
  23. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/section_xml.py +69 -6
  24. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/shape_xml.py +6 -4
  25. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/shapes.py +22 -16
  26. python_hwpx-6.9.0/src/hwpx/layout/line_cache.py +636 -0
  27. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/layout/lint.py +4 -3
  28. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/layout/pages.py +1114 -225
  29. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/__init__.py +4 -0
  30. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/_document_primitives.py +30 -19
  31. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/_paragraph_text_edit.py +5 -11
  32. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/body.py +3 -3
  33. python_hwpx-6.9.0/src/hwpx/oxml/container.py +377 -0
  34. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/curves.py +10 -3
  35. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/document_parts.py +38 -5
  36. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/dutmal_compose.py +1 -1
  37. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/header.py +4 -0
  38. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/header_compat.py +6 -0
  39. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/header_fonts.py +75 -4
  40. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/memo.py +4 -1
  41. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/note_authoring.py +2 -0
  42. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/objects.py +145 -215
  43. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/paragraph.py +54 -81
  44. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/run.py +1 -0
  45. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/section.py +69 -15
  46. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/section_format.py +185 -54
  47. python_hwpx-6.9.0/src/hwpx/oxml/shape_position.py +600 -0
  48. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/table.py +6 -4
  49. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/table_sizes.py +53 -1
  50. python_hwpx-6.9.0/src/hwpx/oxml/table_structure.py +166 -0
  51. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/utils.py +28 -0
  52. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/patch.py +11 -8
  53. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/table_patch.py +428 -56
  54. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/package_validator.py +27 -4
  55. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/toc_author.py +1 -1
  56. {python_hwpx-6.8.0 → python_hwpx-6.9.0/src/python_hwpx.egg-info}/PKG-INFO +9 -4
  57. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/python_hwpx.egg-info/SOURCES.txt +22 -0
  58. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_authoring_defaults.py +63 -0
  59. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_body_patch.py +28 -0
  60. python_hwpx-6.9.0/tests/test_cell_edit_holder_cache.py +101 -0
  61. python_hwpx-6.9.0/tests/test_column_widths_as_shares.py +119 -0
  62. python_hwpx-6.9.0/tests/test_container_members.py +216 -0
  63. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_coverage_ledger.py +2 -1
  64. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_curve_connector_authoring.py +19 -0
  65. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_formatting.py +11 -4
  66. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_metadata.py +2 -2
  67. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_drop_cap_authoring.py +2 -2
  68. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_dutmal_compose_authoring.py +2 -2
  69. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_fit_hancom_rules.py +632 -5
  70. python_hwpx-6.9.0/tests/test_gap_and_new_number_values.py +143 -0
  71. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_header_compat_authoring.py +26 -0
  72. python_hwpx-6.9.0/tests/test_header_list_lookups.py +71 -0
  73. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hwp5_hancom_files.py +10 -1
  74. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hwp5_open.py +206 -7
  75. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hwp5_save.py +82 -16
  76. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_image_object_workflow.py +60 -0
  77. python_hwpx-6.9.0/tests/test_insert_column_by_clone.py +200 -0
  78. python_hwpx-6.9.0/tests/test_insert_row_sides.py +193 -0
  79. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_layout_cache_scope.py +95 -5
  80. python_hwpx-6.9.0/tests/test_layout_line_cache.py +658 -0
  81. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_layout_page_estimate.py +877 -22
  82. python_hwpx-6.9.0/tests/test_line_break_writing.py +86 -0
  83. python_hwpx-6.9.0/tests/test_list_level_range.py +53 -0
  84. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_memo_fields.py +31 -0
  85. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_namespace_hygiene.py +4 -1
  86. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_native_edit_contract.py +1 -1
  87. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_tables.py +21 -0
  88. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_oxml_modularization.py +7 -3
  89. python_hwpx-6.9.0/tests/test_page_and_list_values.py +161 -0
  90. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_property_lookup.py +32 -1
  91. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_section_management.py +11 -0
  92. python_hwpx-6.9.0/tests/test_picture_adjustments.py +256 -0
  93. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_product_boundary.py +4 -1
  94. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_release_workflow_safety.py +2 -2
  95. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_run_choice_atom_authoring.py +7 -6
  96. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_section_columns.py +91 -1
  97. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_set_column_widths_uniform_split.py +6 -5
  98. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_shape_position_frames.py +35 -1
  99. python_hwpx-6.9.0/tests/test_shape_size_values.py +212 -0
  100. python_hwpx-6.9.0/tests/test_split_cell.py +224 -0
  101. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_stale_layout_cache_sweep.py +13 -1
  102. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_style_font_border_reads.py +51 -0
  103. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_tab_character_writing.py +8 -4
  104. python_hwpx-6.9.0/tests/test_table_clone_identities.py +91 -0
  105. python_hwpx-6.9.0/tests/test_table_column_floor.py +119 -0
  106. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_label.py +2 -2
  107. python_hwpx-6.9.0/tests/test_table_ops_cells_without_geometry.py +61 -0
  108. python_hwpx-6.9.0/tests/test_table_ops_delete_every_line.py +44 -0
  109. python_hwpx-6.9.0/tests/test_table_ops_delete_zones.py +74 -0
  110. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_patch.py +15 -2
  111. python_hwpx-6.9.0/tests/test_table_structure_edits.py +281 -0
  112. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_toc_author.py +1 -1
  113. python_hwpx-6.8.0/src/hwpx/oxml/shape_position.py +0 -282
  114. python_hwpx-6.8.0/tests/test_split_cell.py +0 -112
  115. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/LICENSE +0 -0
  116. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/NOTICE +0 -0
  117. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/setup.cfg +0 -0
  118. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/__init__.py +0 -0
  119. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/__init__.py +0 -0
  120. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/_legacy.py +0 -0
  121. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/_resolve.py +0 -0
  122. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/_units.py +0 -0
  123. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/headings.py +0 -0
  124. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/highlight.py +0 -0
  125. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/__init__.py +0 -0
  126. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/_base.py +0 -0
  127. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/fields.py +0 -0
  128. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/media.py +0 -0
  129. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/notes.py +0 -0
  130. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/page.py +0 -0
  131. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/parts.py +0 -0
  132. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/refs.py +0 -0
  133. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/styles.py +0 -0
  134. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/text.py +0 -0
  135. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/ns/tracking.py +0 -0
  136. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/persistence.py +0 -0
  137. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_document/tracked.py +0 -0
  138. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/_moved_modules.py +0 -0
  139. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/data/Skeleton.hwpx +0 -0
  140. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/data/contract_docs/known-traps.md +0 -0
  141. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/data/contract_docs/mutation-semantics.md +0 -0
  142. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/data/contract_docs/recipes-traversal.md +0 -0
  143. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/__init__.py +0 -0
  144. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/authoring.py +0 -0
  145. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/eqedit.py +0 -0
  146. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/mathml.py +0 -0
  147. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/measure.py +0 -0
  148. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/render.py +0 -0
  149. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/equation/tokens.py +0 -0
  150. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/experimental.py +0 -0
  151. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/__init__.py +0 -0
  152. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/apply.py +0 -0
  153. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/engine.py +0 -0
  154. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/policy.py +0 -0
  155. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/form_fit/report.py +0 -0
  156. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/__init__.py +0 -0
  157. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/binary.py +0 -0
  158. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/bodytext.py +0 -0
  159. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/cfb.py +0 -0
  160. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/controls.py +0 -0
  161. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/docinfo.py +0 -0
  162. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/errors.py +0 -0
  163. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/fileheader.py +0 -0
  164. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/owpml.py +0 -0
  165. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/reader.py +0 -0
  166. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/section_common.py +0 -0
  167. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/summary.py +0 -0
  168. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/hwp5/writer.py +0 -0
  169. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/ingest/__init__.py +0 -0
  170. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/ingest/base.py +0 -0
  171. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/ingest/hwpx_converter.py +0 -0
  172. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/layout/__init__.py +0 -0
  173. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/layout/report.py +0 -0
  174. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/model.py +0 -0
  175. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/mutation_report.py +0 -0
  176. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/__init__.py +0 -0
  177. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/binary_item.py +0 -0
  178. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/checkbox.py +0 -0
  179. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/form_field.py +0 -0
  180. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/highlight.py +0 -0
  181. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/results.py +0 -0
  182. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/objects/tracked.py +0 -0
  183. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/opc/package.py +0 -0
  184. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/opc/relationships.py +0 -0
  185. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/opc/security.py +0 -0
  186. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/opc/xml_utils.py +0 -0
  187. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/_document_impl.py +0 -0
  188. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/canonical_defaults.py +0 -0
  189. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/color.py +0 -0
  190. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/common.py +0 -0
  191. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/document.py +0 -0
  192. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/document_metadata.py +0 -0
  193. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/drop_cap.py +0 -0
  194. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/field_marks.py +0 -0
  195. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/header_part.py +0 -0
  196. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/history_part.py +0 -0
  197. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/hyperlink_form.py +0 -0
  198. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/master_page.py +0 -0
  199. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/master_page_authoring.py +0 -0
  200. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/namespaces.py +0 -0
  201. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/numbering.py +0 -0
  202. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/numbering_kinds.py +0 -0
  203. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/paragraph_heading.py +0 -0
  204. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/parser.py +0 -0
  205. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/schema.py +0 -0
  206. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/section_layout.py +0 -0
  207. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/section_story.py +0 -0
  208. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/settings.py +0 -0
  209. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/simple_parts.py +0 -0
  210. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/table_merge.py +0 -0
  211. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/oxml/version_part.py +0 -0
  212. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/package.py +0 -0
  213. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/plan/__init__.py +0 -0
  214. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/plan/_execute.py +0 -0
  215. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/plan/_model.py +0 -0
  216. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/plan/_schema.py +0 -0
  217. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/plan/_validate.py +0 -0
  218. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/py.typed +0 -0
  219. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/__init__.py +0 -0
  220. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/ledger.py +0 -0
  221. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/policy.py +0 -0
  222. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/rendering.py +0 -0
  223. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/report.py +0 -0
  224. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/quality/save_pipeline.py +0 -0
  225. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/templates.py +0 -0
  226. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/__init__.py +0 -0
  227. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/header.xsd +0 -0
  228. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/owpml-body.xsd +0 -0
  229. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/owpml-core.xsd +0 -0
  230. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/owpml-header.xsd +0 -0
  231. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/owpml-paralist.xsd +0 -0
  232. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/owpml-xml.xsd +0 -0
  233. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/_schemas/section.xsd +0 -0
  234. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/archive_cli.py +0 -0
  235. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/doc_diff.py +0 -0
  236. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/document_merge.py +0 -0
  237. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/document_viewer.py +0 -0
  238. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/exporter.py +0 -0
  239. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/generic_inventory.py +0 -0
  240. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/id_integrity.py +0 -0
  241. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/idempotence.py +0 -0
  242. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/ir_equality.py +0 -0
  243. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/layout_preview.py +0 -0
  244. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/mail_merge.py +0 -0
  245. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/markdown_export.py +0 -0
  246. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/object_finder.py +0 -0
  247. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/package_reconcile.py +0 -0
  248. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/page_guard.py +0 -0
  249. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/read_fidelity.py +0 -0
  250. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/recover.py +0 -0
  251. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/redline.py +0 -0
  252. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/repair.py +0 -0
  253. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/report_utils.py +0 -0
  254. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/roundtrip_diff.py +0 -0
  255. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/table_cleanup.py +0 -0
  256. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/table_navigation.py +0 -0
  257. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/template_analyzer.py +0 -0
  258. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/text_extract_cli.py +0 -0
  259. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/text_extractor.py +0 -0
  260. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/toc_fidelity.py +0 -0
  261. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/hwpx/tools/validator.py +0 -0
  262. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
  263. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/python_hwpx.egg-info/entry_points.txt +0 -0
  264. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/python_hwpx.egg-info/requires.txt +0 -0
  265. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/src/python_hwpx.egg-info/top_level.txt +0 -0
  266. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_add_control.py +0 -0
  267. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_add_heading.py +0 -0
  268. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_adoption_metrics_snapshot.py +0 -0
  269. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_arc_authoring.py +0 -0
  270. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_authoring_quality_corpus.py +0 -0
  271. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_blind_eval_fixture.py +0 -0
  272. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_border_fill_image_gradient.py +0 -0
  273. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_border_line_types.py +0 -0
  274. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_build_element_census.py +0 -0
  275. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_byte_patch_identity.py +0 -0
  276. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_capabilities_surface.py +0 -0
  277. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_cell_border_neighbors.py +0 -0
  278. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_cell_fields.py +0 -0
  279. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_cell_line_spacing.py +0 -0
  280. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_cell_margins.py +0 -0
  281. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_cell_text_rewrite_semantics.py +0 -0
  282. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_char_pr_child_order.py +0 -0
  283. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_character_width_ratio.py +0 -0
  284. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_chart_authoring.py +0 -0
  285. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_check_box_authoring.py +0 -0
  286. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_color_arguments.py +0 -0
  287. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_column_break.py +0 -0
  288. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_comment_node_robustness.py +0 -0
  289. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_container_authoring.py +0 -0
  290. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_contract_docs_sync.py +0 -0
  291. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_corpus_read_fidelity.py +0 -0
  292. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_coverage_promotion.py +0 -0
  293. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_coverage_targets.py +0 -0
  294. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_deviations_registry.py +0 -0
  295. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_doc_diff.py +0 -0
  296. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_context_manager.py +0 -0
  297. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_facade_surface.py +0 -0
  298. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_merge.py +0 -0
  299. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_ops_runtime_boundary.py +0 -0
  300. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_save_api.py +0 -0
  301. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_document_viewer.py +0 -0
  302. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_documentation_code_fences.py +0 -0
  303. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_edit_fidelity_corpus.py +0 -0
  304. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_edit_plan.py +0 -0
  305. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_editor_surface_inventory.py +0 -0
  306. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_engine_surface_6_0.py +0 -0
  307. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equalize_widths_per_row.py +0 -0
  308. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_authoring.py +0 -0
  309. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_converter.py +0 -0
  310. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_measure_hancom_saved.py +0 -0
  311. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_render.py +0 -0
  312. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_size.py +0 -0
  313. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_equation_width_hancom_boxes.py +0 -0
  314. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_error_contract.py +0 -0
  315. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_error_messages.py +0 -0
  316. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_evalplan_core_primitives.py +0 -0
  317. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_existing_document_format_editing.py +0 -0
  318. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_export_captions_dutmal.py +0 -0
  319. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_export_cell_text_spaces.py +0 -0
  320. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_export_list_labels.py +0 -0
  321. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_export_notes.py +0 -0
  322. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_export_text_boxes.py +0 -0
  323. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_exporter_table_text.py +0 -0
  324. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_field_marks.py +0 -0
  325. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_field_type_ids.py +0 -0
  326. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_fill_image_hancom_modes.py +0 -0
  327. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_field_authoring.py +0 -0
  328. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_fields.py +0 -0
  329. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_fill_core_primitives.py +0 -0
  330. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_fit.py +0 -0
  331. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_form_fit_integration.py +0 -0
  332. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_fuzz_catalog_derive_expected.py +0 -0
  333. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_fuzz_loop.py +0 -0
  334. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_fuzz_regressions.py +0 -0
  335. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_gap_closure_tools.py +0 -0
  336. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_golden_api.py +0 -0
  337. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_header_control_before_field.py +0 -0
  338. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_heading_style_shapes.py +0 -0
  339. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_highlight_authoring.py +0 -0
  340. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hp_tab_support.py +0 -0
  341. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hwp5_container.py +0 -0
  342. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hwpxlib_corpus_read.py +0 -0
  343. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_hyperlink_hancom_form.py +0 -0
  344. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_id_generator_range.py +0 -0
  345. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_id_integrity.py +0 -0
  346. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_index_mark.py +0 -0
  347. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ingest.py +0 -0
  348. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_inline_models.py +0 -0
  349. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_insert_row_merged_cells.py +0 -0
  350. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_instid_attribute_case.py +0 -0
  351. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_integration_hwpx_compatibility.py +0 -0
  352. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_integration_roundtrip.py +0 -0
  353. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_internal_calls_skip_moved_names.py +0 -0
  354. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_layer_leak_guard.py +0 -0
  355. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_layout_lint.py +0 -0
  356. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_layout_preview.py +0 -0
  357. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_lazy_hwp5_import.py +0 -0
  358. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_lazy_tools_import.py +0 -0
  359. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_lazy_tools_submodules.py +0 -0
  360. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_library_boundary_closure.py +0 -0
  361. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_line_width_arguments.py +0 -0
  362. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_list_continue.py +0 -0
  363. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_list_defaults_vs_hancom.py +0 -0
  364. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_llms_txt.py +0 -0
  365. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_mail_merge_xlsx.py +0 -0
  366. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_manifest_resolve_once.py +0 -0
  367. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_markdown_export.py +0 -0
  368. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_markdown_export_shape_text.py +0 -0
  369. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_master_page_authoring.py +0 -0
  370. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_master_page_pages_taken.py +0 -0
  371. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_memo_and_style_editing.py +0 -0
  372. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_memo_shape_authoring.py +0 -0
  373. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_merge_cells_hancom_files.py +0 -0
  374. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_merge_cells_keeps_contents.py +0 -0
  375. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_moved_module_imports.py +0 -0
  376. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_moved_surface_hints.py +0 -0
  377. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_mutation_report.py +0 -0
  378. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_namespace_handling.py +0 -0
  379. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_namespace_normalization.py +0 -0
  380. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_new_features.py +0 -0
  381. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_note_authoring_contract.py +0 -0
  382. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_fields.py +0 -0
  383. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_media.py +0 -0
  384. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_notes.py +0 -0
  385. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_page.py +0 -0
  386. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_refs.py +0 -0
  387. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_shapes.py +0 -0
  388. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_ns_tracking.py +0 -0
  389. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_number_formats.py +0 -0
  390. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_opc_package.py +0 -0
  391. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_open_safety_corpus.py +0 -0
  392. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_outline_numbering.py +0 -0
  393. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_oxml_parsing.py +0 -0
  394. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_package_hygiene.py +0 -0
  395. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_packaging_license_metadata.py +0 -0
  396. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_packaging_py_typed.py +0 -0
  397. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_page_number_control.py +0 -0
  398. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_page_number_format.py +0 -0
  399. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_page_orientation.py +0 -0
  400. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_para_pr_dedup.py +0 -0
  401. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_align_distribute_space.py +0 -0
  402. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_border.py +0 -0
  403. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_format_targets.py +0 -0
  404. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_hyperlink_style.py +0 -0
  405. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_keep_together.py +0 -0
  406. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_margin_default_scale.py +0 -0
  407. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_property_version_switch.py +0 -0
  408. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_paragraph_remove_in_containers.py +0 -0
  409. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_part_hierarchy_read_models.py +0 -0
  410. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_polygon_authoring.py +0 -0
  411. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_public_artifact_hygiene.py +0 -0
  412. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_public_runtime_boundary.py +0 -0
  413. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_read_fidelity.py +0 -0
  414. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_recover_broken_zip.py +0 -0
  415. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_redline_authoring.py +0 -0
  416. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_redline_verify.py +0 -0
  417. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_remove_unused_charts.py +0 -0
  418. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_remove_unused_images.py +0 -0
  419. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_repair_repack.py +0 -0
  420. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_replace_text_barriers.py +0 -0
  421. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_report_utils.py +0 -0
  422. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_repr_snapshots.py +0 -0
  423. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_return_contract.py +0 -0
  424. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_roundtrip_fidelity.py +0 -0
  425. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_roundtrip_fidelity_corpus.py +0 -0
  426. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_row_heights.py +0 -0
  427. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_run_content_kinds.py +0 -0
  428. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_run_font_refs.py +0 -0
  429. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_run_style_base.py +0 -0
  430. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_safe_edit_example.py +0 -0
  431. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_save_keeps_inline_text_caches.py +0 -0
  432. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_save_pipeline.py +0 -0
  433. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_save_verifies_once.py +0 -0
  434. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_section_clear_body.py +0 -0
  435. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_section_headers.py +0 -0
  436. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_section_ids_order.py +0 -0
  437. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_section_properties_real_roundtrip.py +0 -0
  438. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_set_paragraph_format_keep.py +0 -0
  439. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_shape_geometry_contract.py +0 -0
  440. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_shape_open_safety_risk_check.py +0 -0
  441. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_shape_original_size.py +0 -0
  442. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_size_ratchet.py +0 -0
  443. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_skeleton_template_ids.py +0 -0
  444. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_split_merged_cell.py +0 -0
  445. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_stable_surface.py +0 -0
  446. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_story_edits_reach_control_copy.py +0 -0
  447. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_style_resolution.py +0 -0
  448. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_tab_definition_version_switch.py +0 -0
  449. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_cell_equalize.py +0 -0
  450. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_cleanup.py +0 -0
  451. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_navigation.py +0 -0
  452. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_page_flow.py +0 -0
  453. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_patch_dryrun.py +0 -0
  454. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_patch_m105.py +0 -0
  455. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_patch_structure_fixes.py +0 -0
  456. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_table_split_merge.py +0 -0
  457. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_tables_default_border.py +0 -0
  458. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_template_analyzer_enrichment.py +0 -0
  459. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_box_fields.py +0 -0
  460. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_extractor_annotations.py +0 -0
  461. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_extractor_nested_once.py +0 -0
  462. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_extractor_run_choice_atoms.py +0 -0
  463. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_inline_elements.py +0 -0
  464. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_text_replace_everywhere.py +0 -0
  465. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_title_mark.py +0 -0
  466. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_toc_fidelity.py +0 -0
  467. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_tracked_overlap_render_smoke.py +0 -0
  468. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_tracked_paragraph_delete_break.py +0 -0
  469. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validation_severity.py +0 -0
  470. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validator_comment_nodes.py +0 -0
  471. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validator_form_parameters.py +0 -0
  472. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validator_full_schema.py +0 -0
  473. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validator_hancom_open_rules.py +0 -0
  474. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_validator_section_ids.py +0 -0
  475. {python_hwpx-6.8.0 → python_hwpx-6.9.0}/tests/test_version_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 6.8.0
3
+ Version: 6.9.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -216,9 +216,14 @@ GitHub Release에 첨부되는 `py3-none-any` wheel을 문서와 함께 올리
216
216
  ## 알려진 제약
217
217
 
218
218
  - `add_shape()` / `add_control()`은 저수준 탈출구라, 그대로 저장하면 한/글이
219
- 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 `add_line()` /
220
- `add_rectangle()` / `add_ellipse()`를 쓰세요.
221
- - 그림은 단순 개체 생성까지 지원합니다 (그룹·효과 미지원).
219
+ 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 한/글이 요구하는 하위
220
+ 요소까지 만드는 전용 메서드를 쓰세요: `add_line()` / `add_rectangle()` /
221
+ `add_ellipse()` / `add_arc()` / `add_polygon()` / `add_curve()` /
222
+ `add_connector()` / `add_container()`(묶음). 컨트롤은 `add_column_definition()` /
223
+ `add_bookmark()` / `add_hyperlink()`가 있습니다.
224
+ - 그림은 단순 개체 생성과 묶음 안 그림까지 지원합니다. 밝기·대비·회색조·흑백·투명도와 실선 테두리는
225
+ `add_picture()` 인자로 줄 수 있고(자르기는 `crop`, 그림자·빛남·부드러운 가장자리·반사는 `shadow`·`glow`·`soft_edge`·
226
+ `reflection`), 뒤집기·회전은 아직 지원하지 않습니다.
222
227
  - 암호화된 HWPX와 암호·배포용 HWP는 지원하지 않습니다.
223
228
 
224
229
  ## 기여하기
@@ -165,9 +165,14 @@ GitHub Release에 첨부되는 `py3-none-any` wheel을 문서와 함께 올리
165
165
  ## 알려진 제약
166
166
 
167
167
  - `add_shape()` / `add_control()`은 저수준 탈출구라, 그대로 저장하면 한/글이
168
- 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 `add_line()` /
169
- `add_rectangle()` / `add_ellipse()`를 쓰세요.
170
- - 그림은 단순 개체 생성까지 지원합니다 (그룹·효과 미지원).
168
+ 열지 못하는 파일이 됩니다(호출 시 경고만 나옵니다). 도형은 한/글이 요구하는 하위
169
+ 요소까지 만드는 전용 메서드를 쓰세요: `add_line()` / `add_rectangle()` /
170
+ `add_ellipse()` / `add_arc()` / `add_polygon()` / `add_curve()` /
171
+ `add_connector()` / `add_container()`(묶음). 컨트롤은 `add_column_definition()` /
172
+ `add_bookmark()` / `add_hyperlink()`가 있습니다.
173
+ - 그림은 단순 개체 생성과 묶음 안 그림까지 지원합니다. 밝기·대비·회색조·흑백·투명도와 실선 테두리는
174
+ `add_picture()` 인자로 줄 수 있고(자르기는 `crop`, 그림자·빛남·부드러운 가장자리·반사는 `shadow`·`glow`·`soft_edge`·
175
+ `reflection`), 뒤집기·회전은 아직 지원하지 않습니다.
171
176
  - 암호화된 HWPX와 암호·배포용 HWP는 지원하지 않습니다.
172
177
 
173
178
  ## 기여하기
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx"
7
- version = "6.8.0"
7
+ version = "6.9.0"
8
8
  description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
@@ -295,6 +295,9 @@ files = [
295
295
  # Table width/height distribution (distribute cell width row by row) moved out
296
296
  # of table.py -- born in the gate, per the WP-A rule.
297
297
  "src/hwpx/oxml/table_sizes.py",
298
+ # Row and column insertion and deletion and cell splits on the table object, moved
299
+ # out of table.py -- born in the gate, per the WP-A rule.
300
+ "src/hwpx/oxml/table_structure.py",
298
301
  # A paragraph shape's heading (outline levels 8 to 10 in Hancom's hp:switch form),
299
302
  # written and read in one place -- born in the gate, per the WP-A rule.
300
303
  "src/hwpx/oxml/paragraph_heading.py",
@@ -504,6 +507,9 @@ include = [
504
507
  # Table width/height distribution (distribute cell width row by row) moved out
505
508
  # of table.py -- born in the gate, per the WP-A rule.
506
509
  "src/hwpx/oxml/table_sizes.py",
510
+ # Row and column insertion and deletion and cell splits on the table object, moved
511
+ # out of table.py -- born in the gate, per the WP-A rule.
512
+ "src/hwpx/oxml/table_structure.py",
507
513
  # A paragraph shape's heading (outline levels 8 to 10 in Hancom's hp:switch form),
508
514
  # written and read in one place -- born in the gate, per the WP-A rule.
509
515
  "src/hwpx/oxml/paragraph_heading.py",
@@ -663,7 +663,7 @@ def add_form_field(
663
663
  """Create a click-here (누름틀) form field and return it as a :class:`FormField`.
664
664
 
665
665
  The emitted XML follows the real-Hancom CLICKHERE contract
666
- (reverse-engineered from Hancom Office 12.0.0.3288 gold documents). The
666
+ (inferred from Hancom Office 12.0.0.3288 gold documents). The
667
667
  prompt (안내문) is materialized as a screen-only red-italic run, exactly as
668
668
  Hancom authors it. The created field is immediately re-read through the
669
669
  standard ``list_form_fields`` matcher — creation fails loudly if the
@@ -19,7 +19,9 @@ from ..oxml._document_primitives import NEW_NUM_KINDS
19
19
  from ..oxml.namespaces import HH, HP
20
20
  from ..oxml.numbering_kinds import ensure_numbering_levels
21
21
  from ..oxml.objects import HwpxOxmlInlineObject
22
- from ..oxml.section_format import _PAGE_LANDSCAPE, _PAGE_PORTRAIT, _page_orientation_value
22
+ from ..oxml.section_format import (
23
+ _PAGE_LANDSCAPE, _PAGE_PORTRAIT, _checked, _page_orientation_value, column_shares, validate_column_gap,
24
+ )
23
25
  from ..oxml.table_sizes import cell_margins_of
24
26
  from ._units import _mm_to_hwp_units, _pt_to_hwp_units
25
27
 
@@ -512,12 +514,12 @@ def set_list_format(
512
514
  suggestion="A continued list numbers on in the format of the list before it: "
513
515
  "leave out number_format and start, or continue_list to start a new list.",
514
516
  )
515
- if level < 1:
517
+ if isinstance(level, bool) or not isinstance(level, int) or not 1 <= level <= 10:
516
518
  raise HwpxValueError(
517
- "level must be 1 or greater",
519
+ f"level must be an int from 1 to 10; got {level!r}",
518
520
  code="style-list-level-invalid",
519
- context={"requested": level},
520
- suggestion="Levels start at 1.",
521
+ context={"requested": repr(level)},
522
+ suggestion="Hancom lists have ten levels, 1 to 10; it cannot save a document using a deeper one.",
521
523
  )
522
524
  if not doc._root.headers:
523
525
  raise HwpxValueError(
@@ -525,6 +527,13 @@ def set_list_format(
525
527
  code="document-header-missing",
526
528
  suggestion="Check that this is an intact HWPX package.",
527
529
  )
530
+ if start is not None and (isinstance(start, bool) or not isinstance(start, int) or not 0 <= start <= 0xFFFF):
531
+ raise HwpxValueError( # Hancom keeps a list's start as 16 bits unsigned (0 drawn as 1)
532
+ f"start must be an int from 0 to 65535; got {start!r}",
533
+ code="style-list-start-value",
534
+ context={"requested": repr(start)},
535
+ suggestion="Pass the number the list starts at, from 0 to 65535.",
536
+ )
528
537
 
529
538
  level_specs: list[dict[str, str]] = [{} for _ in range(level)]
530
539
  if bullet_char:
@@ -532,7 +541,7 @@ def set_list_format(
532
541
  if number_format:
533
542
  level_specs[level - 1]["format"] = str(number_format).upper()
534
543
  if start is not None:
535
- level_specs[level - 1]["start"] = str(max(1, int(start)))
544
+ level_specs[level - 1]["start"] = str(start)
536
545
 
537
546
  targets = _resolve_paragraph_targets(doc,
538
547
  paragraph_index=paragraph_index,
@@ -609,6 +618,8 @@ def set_page_setup(
609
618
  """
610
619
 
611
620
  normalized_orientation = _normalize_page_orientation(orientation)
621
+ if columns is not None: # the gap checked before the page or its margins change
622
+ validate_column_gap(_mm_to_hwp_units(float(column_gap_mm or 0)))
612
623
  target_width_mm = width_mm
613
624
  target_height_mm = height_mm
614
625
  if paper_size:
@@ -633,6 +644,24 @@ def set_page_setup(
633
644
 
634
645
  width = _mm_to_hwp_units(float(target_width_mm)) if target_width_mm is not None else None
635
646
  height = _mm_to_hwp_units(float(target_height_mm)) if target_height_mm is not None else None
647
+ margin_source = dict(margins_mm or {})
648
+ margin_values = {
649
+ "left": margin_left_mm if margin_left_mm is not None else margin_source.get("left"),
650
+ "right": margin_right_mm if margin_right_mm is not None else margin_source.get("right"),
651
+ "top": margin_top_mm if margin_top_mm is not None else margin_source.get("top"),
652
+ "bottom": margin_bottom_mm if margin_bottom_mm is not None else margin_source.get("bottom"),
653
+ "header": header_margin_mm if header_margin_mm is not None else margin_source.get("header"),
654
+ "footer": footer_margin_mm if footer_margin_mm is not None else margin_source.get("footer"),
655
+ "gutter": gutter_mm if gutter_mm is not None else margin_source.get("gutter"),
656
+ }
657
+ hwp_margins = {
658
+ name: _mm_to_hwp_units(float(value))
659
+ for name, value in margin_values.items()
660
+ if value is not None
661
+ }
662
+ for name, value in (("width", width), ("height", height), *hwp_margins.items()):
663
+ if value is not None: # checked before the page or its margins change
664
+ _checked(name, value, 2**31 - 1, "page-size-value")
636
665
  if width is not None or height is not None or normalized_orientation is not None:
637
666
  # Call the local primitives directly rather than `doc.set_page_size`/
638
667
  # `doc.set_page_margins`/`doc.set_columns` below — all three names
@@ -649,21 +678,6 @@ def set_page_setup(
649
678
  section_index=section_index,
650
679
  )
651
680
 
652
- margin_source = dict(margins_mm or {})
653
- margin_values = {
654
- "left": margin_left_mm if margin_left_mm is not None else margin_source.get("left"),
655
- "right": margin_right_mm if margin_right_mm is not None else margin_source.get("right"),
656
- "top": margin_top_mm if margin_top_mm is not None else margin_source.get("top"),
657
- "bottom": margin_bottom_mm if margin_bottom_mm is not None else margin_source.get("bottom"),
658
- "header": header_margin_mm if header_margin_mm is not None else margin_source.get("header"),
659
- "footer": footer_margin_mm if footer_margin_mm is not None else margin_source.get("footer"),
660
- "gutter": gutter_mm if gutter_mm is not None else margin_source.get("gutter"),
661
- }
662
- hwp_margins = {
663
- name: _mm_to_hwp_units(float(value))
664
- for name, value in margin_values.items()
665
- if value is not None
666
- }
667
681
  if hwp_margins:
668
682
  set_page_margins(
669
683
  doc,
@@ -723,15 +737,18 @@ def set_columns(
723
737
  ) -> HwpxOxmlInlineObject:
724
738
  """Set the columns of a section, or start new columns at a paragraph.
725
739
 
726
- Without ``paragraph`` this rewrites the section's own column layout (the
727
- ``hp:colPr`` next to ``hp:secPr``) in place, so the whole section is laid
728
- out in ``col_count`` columns. With ``paragraph`` it adds a column
729
- definition control there, and the text from that paragraph on uses it.
740
+ Without ``paragraph``, or with a section's first one, this rewrites the
741
+ section's own column layout (the ``hp:colPr`` next to ``hp:secPr``) in
742
+ place, so the whole section is laid out in ``col_count`` columns. With a
743
+ later ``paragraph`` it adds a column definition there, used from it on.
730
744
 
731
745
  Args:
732
746
  col_count: Number of columns (1–255).
733
747
  col_type: ``NEWSPAPER``, ``BALANCED_NEWSPAPER``, or ``PARALLEL``.
734
748
  same_gap: Gap in HWPUNIT (7200 = 1 inch).
749
+ column_widths: With ``same_size=False``, a ``(width, gap)`` pair per
750
+ column, in HWPUNIT adding up to the text width or in plain
751
+ proportions; Hancom keeps them as shares of 32768 of the text width.
735
752
  separator_type: Optional column separator line type (e.g. ``SOLID``).
736
753
  """
737
754
  if not 1 <= col_count <= 255:
@@ -741,6 +758,10 @@ def set_columns(
741
758
  context={"requested": col_count},
742
759
  suggestion="Use columns=1 to remove columns.",
743
760
  )
761
+ if same_size:
762
+ validate_column_gap(same_gap) # before a paragraph is added
763
+ elif column_widths:
764
+ column_shares(column_widths) # checked before a paragraph is added too
744
765
  if paragraph is None:
745
766
  target_section = _resolve_section(doc, section=section, section_index=section_index)
746
767
  ctrl = target_section.properties.set_columns(
@@ -19,6 +19,11 @@ from ..opc.relationships import (
19
19
  )
20
20
  from ..oxml import HwpxOxmlInlineObject, HwpxOxmlParagraph
21
21
  from ..oxml.namespaces import HC, HP
22
+ from ..oxml.color import normalize_color
23
+ from ..oxml.shape_position import PictureGlow, PictureReflection, PictureShadow, validate_picture_effects
24
+ from ..oxml.shape_position import (
25
+ validate_picture_align, validate_picture_border, validate_picture_crop, validate_picture_image, validate_shape_size,
26
+ )
22
27
  from ._units import _mm_to_hwp_units
23
28
 
24
29
  if TYPE_CHECKING:
@@ -106,23 +111,35 @@ def add_picture(
106
111
  width_mm: float | None = None,
107
112
  height_mm: float | None = None,
108
113
  align: str | None = None,
114
+ brightness: int = 0,
115
+ contrast: int = 0,
116
+ effect: str = "REAL_PIC",
117
+ alpha: int = 0,
118
+ line_color: str | None = None,
119
+ line_width: int = 33,
120
+ crop: tuple[int, int, int, int] | None = None,
121
+ shadow: PictureShadow | None = None,
122
+ glow: PictureGlow | None = None,
123
+ soft_edge: int | None = None,
124
+ reflection: PictureReflection | None = None,
109
125
  para_pr_id_ref: str | int | None = None,
110
126
  style_id_ref: str | int | None = None,
111
127
  char_pr_id_ref: str | int | None = None,
112
128
  run_attributes: dict[str, str] | None = None,
113
129
  **extra_attrs: str,
114
130
  ) -> HwpxOxmlInlineObject:
115
- """Embed image data and place a picture object in a new paragraph."""
116
-
117
- # Call the local primitive directly rather than `doc.add_image` — that
118
- # facade name moved in 6.0 (design table row 33), and going through it
119
- # would fire a DeprecationWarning on every `add_picture` call even though
120
- # `add_picture` itself is a kept (unmoved) root method.
121
- binary_item_id_ref = str(add_image(doc, image_data, image_format))
122
-
131
+ """Embed image data and place a picture object in a new paragraph, the image adjusted and bordered as
132
+ :meth:`HwpxOxmlParagraph.add_picture` takes it."""
133
+
134
+ validate_picture_align(align) # before anything is stored: Hancom reads an unknown one as LEFT
135
+ validate_picture_image(brightness, contrast, effect, alpha) # ... and draws no other adjustment
136
+ validate_picture_border(line_width)
137
+ if line_color is not None:
138
+ normalize_color(line_color)
123
139
  resolved_width = width
124
140
  if resolved_width is None:
125
141
  resolved_width = _mm_to_hwp_units(width_mm) if width_mm is not None else 14400
142
+ validate_shape_size(resolved_width, 0) # validate before aspect-ratio arithmetic too
126
143
 
127
144
  resolved_height = height
128
145
  if resolved_height is None:
@@ -135,6 +152,15 @@ def add_picture(
135
152
  resolved_height = round(resolved_width * source_height / source_width)
136
153
  else:
137
154
  resolved_height = resolved_width
155
+ validate_shape_size(resolved_width, resolved_height) # before anything is stored too
156
+ validate_picture_crop(crop, resolved_width, resolved_height)
157
+ validate_picture_effects(shadow, glow, soft_edge, reflection)
158
+
159
+ # Call the local primitive directly rather than `doc.add_image` — that
160
+ # facade name moved in 6.0 (design table row 33), and going through it
161
+ # would fire a DeprecationWarning on every `add_picture` call even though
162
+ # `add_picture` itself is a kept (unmoved) root method.
163
+ binary_item_id_ref = str(add_image(doc, image_data, image_format))
138
164
 
139
165
  paragraph = doc.add_paragraph(
140
166
  "",
@@ -158,6 +184,17 @@ def add_picture(
158
184
  width=resolved_width,
159
185
  height=resolved_height,
160
186
  align=align,
187
+ brightness=brightness,
188
+ contrast=contrast,
189
+ effect=effect,
190
+ alpha=alpha,
191
+ line_color=line_color,
192
+ line_width=line_width,
193
+ crop=crop,
194
+ shadow=shadow,
195
+ glow=glow,
196
+ soft_edge=soft_edge,
197
+ reflection=reflection,
161
198
  run_attributes=run_attributes,
162
199
  char_pr_id_ref=char_pr_id_ref,
163
200
  )
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from ..errors import HwpxValueError
10
10
 
11
- from ..oxml._document_primitives import _object_id
11
+ from ..oxml._document_primitives import _clear_paragraph_layout_cache, _object_id
12
12
  from ..oxml.namespaces import HP
13
13
 
14
14
  if TYPE_CHECKING:
@@ -189,6 +189,7 @@ def attach_memo_field(
189
189
  "dirty": "0",
190
190
  "zorder": order,
191
191
  "fieldid": MEMO_FIELD_ID,
192
+ "metaTag": "",
192
193
  },
193
194
  )
194
195
 
@@ -247,6 +248,7 @@ def attach_memo_field(
247
248
 
248
249
  paragraph.element.insert(0, run_begin)
249
250
  paragraph.element.append(run_end)
251
+ _clear_paragraph_layout_cache(paragraph.element) # the field's controls move its text: laid out again
250
252
  paragraph.section.mark_dirty()
251
253
 
252
254
  return memo
@@ -345,8 +345,8 @@ class ShapesNamespace(_Namespace):
345
345
  section: "int | Section | None" = None,
346
346
  section_index: int | None = None,
347
347
  ) -> "Shape":
348
- """도형을 그룹으로 묶는다(`ContainerMember.rect`/`.ellipse`/`.polygon`으로
349
- 각 부재를 그룹 로컬 좌표로 만들어 넘긴다)."""
348
+ """도형을 그룹으로 묶는다(`ContainerMember.rect`/`.ellipse`/`.polygon`/`.line`/
349
+ `.text_box`/`.picture`/`.group`으로 각 부재를 그룹 로컬 좌표로 만들어 넘긴다)."""
350
350
 
351
351
  from .. import shapes as _shapes
352
352
 
@@ -1,5 +1,5 @@
1
1
  # SPDX-License-Identifier: Apache-2.0
2
- """`doc.tables` — 표 탐색·매핑·병합·경로 채움.
2
+ """`doc.tables` — 표 탐색·매핑·병합·구조 편집·경로 채움.
3
3
 
4
4
  표를 **만드는** 것은 루트에 남았다(`doc.add_table` — python-docx 대응이자
5
5
  automation 최다 사용 저작 동사 중 하나). 만든 다음에 하는 일들이 여기 모인다.
@@ -11,7 +11,7 @@ automation 최다 사용 저작 동사 중 하나). 만든 다음에 하는 일
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
- from typing import TYPE_CHECKING, Mapping
14
+ from typing import TYPE_CHECKING, Iterable, Mapping, overload
15
15
 
16
16
  from ._base import _Namespace
17
17
 
@@ -28,7 +28,7 @@ __all__ = ["TablesNamespace"]
28
28
 
29
29
 
30
30
  class TablesNamespace(_Namespace):
31
- """표 탐색·매핑·병합·경로 채움."""
31
+ """표 탐색·매핑·병합·구조 편집·경로 채움."""
32
32
 
33
33
  __slots__ = ()
34
34
  _path = "doc.tables"
@@ -48,6 +48,20 @@ class TablesNamespace(_Namespace):
48
48
  def __iter__(self):
49
49
  return iter(self.all)
50
50
 
51
+ @overload
52
+ def __getitem__(self, index: int) -> "Table": ...
53
+
54
+ @overload
55
+ def __getitem__(self, index: slice) -> list["Table"]: ...
56
+
57
+ def __getitem__(self, index: int | slice) -> "Table | list[Table]":
58
+ """문서 순서의 ``index`` 번째 표(``doc.tables[0]``, ``doc.tables[-1]``, ``doc.tables[1:3]``).
59
+
60
+ ``doc.tables.all[index]``와 같다. 범위 밖이면 ``IndexError``.
61
+ """
62
+
63
+ return self.all[index]
64
+
51
65
  def map(self) -> "TableMapResult":
52
66
  """모든 표의 위치·모양·헤더 미리보기를 담은 간결 지도."""
53
67
 
@@ -78,3 +92,35 @@ class TablesNamespace(_Namespace):
78
92
  """
79
93
 
80
94
  return table.merge_cells(cell_range)
95
+
96
+ def insert_rows(
97
+ self, table: "Table", ref_row: int, count: int = 1, *, side: str = "below", blank: bool = False
98
+ ) -> None:
99
+ """``ref_row`` 행을 복제해 그 아래(``side="above"``면 위)에 ``count`` 행을 넣는다.
100
+
101
+ 새 칸은 같은 열 기준 칸의 서식과 글을 받고, ``blank=True``면 한/글처럼 빈 문단 하나만 둔다.
102
+ """
103
+
104
+ table.insert_rows(ref_row, count, side=side, blank=blank)
105
+
106
+ def insert_columns(
107
+ self, table: "Table", ref_col: int, count: int = 1, *, side: str = "right", blank: bool = False
108
+ ) -> None:
109
+ """``ref_col`` 열을 복제해 그 오른쪽(``side="left"``면 왼쪽)에 ``count`` 열을 넣는다(표가 넓어진다)."""
110
+
111
+ table.insert_columns(ref_col, count, side=side, blank=blank)
112
+
113
+ def delete_rows(self, table: "Table", rows: int | Iterable[int]) -> None:
114
+ """행을 지운다. 모든 행은 지울 수 없다(표 삭제를 쓴다)."""
115
+
116
+ table.delete_rows(rows)
117
+
118
+ def delete_columns(self, table: "Table", cols: int | Iterable[int]) -> None:
119
+ """열을 지우고 그 폭을 남은 열에 나눈다. 모든 열은 지울 수 없다."""
120
+
121
+ table.delete_columns(cols)
122
+
123
+ def split_cell(self, table: "Table", row: int, col: int, *, rows: int = 1, cols: int = 1) -> None:
124
+ """(``row``, ``col``) 칸을 ``rows`` 줄 × ``cols`` 칸으로 나눈다(한/글의 셀 나누기)."""
125
+
126
+ table.split_cell(row, col, rows=rows, cols=cols)
@@ -8,7 +8,9 @@ from typing import TYPE_CHECKING, Any, Sequence, cast
8
8
  from ._units import _mm_to_hwp_units
9
9
  from ..errors import HwpxStateError, HwpxValueError
10
10
  from ..oxml.objects import _closed_points
11
- from ..oxml.shape_position import validate_original_size
11
+ from ..oxml.shape_position import (
12
+ validate_equation_base_unit, validate_original_size, validate_rect_ratio, validate_shape_size,
13
+ )
12
14
 
13
15
  if TYPE_CHECKING:
14
16
  from hwpx.document import HwpxDocument
@@ -265,6 +267,8 @@ def add_rectangle(
265
267
  Dimensions are in HWPUNIT. *ratio* controls corner roundness
266
268
  (0 = sharp, 50 = semicircle).
267
269
  """
270
+ validate_shape_size(width, height)
271
+ validate_rect_ratio(ratio)
268
272
  validate_original_size(original_size)
269
273
  if paragraph is None:
270
274
  paragraph = doc.add_paragraph(
@@ -297,6 +301,7 @@ def add_ellipse(
297
301
 
298
302
  Dimensions are in HWPUNIT.
299
303
  """
304
+ validate_shape_size(width, height)
300
305
  validate_original_size(original_size)
301
306
  if paragraph is None:
302
307
  paragraph = doc.add_paragraph(
@@ -334,6 +339,7 @@ def add_arc(
334
339
  mechanism every other shape here already uses. *arc_type* is the
335
340
  schema's own ``NORMAL``/``PIE``/``CHORD`` passthrough.
336
341
  """
342
+ validate_shape_size(width, height)
337
343
  if paragraph is None:
338
344
  paragraph = doc.add_paragraph(
339
345
  "", section=section, section_index=section_index,
@@ -461,7 +467,8 @@ def add_container(
461
467
  """Insert a group (``<hp:container>``) wrapping *members*.
462
468
 
463
469
  Each member is built with :class:`hwpx.oxml.ContainerMember`'s
464
- ``rect``/``ellipse``/``polygon`` classmethods, which take the member's
470
+ ``rect``/``ellipse``/``polygon``/``line``/``text_box``/``picture``/``group``
471
+ classmethods, which take the member's
465
472
  position in the group's own local coordinate space (HWPUNIT, top-left
466
473
  anchored) alongside its usual shape parameters::
467
474
 
@@ -522,15 +529,16 @@ def add_equation(
522
529
  context={"length": len(text), "limit": MAX_SOURCE_LENGTH},
523
530
  suggestion="Split the equation.",
524
531
  )
532
+ validate_equation_base_unit(base_unit)
533
+ width, height = size if size is not None else estimate_equation_size(text, base_unit=base_unit)
534
+ validate_shape_size(width, height) # one measured at a huge base_unit too
525
535
  if paragraph is None:
526
536
  paragraph = doc.add_paragraph(
527
537
  "", section=section, section_index=section_index,
528
538
  include_run=False,
529
539
  )
530
- if size is None:
531
- size = estimate_equation_size(text, base_unit=base_unit)
532
540
  inline_object = paragraph.add_equation(
533
- text, base_unit=base_unit, size=size, char_pr_id_ref=char_pr_id_ref,
541
+ text, base_unit=base_unit, size=(width, height), char_pr_id_ref=char_pr_id_ref,
534
542
  )
535
543
 
536
544
  created_id = inline_object.element.get("id", "")
@@ -756,10 +764,10 @@ def add_drop_cap(
756
764
  char_pr_id_ref: str | int | None = None,
757
765
  para_pr_id_ref: str | int | None = None,
758
766
  ) -> HwpxOxmlInlineObject:
759
- """Insert a drop cap (문단 첫 글자 장식), reverse-engineered from the one
767
+ """Insert a drop cap (문단 첫 글자 장식), inferred from the one
760
768
  real-corpus example that carries a non-default ``dropcapstyle``
761
769
  (``error__20230809__test.hwpx`` -- see ``oxml.drop_cap``'s own
762
- docstring for the full structural reverse engineering and why it
770
+ docstring for the full structural format inference and why it
763
771
  supports ``style="TripleLine"`` and ``"DoubleLine"`` only).
764
772
 
765
773
  Element construction (element-building validation included) happens in
@@ -38,6 +38,7 @@ from typing import Any, Mapping, Sequence
38
38
  from .opc.security import guard_zip_file, read_member
39
39
  from .mutation_report import MutationReport, project_byte_splice
40
40
  from .patch import (
41
+ _escaped_text,
41
42
  _finalize,
42
43
  _patch_zip_entries,
43
44
  _read_source_bytes,
@@ -185,9 +186,9 @@ def _op_replace_text(xml: str, op: Mapping[str, Any]) -> tuple[str, dict[str, An
185
186
  replace = str(op.get("replace", ""))
186
187
  expected = int(op.get("count", 1))
187
188
  esc_find = html.escape(find, quote=False)
188
- # A tab goes in as an hp:tab element: Hancom never finishes laying out a
189
- # raw tab character inside hp:t.
190
- esc_replace = "<hp:tab/>".join(html.escape(part, quote=False) for part in replace.split("\t"))
189
+ # A tab goes in as an hp:tab element (Hancom never finishes laying out a
190
+ # raw tab character inside hp:t), a line break as an hp:lineBreak.
191
+ esc_replace = _escaped_text(replace, b"hp:").decode("utf-8")
191
192
  hits: list[tuple[int, int]] = []
192
193
  for t in _T_CONTENT_RE.finditer(xml):
193
194
  a, b = t.start(1), t.end(1)
@@ -215,14 +216,6 @@ def _op_replace_text(xml: str, op: Mapping[str, Any]) -> tuple[str, dict[str, An
215
216
  raise ValueError("replace_text: match outside any <hp:p> paragraph")
216
217
  by_para.setdefault(span, []).append(hit)
217
218
 
218
- edits: list[tuple[int, int, str]] = []
219
- for (pa, pb), phits in by_para.items():
220
- block = xml[pa:pb]
221
- for a, b in sorted(phits, reverse=True):
222
- block = block[: a - pa] + esc_replace + block[b - pa:]
223
- block = _strip_paragraph_layout_cache(block.encode("utf-8")).decode("utf-8")
224
- edits.append((pa, pb, block))
225
-
226
219
  # 모듈 취지는 표-밖 본문이지만(위 docstring), <hp:t> 검색은 구조적으로
227
220
  # 표 셀 내부까지 닿는다 — 편집된 문단이 셀(subList) 안이면 그 셀의
228
221
  # lineWrap=SQUEEZE도 함께 BREAK로 바꾼다. 안 바꾸면 실측대로 압축 자간이
@@ -232,6 +225,7 @@ def _op_replace_text(xml: str, op: Mapping[str, Any]) -> tuple[str, dict[str, An
232
225
  # 문단이 여럿이어도 셀 편집은 한 번만.
233
226
  sublist_spans = _all_sublist_spans(xml)
234
227
  fixed_sublists: set[tuple[int, int]] = set()
228
+ wrap_edits: list[tuple[int, int, str]] = []
235
229
  for pa, _pb in by_para:
236
230
  sublist_span = _innermost_span(pa, sublist_spans)
237
231
  if sublist_span is None or sublist_span in fixed_sublists:
@@ -239,7 +233,28 @@ def _op_replace_text(xml: str, op: Mapping[str, Any]) -> tuple[str, dict[str, An
239
233
  fixed_sublists.add(sublist_span)
240
234
  wrap_edit = _squeeze_wrap_edit_for_sublist(xml, sublist_span)
241
235
  if wrap_edit is not None:
242
- edits.append(wrap_edit)
236
+ wrap_edits.append(wrap_edit)
237
+
238
+ # 표를 담은 문단과 그 표의 셀 문단이 둘 다 편집되면 두 문단 구간이 겹친다.
239
+ # 바깥 문단 하나로 묶어 그 안의 치환·셀 SQUEEZE 편집을 한 번에 적용한 뒤
240
+ # 캐시를 지운다(바깥 문단의 캐시 제거가 안쪽 문단의 캐시도 지운다).
241
+ outer = [
242
+ span for span in by_para
243
+ if not any(other != span and other[0] <= span[0] and span[1] <= other[1] for other in by_para)
244
+ ]
245
+ edits: list[tuple[int, int, str]] = []
246
+ for pa, pb in outer:
247
+ local = [(a, b, esc_replace) for a, b in hits if pa <= a < pb]
248
+ local += [edit for edit in wrap_edits if pa <= edit[0] and edit[1] <= pb]
249
+ block = xml[pa:pb]
250
+ for a, b, replacement in sorted(local, key=lambda e: e[0], reverse=True):
251
+ block = block[: a - pa] + replacement + block[b - pa:]
252
+ block = _strip_paragraph_layout_cache(block.encode("utf-8")).decode("utf-8")
253
+ edits.append((pa, pb, block))
254
+ edits += [
255
+ edit for edit in wrap_edits
256
+ if not any(pa <= edit[0] and edit[1] <= pb for pa, pb in outer)
257
+ ]
243
258
 
244
259
  for a, b, replacement in sorted(edits, key=lambda e: e[0], reverse=True):
245
260
  xml = xml[:a] + replacement + xml[b:]
@@ -81,7 +81,7 @@ _CAPABILITY_AREAS: tuple[dict[str, Any], ...] = (
81
81
  "area": "table-structure",
82
82
  "namespace": "doc.tables",
83
83
  "matrix_row": "표 구조 변경(행·열·표 삭제/삽입, 열 오토핏)",
84
- "entry_points": ("hwpx.table_patch:apply_table_ops",),
84
+ "entry_points": ("hwpx.table_patch:apply_table_ops", "hwpx.model:Table"),
85
85
  "authoring_methods": (),
86
86
  },
87
87
  {