python-hwpx 3.5.0__py3-none-any.whl → 3.6.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/form_fit/engine.py CHANGED
@@ -55,6 +55,26 @@ class FitEngine:
55
55
  policy: FitPolicy | None = None,
56
56
  *,
57
57
  field_id: str | None = None,
58
+ ) -> FitResult:
59
+ result = self._fit_decide(value, slot, policy, field_id=field_id)
60
+ inline_count = int(getattr(slot, "inline_object_count", 0) or 0)
61
+ if inline_count and (value or "") != "":
62
+ inline_width = float(getattr(slot, "inline_object_width", 0.0) or 0.0)
63
+ result.warnings.append(
64
+ f"cell line shared with {inline_count} inline control(s) "
65
+ f"({inline_width:.0f} HWPUNIT deducted from the width budget); "
66
+ "control-sharing cells are unusual fill targets — verify with a "
67
+ "render gate or pick the intended value cell"
68
+ )
69
+ return result
70
+
71
+ def _fit_decide(
72
+ self,
73
+ value: str,
74
+ slot: SlotMetrics,
75
+ policy: FitPolicy | None = None,
76
+ *,
77
+ field_id: str | None = None,
58
78
  ) -> FitResult:
59
79
  policy = policy or FitPolicy()
60
80
  value = "" if value is None else str(value)
@@ -77,6 +97,29 @@ class FitEngine:
77
97
  if policy.mode == "expand_row":
78
98
  return self._expand_row(value, slot, policy, field_id)
79
99
 
100
+ # Inline objects can consume the whole line; with less than a glyph of
101
+ # width left there is nothing to reshape into — refuse with a message
102
+ # that names the cause instead of an absurd wrapped-line estimate.
103
+ if (
104
+ int(getattr(slot, "inline_object_count", 0) or 0)
105
+ and slot.available_width < slot.font_pt * 100.0 * 0.6
106
+ ):
107
+ return FitResult(
108
+ ok=False,
109
+ value=value,
110
+ applied_value=value,
111
+ lines=0,
112
+ font_pt=slot.font_pt,
113
+ overflow_detected=True,
114
+ confidence="high",
115
+ errors=[
116
+ "FIELD_OVERFLOW: inline control(s) leave no usable width in "
117
+ "this cell; choose the intended value cell or allow row "
118
+ "expansion"
119
+ ],
120
+ field_id=field_id,
121
+ )
122
+
80
123
  # Reshaping ladder: wrap, shrink, or wrap_then_shrink, then fail_on_overflow.
81
124
  wrap_lines = policy.effective_max_lines if policy.may_wrap else 1
82
125
  wrap_slot = replace(slot, max_lines=wrap_lines)
hwpx/form_fit/measure.py CHANGED
@@ -221,6 +221,13 @@ class SlotMetrics:
221
221
  # Per-line advance as a multiple of the em, from the cell's declared paragraph
222
222
  # line spacing (PERCENT). ``None`` falls back to ``DEFAULT_LINE_SPACING_RATIO``.
223
223
  line_spacing_ratio: float | None = None
224
+ # Width already consumed on the line by inline treat-as-char objects
225
+ # (checkboxes, form controls, pictures) that share the target paragraph.
226
+ # Their declared ``hp:sz/@width`` is subtracted from the usable width —
227
+ # ignoring them made the engine call "fits" on a fill that real Hancom
228
+ # wrapped, growing the row and repaginating a 10-page form (S-087 P0).
229
+ inline_object_width: float = 0.0
230
+ inline_object_count: int = 0
224
231
  # A cell height existed but was unusable (merged row-span fragment, or an
225
232
  # auto-grow floor shorter than one line). Records "height budget unavailable"
226
233
  # so the fit reports width-only honestly rather than guessing a vertical fit.
