python-hwpx 5.6.0__py3-none-any.whl → 5.7.0__py3-none-any.whl
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.
- hwpx/_document/fields.py +115 -0
- hwpx/document.py +62 -0
- hwpx/oxml/paragraph.py +85 -0
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/METADATA +1 -1
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/RECORD +10 -10
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/WHEEL +0 -0
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/entry_points.txt +0 -0
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/licenses/LICENSE +0 -0
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/licenses/NOTICE +0 -0
- {python_hwpx-5.6.0.dist-info → python_hwpx-5.7.0.dist-info}/top_level.txt +0 -0
hwpx/_document/fields.py
CHANGED
|
@@ -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]],
|
hwpx/document.py
CHANGED
|
@@ -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,
|
hwpx/oxml/paragraph.py
CHANGED
|
@@ -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."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
hwpx/__init__.py,sha256=DqoPNdD-4-oP4b_56P5ADABnyRGcDZiHch__S-urI4s,16642
|
|
2
2
|
hwpx/body_patch.py,sha256=XfwrTMThS9vPLA62PDnEleCGuvYv61URxelbD_LHe8w,25730
|
|
3
3
|
hwpx/capabilities.py,sha256=NZIuzerbKhdS6A4Ls9bpIrLOMQWoJ6JcICD4HrosJxQ,16597
|
|
4
|
-
hwpx/document.py,sha256=
|
|
4
|
+
hwpx/document.py,sha256=ylIo6R3k67aRHgPzVDtQrUQF_gt-D2FHpQ3pNPckm9c,69441
|
|
5
5
|
hwpx/errors.py,sha256=l3QK2Izwkpm25ar7QnNQvyg0yosoLfQt7kW5wn5MCG0,3033
|
|
6
6
|
hwpx/experimental.py,sha256=Jvc7WAi3eMFRKYubqwtIWi9oU0OjuqNOfottWkaP2RQ,2113
|
|
7
7
|
hwpx/mutation_report.py,sha256=6hurhDdgGiONeLIeWZJT8lwLtWJZ1VHp1l6G0zy0mQo,19409
|
|
@@ -12,7 +12,7 @@ hwpx/table_patch.py,sha256=nl7UT-Qf5-KV25Wdxm4ve9ZxX6FfijibymPXZcJJIjQ,83729
|
|
|
12
12
|
hwpx/templates.py,sha256=28bYqeJVeDb1Cq8G9NZG9Mhnu4K2GamAKC4QhxvUZyA,1187
|
|
13
13
|
hwpx/_document/__init__.py,sha256=REiNqMbuk_TS4NVC27FIo1rUt3Z8nhJrTBGOgNtxBRg,90
|
|
14
14
|
hwpx/_document/_units.py,sha256=qyC8YtnvV2VlSGltJntDaIMF_sHy_XJZCwwe-g080TQ,386
|
|
15
|
-
hwpx/_document/fields.py,sha256=
|
|
15
|
+
hwpx/_document/fields.py,sha256=xOpTKXedTlpSg82G2_zPVQ0I6N6d6PQSAyQvZMtHF5A,26935
|
|
16
16
|
hwpx/_document/layout.py,sha256=_fNHVqI2zFUn6L1-JsdyO8Z4UShmIi9G29Y5YJtQtKw,23553
|
|
17
17
|
hwpx/_document/media.py,sha256=kamsFJZ4K9aoKhjyztMHC64MVFR_ZQ50rDVgouZqxFA,11592
|
|
18
18
|
hwpx/_document/memos.py,sha256=_tscEhNfPe9-iAkcEtJGjwHTN42_lx4stFoPrgJxk3o,7481
|
|
@@ -61,7 +61,7 @@ hwpx/oxml/memo.py,sha256=jTtZIrR7LwWn_vvFN6Fr9yX2eSJfMPh8WnNW1DhD2Rs,10283
|
|
|
61
61
|
hwpx/oxml/namespaces.py,sha256=c7JfdOdJbzrhyHvjbxoeeeloRE3xPuoB7v3YI8SmKDk,6524
|
|
62
62
|
hwpx/oxml/numbering.py,sha256=9a0ARGW1DkKsTF7mEEXM4FA5loQmzIBuqE4APA8UFIQ,669
|
|
63
63
|
hwpx/oxml/objects.py,sha256=8ErDXWvkjLm3FTsx0Z_IovUXbiv2uc-j0B5Y2-OtihA,20864
|
|
64
|
-
hwpx/oxml/paragraph.py,sha256=
|
|
64
|
+
hwpx/oxml/paragraph.py,sha256=q7uxcMMTSslRR3WpaEK5waFV8boGOMZA2Hx2uU87_cU,56601
|
|
65
65
|
hwpx/oxml/parser.py,sha256=pIfyNdW3WdFkCcE8JFY7hn1fobRbMngzfhVZICvWeVs,2937
|
|
66
66
|
hwpx/oxml/run.py,sha256=F939J1W6zQjr-JQ1Z8-nKh2s7NQ_0Okov4QJAEyw_5M,15503
|
|
67
67
|
hwpx/oxml/schema.py,sha256=ElR3_IIhhPPZEqJtNKMNCHA-VFVLdhL454W_4spuvlc,1284
|
|
@@ -114,10 +114,10 @@ hwpx/tools/toc_fidelity.py,sha256=rvoKH8QJ65WNVrxS0d-i2AtODvBy-4O8NRudfQ244v4,19
|
|
|
114
114
|
hwpx/tools/validator.py,sha256=U856izL9NcJZOiKDYoCpwOSaFNQ2Un8Jn4pzL20A96Q,7100
|
|
115
115
|
hwpx/tools/_schemas/header.xsd,sha256=mJXuFMuHGT1JnFFaluUpYUglwjMCNlfbFCRVM26eHXE,664
|
|
116
116
|
hwpx/tools/_schemas/section.xsd,sha256=MgvavVHG05RDfUnVPxVU10H4FQOja5ON04_m9Uk_m7E,522
|
|
117
|
-
python_hwpx-5.
|
|
118
|
-
python_hwpx-5.
|
|
119
|
-
python_hwpx-5.
|
|
120
|
-
python_hwpx-5.
|
|
121
|
-
python_hwpx-5.
|
|
122
|
-
python_hwpx-5.
|
|
123
|
-
python_hwpx-5.
|
|
117
|
+
python_hwpx-5.7.0.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
|
|
118
|
+
python_hwpx-5.7.0.dist-info/licenses/NOTICE,sha256=auRgKYGdrOgWrj4kZfAuXQnqgDnx5myI9TI17IztIzQ,3466
|
|
119
|
+
python_hwpx-5.7.0.dist-info/METADATA,sha256=L4W3jUCNCTx15HU552LRUGTpIK5CuIFJegAlB6MEGCg,13162
|
|
120
|
+
python_hwpx-5.7.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
121
|
+
python_hwpx-5.7.0.dist-info/entry_points.txt,sha256=JUKRxbly9UaeHV7YzOea23y8IiqSTcrhUlooP3fS_Zc,405
|
|
122
|
+
python_hwpx-5.7.0.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
|
|
123
|
+
python_hwpx-5.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|