@@ -473,6 +480,11 @@ def resolve_slot_metrics(
473
480
  element = getattr(cell, "element", None)
474
481
  left, right = _cell_margin(element) if element is not None else (0, 0)
475
482
  inner = max(raw_width - left - right, 0.0) * safety
483
+ inline_width, inline_count = (
484
+ _inline_object_width(element) if element is not None else (0.0, 0)
485
+ )
486
+ if inline_width:
487
+ inner = max(inner - inline_width, 0.0)
476
488
  resolved_pt = font_pt if font_pt is not None else _first_run_font_pt(cell, document)
477
489
  line_ratio = _first_para_line_spacing_ratio(cell, document)
478
490
 
@@ -503,9 +515,49 @@ def resolve_slot_metrics(
503
515
  available_height=available_height,
504
516
  line_spacing_ratio=line_ratio,
505
517
  height_unavailable=height_unavailable,
518
+ inline_object_width=inline_width,
519
+ inline_object_count=inline_count,
506
520
  )
507
521
 
508
522
 
523
+ def _inline_object_width(cell_element: object) -> tuple[float, int]:
524
+ """Total declared width of inline treat-as-char objects in the cell.
525
+
526
+ Checkboxes and similar form controls flow on the text line
527
+ (``hp:pos/@treatAsChar='1'``) and consume their ``hp:sz/@width``; a fill
528
+ value shares whatever width remains. Objects without a declared size are
529
+ counted but contribute 0 width (the count still signals reduced trust).
530
+ """
531
+
532
+ total = 0.0
533
+ count = 0
534
+ iter_fn = getattr(cell_element, "iter", None)
535
+ if iter_fn is None:
536
+ return 0.0, 0
537
+ for node in iter_fn():
538
+ tag = getattr(node, "tag", "")
539
+ if not isinstance(tag, str):
540
+ continue
541
+ local = tag.rsplit("}", 1)[-1].lower()
542
+ if local != "pos":
543
+ continue
544
+ if (node.get("treatAsChar") or "").strip() not in {"1", "true", "TRUE"}:
545
+ continue
546
+ holder = node.getparent() if hasattr(node, "getparent") else None
547
+ if holder is None:
548
+ continue
549
+ count += 1
550
+ for sibling in holder:
551
+ sib_local = str(getattr(sibling, "tag", "")).rsplit("}", 1)[-1].lower()
552
+ if sib_local == "sz":
553
+ try:
554
+ total += float(sibling.get("width") or 0)
555
+ except (TypeError, ValueError):
556
+ pass
557
+ break
558
+ return total, count
559
+
560
+
509
561
  __all__ = [
510
562
  "SlotMetrics",
511
563
  "Measurement",
hwpx/opc/package.py CHANGED
@@ -159,10 +159,69 @@ def _local_name(element: etree._Element) -> str:
159
159
  return tag
160
160
 
161
161
 
162
+ def _paragraph_plain_text_length(paragraph: etree._Element) -> int | None:
163
+ """Plain text length of a simple paragraph, or ``None`` when unjudgeable.
164
+
165
+ Mirrors the oxml-side stale detector: only paragraphs made purely of runs
166
+ with text/tab-like children can be judged at the byte boundary; anything
167
+ else (controls, tables, fields) returns ``None`` so its cache is kept.
168
+ """
169
+ total = 0
170
+ for child in paragraph:
171
+ child_name = _local_name(child).lower()
172
+ if child_name in _LAYOUT_CACHE_ELEMENT_NAMES:
173
+ continue
174
+ if child_name != "run":
175
+ return None
176
+ for run_child in child:
177
+ run_child_name = _local_name(run_child).lower()
178
+ if run_child_name == "t":
179
+ total += len("".join(run_child.itertext()))
180
+ elif run_child_name in {"tab", "linebreak", "hyphen", "nbspace"}:
181
+ total += 1
182
+ else:
183
+ return None
184
+ return total
185
+
186
+
187
+ def _paragraph_layout_cache_is_stale(paragraph: etree._Element) -> bool:
188
+ text_length = _paragraph_plain_text_length(paragraph)
189
+ if text_length is None:
190
+ return False
191
+ for child in paragraph:
192
+ if _local_name(child).lower() not in _LAYOUT_CACHE_ELEMENT_NAMES:
193
+ continue
194
+ for line_seg in child:
195
+ if _local_name(line_seg).lower() != "lineseg":
196
+ continue
197
+ textpos = line_seg.get("textpos")
198
+ if textpos is None:
199
+ continue
200
+ try:
201
+ if int(textpos) > text_length:
202
+ return True
203
+ except ValueError:
204
+ return True
205
+ return False
206
+
207
+
162
208
  def _strip_section_layout_caches(payload: bytes) -> bytes:
209
+ """Drop only *stale* paragraph layout caches from a section payload.
210
+
211
+ Valid caches are the authored layout of untouched paragraphs; removing
212
+ them forces the editor to re-lay-out entire multi-page documents, which
213
+ shifted page counts and stacked glyphs in the wild form-fill differential
214
+ (specs/031 P0 receipt). The mutating APIs upstream clear the caches of
215
+ exactly the paragraphs they touch, so this byte-boundary sweep only
216
+ guards against writers that left a provably stale cache behind.
217
+ """
163
218
  root = parse_xml(payload)
164
219
  removed = False
165
220
  for parent in root.iter():
221
+ if _local_name(parent).lower() != "p":
222
+ continue
223
+ if not _paragraph_layout_cache_is_stale(parent):
224
+ continue
166
225
  for child in list(parent):
167
226
  if _local_name(child).lower() in _LAYOUT_CACHE_ELEMENT_NAMES:
168
227
  parent.remove(child)
@@ -1086,10 +1086,14 @@ class HwpxOxmlDocument:
1086
1086
  if self._manifest_dirty:
1087
1087
  updates[self._manifest_path] = _serialize_xml(self._manifest)
1088
1088
  for section in self._sections:
1089
- if section.dirty:
1090
- section.remove_layout_caches()
1091
- else:
1092
- section.remove_stale_layout_caches()
1089
+ # Edit-scoped invalidation: the mutating APIs (cell/paragraph/run
1090
+ # text and style setters) clear the caches of exactly the
1091
+ # paragraphs they touch, so even a dirty section only needs the
1092
+ # stale sweep as a safety net. Nuking every cache here forced
1093
+ # Hancom to re-lay-out untouched pages of multi-page forms, which
1094
+ # is what stacked glyphs and shifted page counts in the wild
1095
+ # form-fill differential (specs/031 P0 receipt).
1096
+ section.remove_stale_layout_caches()
1093
1097
  for section in self._sections:
1094
1098
  if section.dirty:
1095
1099
  updates[section.part_name] = section.to_bytes()
hwpx/oxml/paragraph.py CHANGED
@@ -914,6 +914,9 @@ class HwpxOxmlParagraph:
914
914
  changed = True
915
915
 
916
916
  if changed:
917
+ # A style swap changes glyph metrics, so the cached line layout of
918
+ # this paragraph no longer holds.
919
+ _clear_paragraph_layout_cache(self.element)
917
920
  self.section.mark_dirty()
918
921
 
919
922
  __all__ = ["HwpxOxmlParagraph"]
hwpx/oxml/run.py CHANGED
@@ -320,12 +320,16 @@ class HwpxOxmlRun:
320
320
  if value is None:
321
321
  if "charPrIDRef" in self.element.attrib:
322
322
  del self.element.attrib["charPrIDRef"]
323
+ _clear_paragraph_layout_cache(self.paragraph.element)
323
324
  self.paragraph.section.mark_dirty()
324
325
  return
325
326
 
326
327
  new_value = str(value)
327
328
  if self.element.get("charPrIDRef") != new_value:
328
329
  self.element.set("charPrIDRef", new_value)
330
+ # A style swap changes glyph metrics, so the cached line layout of
331
+ # the containing paragraph no longer holds.
332
+ _clear_paragraph_layout_cache(self.paragraph.element)
329
333
  self.paragraph.section.mark_dirty()
330
334
 
331
335
  def _plain_text_nodes(self) -> list[ET.Element]:
hwpx/oxml/table.py CHANGED
@@ -209,6 +209,7 @@ class HwpxOxmlTableCell:
209
209
  sublist.set("lineWrap", "BREAK")
210
210
  if split_paragraphs:
211
211
  self._set_split_paragraph_text(sanitized_value)
212
+ self._clear_own_layout_caches()
212
213
  self.element.set("dirty", "1")
213
214
  self.table.mark_dirty()
214
215
  return
@@ -226,9 +227,19 @@ class HwpxOxmlTableCell:
226
227
  current = current.getparent() if hasattr(current, "getparent") else None
227
228
  if current is not None:
228
229
  current.set("charPrIDRef", "0")
230
+ self._clear_own_layout_caches()
229
231
  self.element.set("dirty", "1")
230
232
  self.table.mark_dirty()
231
233
 
234
+ def _clear_own_layout_caches(self) -> None:
235
+ # Edit-scoped invalidation: only this cell's paragraphs lose their
236
+ # layout caches, so Hancom re-lays-out just the filled cell instead of
237
+ # the whole section (specs/031 P0 — the whole-section nuke shifted
238
+ # pages and stacked glyphs on multi-page forms).
239
+ for node in self.element.iter():
240
+ if _element_local_name(node) == "p":
241
+ _clear_paragraph_layout_cache(node)
242
+
232
243
  def remove(self) -> None:
233
244
  self._row_element.remove(self.element)
234
245
  self.table.mark_dirty()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 3.5.0
3
+ Version: 3.6.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -70,7 +70,7 @@ Dynamic: license-file
70
70
  `hwpx-mcp-server`와 `hwpx-plugin`은 같은 프로젝트가 직접 유지보수하는 first-party 연동 구성요소입니다.
71
71
  “first-party”는 프로젝트 유지보수 관계를 뜻하며, 한컴 또는 제3자의 공식 인증을 뜻하지 않습니다.
72
72
 
73
- 현재 PyPI 공개 릴리스는 `python-hwpx 3.5.0`입니다. 일반
73
+ 현재 PyPI 공개 릴리스는 `python-hwpx 3.6.0`입니다. 일반
74
74
  `pip install python-hwpx`로 이 릴리스를 설치할 수 있습니다.
75
75
  현재 패키지 분류는 `Development Status :: 3 - Alpha`입니다. 이 분류는 API와 제품의
76
76
  성숙도를 나타내며, 공개 버전이나 플러그인의 최소 호환 버전을 대신하지 않습니다.
@@ -94,8 +94,8 @@ hwpx/exam/parser.py,sha256=hA_oq_8i1XpmzJ8xw1L7R6LenxREKRvlOwMMXbs9Mpc,5026
94
94
  hwpx/exam/profile.py,sha256=pEYpgwoNUuMToHMJjiknHykjXs0UobGuYW67N_3VnEQ,4071
95
95
  hwpx/form_fit/__init__.py,sha256=l1HcFge4u_wL2vogB-VxSMrb-8h7zUT_IgTWMtnH9CM,1779
96
96
  hwpx/form_fit/apply.py,sha256=X2q7K-mLku4AXxm4-xP80QGzhBlpzpqA_Ade8aPXzNY,3450
97
- hwpx/form_fit/engine.py,sha256=N6twcIS-xjVdkCtVy03cHG8dGtRDtYV7pTRMhsPQ8rc,19799
98
- hwpx/form_fit/measure.py,sha256=HZfcsCrvv4GOPIp8Tiz7BHpw_Hv0LGWrpkpV19PKFPg,20221
97
+ hwpx/form_fit/engine.py,sha256=DiAGTRbpaH3WJNAiimRiv9KWHA-ijtUd5W2za-QhaWQ,21576
98
+ hwpx/form_fit/measure.py,sha256=gnSfqccm7_3oTKv3HH6Zs77r2bToxzmfvUWN2zshDE8,22323
99
99
  hwpx/form_fit/policy.py,sha256=iXAatjS7ZycOdKgMCL4Q_kYg0qnogFPdRNlr_ecOvjc,3106
100
100
  hwpx/form_fit/report.py,sha256=h1NzQfbj1JYvZtKUi0DckJNWGnUHMZtQ3iCzbUxUjHA,3305
101
101
  hwpx/form_fit/seal.py,sha256=QsPzFagaEi7UKAXS-iFwiZs8rLG-_Fo9RixLc2OaqbU,17640
@@ -106,7 +106,7 @@ hwpx/ingest/hwpx_converter.py,sha256=HOUCFYO-HK4WRpWiN5sA_kZIvdv439mkZ6PMCFU3hLg
106
106
  hwpx/layout/__init__.py,sha256=qotAVhol_yrPlOHvQfs68nx7ADYxPcwITYNA9gMutUM,1158
107
107
  hwpx/layout/lint.py,sha256=byhIkP5av_il1cDdm71Oj_Em7-HUuU0w1nwhzu8tpto,15410
108
108
  hwpx/layout/report.py,sha256=lwTIEWrZRHXc02k8_vpcaXNEcU1dDQ3dZ48y7Fxkxkc,3957
109
- hwpx/opc/package.py,sha256=4Ydaw-1uyTZ7DwNZ89ICJ7CHYwUGzeGR-FbZiyJ5XWk,35484
109
+ hwpx/opc/package.py,sha256=7i9h5HU2Vwfl5-SWPy8OHIfmLxrxTbkGnSqIc-Z0IUk,37824
110
110
  hwpx/opc/relationships.py,sha256=tPWLHRMlw0Spvtwou2jCDRfHdcm9FEKKLd95YVHLwYI,6971
111
111
  hwpx/opc/security.py,sha256=hsA73sZxUoGIgy2zue9EnV7ChHMfasnplmqzxFs3Mp4,4419
112
112
  hwpx/opc/xml_utils.py,sha256=L_fHY1-D5I_TfdRkDQV-bn55EnXc6AqEDWItfMpawVs,3840
@@ -117,22 +117,22 @@ hwpx/oxml/body.py,sha256=j-MJFHgInfOuFYYf-tsjFXvzPvKLt7HqUvc5Lhmp0is,30586
117
117
  hwpx/oxml/canonical_defaults.py,sha256=WHAK7u_W-PDv3P3N1-1-aWAoiUgBh4x-UybFL4NqW-w,3791
118
118
  hwpx/oxml/common.py,sha256=TJkafzg7x4T3J29tZchRZk57ZTsrM9PEiqGT3rX3w5o,1044
119
119
  hwpx/oxml/document.py,sha256=0Ml-t2fwa4f2xMfu5jbkOoh_KDKCCf3gNpaYICRV-KY,1172
120
- hwpx/oxml/document_parts.py,sha256=yd12u15Yzb10gtpdJfSLzOZkKdj1xY2xHYs5CbbB_TA,41281
120
+ hwpx/oxml/document_parts.py,sha256=fbj_MA8imnKhz6XN2ufNAMPYNI5xi0dCNjEv0nGESwo,41692
121
121
  hwpx/oxml/header.py,sha256=4LtTDPe7YW0o_4jPv3Rd-vyv5suRfebecxtTWbF65Uw,50011
122
122
  hwpx/oxml/header_part.py,sha256=BIwdzDlwJSxcVIErZ8jQuT2vQ4Tndr4Xa87beYbpSFU,50640
123
123
  hwpx/oxml/memo.py,sha256=QlmuELEzywnmywgjm94S3vzJB8wPpoFOmI8KVuzoNPw,9484
124
124
  hwpx/oxml/namespaces.py,sha256=c7JfdOdJbzrhyHvjbxoeeeloRE3xPuoB7v3YI8SmKDk,6524
125
125
  hwpx/oxml/numbering.py,sha256=9a0ARGW1DkKsTF7mEEXM4FA5loQmzIBuqE4APA8UFIQ,669
126
126
  hwpx/oxml/objects.py,sha256=44778UkUuoNZoAxIbaA6VjAbzDm0auV7TPtllhQyln0,15767
127
- hwpx/oxml/paragraph.py,sha256=YQXMkcjUVwPe0FanPY1VdJXzIT9ZDNOWTgyD3XwSVLk,32712
127
+ hwpx/oxml/paragraph.py,sha256=-E8A-DjKagjm47xNPnPkbIUYWNdEdAQowzl4X-EP6uI,32893
128
128
  hwpx/oxml/parser.py,sha256=pIfyNdW3WdFkCcE8JFY7hn1fobRbMngzfhVZICvWeVs,2937
129
- hwpx/oxml/run.py,sha256=L-ZhZ145eFG8OhrkSHHTWJ2Obp5O4SgXkA1X8fvyuHo,15232
129
+ hwpx/oxml/run.py,sha256=F939J1W6zQjr-JQ1Z8-nKh2s7NQ_0Okov4QJAEyw_5M,15503
130
130
  hwpx/oxml/schema.py,sha256=ElR3_IIhhPPZEqJtNKMNCHA-VFVLdhL454W_4spuvlc,1284
131
131
  hwpx/oxml/section.py,sha256=i3t1SKIMneA1qWAajDlmZ-yrWJF9VvqT-aoq3mRXtxE,11638
132
132
  hwpx/oxml/section_format.py,sha256=EKqod989mzcaPz-lDKM1Y7atdpFX9x_SdQ8zfCL3BpU,18320
133
133
  hwpx/oxml/section_story.py,sha256=zvUV7O4dtogWcM9-zudRAszCHRwfzTBMW2GGI1Q24kg,22410
134
134
  hwpx/oxml/simple_parts.py,sha256=dOVKWlUCQV8GePH0WRWPUmRylvH5O8bu0B6jj_4lejk,1864
135
- hwpx/oxml/table.py,sha256=w34AEoBC1Jkhks7OUwQM8MnQpEQMZ7ptdMlnS_mEY7k,39145
135
+ hwpx/oxml/table.py,sha256=SPm4a3s_xSq1DNBh-2VSK87mIGroPnSyLeBSM50-4ok,39708
136
136
  hwpx/oxml/utils.py,sha256=2L9au3SD_MFkFt2DXx3udXBT5caYuYO623lhGhgldVo,2972
137
137
  hwpx/presets/__init__.py,sha256=I3gSJDT2tuvxXbw3FOmI3CnbxvjDMPZfppyg6OlMHOI,535
138
138
  hwpx/presets/proposal.py,sha256=2yzLfDxOAT0P19dPX1RBWh56_AIOMsWqINDadtnKQoc,21176
@@ -199,10 +199,10 @@ hwpx/visual/page_qa.py,sha256=AuJCySfLIdXjPeekhAc3YnrIWOsN0sI35Pnz8BZ6Pro,7278
199
199
  hwpx/visual/qa_contracts.py,sha256=1cjoiRTAWgi7NgE8SPc6bKxdoUfKx9nyou1mgC47TxY,10506
200
200
  hwpx/visual/qa_metrics.py,sha256=I7RdybN-f1Fvcv2j-ceDPoek51nOkMui1Zrd7TEX_C8,9838
201
201
  hwpx/visual/report.py,sha256=2RhXN1KBYOZTim9FNpeUhaaDHR7oFxI6Z2DLUkDIiwE,1717
202
- python_hwpx-3.5.0.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
203
- python_hwpx-3.5.0.dist-info/licenses/NOTICE,sha256=k48h6EaGQE8Y1c0dS9sIOOcz4YqkbcImWClF7pBOgsg,2473
204
- python_hwpx-3.5.0.dist-info/METADATA,sha256=dokrEArymZrHXyslBq1OhwQwYg3NhnNSeFLpHx10ppk,13561
205
- python_hwpx-3.5.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
206
- python_hwpx-3.5.0.dist-info/entry_points.txt,sha256=PVUBTBQtBHiflQ9XBjfd004w-LrDibgZjwzxgu8Tx7w,480
207
- python_hwpx-3.5.0.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
208
- python_hwpx-3.5.0.dist-info/RECORD,,
202
+ python_hwpx-3.6.0.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
203
+ python_hwpx-3.6.0.dist-info/licenses/NOTICE,sha256=k48h6EaGQE8Y1c0dS9sIOOcz4YqkbcImWClF7pBOgsg,2473
204
+ python_hwpx-3.6.0.dist-info/METADATA,sha256=L4frTvOU4Mi8VP5EDr3ps2UPQ5c5Qx1Yadn0uu_CXVU,13561
205
+ python_hwpx-3.6.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
206
+ python_hwpx-3.6.0.dist-info/entry_points.txt,sha256=PVUBTBQtBHiflQ9XBjfd004w-LrDibgZjwzxgu8Tx7w,480
207
+ python_hwpx-3.6.0.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
208
+ python_hwpx-3.6.0.dist-info/RECORD,,