python-hwpx 6.2.1__py3-none-any.whl → 6.3.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.
@@ -200,8 +200,12 @@ def _refresh_copied_paragraph_subtree_ids(paragraph: ET.Element) -> None:
200
200
  }:
201
201
  node.set("id", _object_id())
202
202
 
203
- if "instId" in node.attrib:
204
- node.set("instId", _object_id())
203
+ # 스키마·실한컴 산출물의 속성명은 소문자 "instid"다. 과거 이 코드가
204
+ # 방출한 "instId"(카멜케이스)를 실은 파일도 재발급 대상에 남긴다 —
205
+ # 속성명 자체는 보존(바이트 보수), 값만 재발급.
206
+ for inst_attr in ("instid", "instId"):
207
+ if inst_attr in node.attrib:
208
+ node.set(inst_attr, _object_id())
205
209
 
206
210
 
207
211
  def _clone_paragraph_element(paragraph: ET.Element) -> ET.Element:
hwpx/oxml/body.py CHANGED
@@ -32,7 +32,10 @@ INLINE_OBJECT_NAMES = {
32
32
  "ole",
33
33
  "chart",
34
34
  "video",
35
- "audio",
35
+ # "audio"는 제외 — 실한컴(Windows 한글 2024 실측)은 소리 삽입을
36
+ # hp:ole(EMBEDDED/ICON)로 방출하고 hp:audio를 생성하지 않으며, 보유
37
+ # 실코퍼스 표본도 0건이다. 실물 미검증 요소를 인라인 읽기 지원으로
38
+ # 분류하지 않는다(빈도 0 = 읽기측 불투명 보존만). python-hwpx#89.
36
39
  "textart",
37
40
  }
38
41
 
hwpx/oxml/memo.py CHANGED
@@ -277,7 +277,9 @@ class HwpxOxmlNote:
277
277
 
278
278
  @property
279
279
  def inst_id(self) -> str | None:
280
- return self.element.get("instId")
280
+ # 정본 속성명은 "instid"(스키마·실한컴). "instId"는 과거 우리 저작이
281
+ # 방출한 카멜케이스 산출물 호환용 폴백.
282
+ return self.element.get("instid") or self.element.get("instId")
281
283
 
282
284
  @property
283
285
  def text(self) -> str:
@@ -128,7 +128,7 @@ def _paragraph_add_note(
128
128
  {
129
129
  "number": str(number),
130
130
  "suffixChar": str(ord(suffix[0])) if suffix else "41",
131
- "instId": _object_id(),
131
+ "instid": _object_id(),
132
132
  },
133
133
  )
134
134
  sublist_attrs = _default_sublist_attributes()
hwpx/table_patch.py CHANGED
@@ -1053,6 +1053,34 @@ def _delete_rows(table: str, del_rows: Iterable[int]) -> str:
1053
1053
  return _rebuild(prefix, rows, suffix, rowcnt=len(rows))
1054
1054
 
1055
1055
 
1056
+ def _reorder_rows(table: str, order: Sequence[int]) -> str:
1057
+ """물리 행 재배열 — 편집기 [표>정렬…] 대응의 엔진 프리미티브.
1058
+
1059
+ *order*는 현재 물리 행 인덱스의 완전 순열: 새 ``i``번째 행 = 기존
1060
+ ``order[i]``. 행 문자열을 통째로 옮기고 각 셀의 ``rowAddr``만 목적지
1061
+ 인덱스로 재기입한다(행 높이·서식·문단은 행과 함께 이동). 수직 병합
1062
+ (rowSpan>1)이 하나라도 있으면 refuse — 병합 블록을 가로지르는 재배열은
1063
+ 의미가 정의되지 않는다. 정렬 키 비교(어떤 순서로 놓을지)는 호출자 몫이다.
1064
+ """
1065
+ _guard_flat(table)
1066
+ prefix, rows, suffix = _parse_table(table)
1067
+ if sorted(order) != list(range(len(rows))):
1068
+ raise TableStructureError(
1069
+ f"reorder_rows: order must be a permutation of 0..{len(rows) - 1}, got {list(order)}"
1070
+ )
1071
+ for row in rows:
1072
+ for tc in _S_TC.findall(row):
1073
+ if (_si(tc, "cellSpan", "rowSpan") or 1) > 1:
1074
+ raise TableStructureError(
1075
+ "reorder_rows: vertical merge (rowSpan>1) present — refuse"
1076
+ )
1077
+ new_rows = [
1078
+ _map_cells(rows[src], lambda tc, dest=dest: _ss(tc, "cellAddr", "rowAddr", dest))
1079
+ for dest, src in enumerate(order)
1080
+ ]
1081
+ return _rebuild(prefix, new_rows, suffix)
1082
+
1083
+
1056
1084
  _PARA_ID_RE = re.compile(r'(<hp:p\b[^>]*\bid=")(\d+)(")')
1057
1085
 
1058
1086
 
@@ -1389,6 +1417,7 @@ def _widths_arg(o: Mapping[str, Any]) -> dict[int, int]:
1389
1417
  _STRUCT_OPS = {
1390
1418
  "delete_column": lambda t, o: _collapse_empty_rows(_delete_columns(t, o["cols"] if "cols" in o else [o["col"]])),
1391
1419
  "delete_row": lambda t, o: _delete_rows(t, o["rows"] if "rows" in o else [o["row"]]),
1420
+ "reorder_rows": lambda t, o: _reorder_rows(t, [int(x) for x in o["order"]]),
1392
1421
  "insert_row_by_clone": lambda t, o: _insert_row_by_clone(t, o["ref_row"], int(o.get("count", 1))),
1393
1422
  "insert_block_by_clone": lambda t, o: _insert_block_by_clone(t, int(o["ref_rows"][0]), int(o["ref_rows"][1]), int(o.get("count", 1))),
1394
1423
  "set_column_widths": lambda t, o: _set_column_widths(t, _widths_arg(o)),
@@ -261,7 +261,7 @@ def _md_handle_ctrl_child(child, state: _MdParagraphState, base_cp, chars, doc=N
261
261
  def _md_handle_note_child(
262
262
  child, tag: str, state: _MdParagraphState, base_cp, chars, doc, notes_out: list | None
263
263
  ) -> None:
264
- inst_id = child.get("instId", "")
264
+ inst_id = child.get("instid") or child.get("instId", "")
265
265
  kind = "fn" if tag == "footNote" else "en"
266
266
  marker = f"[^{kind}{inst_id}]"
267
267
  if state.link_url is not None:
@@ -317,7 +317,7 @@ class ObjectFinder:
317
317
  ) -> AnnotationMatch:
318
318
  path = describe_element_path(element, parent_map)
319
319
  found = FoundElement(section=section, path=path, element=element)
320
- inst_id = element.get("instId") or ""
320
+ inst_id = element.get("instid") or element.get("instId") or ""
321
321
  behavior = options.footnote if kind == "footnote" else options.endnote
322
322
  if behavior == "inline":
323
323
  text = _resolve_note_text(
@@ -331,14 +331,17 @@ def _first_child_by_local(element: ET.Element, name: str) -> ET.Element | None:
331
331
  return None
332
332
 
333
333
 
334
- def _simple_paragraph_text_length(paragraph: ET.Element) -> int | None:
335
- """Return visible text length for plain text-only paragraphs.
334
+ def _simple_paragraph_text(paragraph: ET.Element) -> str | None:
335
+ """Return visible text for plain text-only paragraphs (``None`` = 판정 불가).
336
336
 
337
337
  Paragraphs containing fields, shapes, tables, or other embedded controls are
338
338
  skipped to avoid guessing how a specific editor counts their layout units.
339
+ Single-position control characters (tab/linebreak/hyphen/nbspace) are
340
+ represented as one space — a deliberate width underestimate so downstream
341
+ width judgments stay conservative.
339
342
  """
340
343
 
341
- total = 0
344
+ pieces: list[str] = []
342
345
  for child in paragraph:
343
346
  child_name = _local_name(child).lower()
344
347
  if child_name == "linesegarray":
@@ -348,12 +351,73 @@ def _simple_paragraph_text_length(paragraph: ET.Element) -> int | None:
348
351
  for run_child in child:
349
352
  run_child_name = _local_name(run_child).lower()
350
353
  if run_child_name == "t":
351
- total += len("".join(run_child.itertext()))
354
+ pieces.append("".join(run_child.itertext()))
352
355
  elif run_child_name in {"tab", "linebreak", "hyphen", "nbspace"}:
353
- total += 1
356
+ pieces.append(" ")
354
357
  else:
355
358
  return None
356
- return total
359
+ return "".join(pieces)
360
+
361
+
362
+ def _simple_paragraph_text_length(paragraph: ET.Element) -> int | None:
363
+ text = _simple_paragraph_text(paragraph)
364
+ return None if text is None else len(text)
365
+
366
+
367
+ #: 꼬리 폭 판정 마진 — 추정 폭이 줄 폭의 이 배수를 넘을 때만 증명으로 취급.
368
+ _STALE_TAIL_WIDTH_FACTOR = 2.0
369
+ _STALE_TAIL_MIN_CHARS = 8
370
+
371
+
372
+ def _check_line_seg_tail_coverage(
373
+ issues: list[PackageValidationIssue],
374
+ part_name: str,
375
+ paragraph_index: int,
376
+ text: str,
377
+ line_segs: list[ET.Element],
378
+ ) -> None:
379
+ """Catch caches that lack lines for text grown past the last cached line.
380
+
381
+ The stale-cache direction the textpos range check cannot see: an edit made
382
+ the paragraph *longer* but an old ``<hp:linesegarray>`` survived, so every
383
+ cached ``textpos`` is still in range — yet the cache has too few lines and
384
+ Hancom renders the tail overlapped (the tracked-change overlap defect).
385
+ Provable-with-margin: the tail after the last cached line start must not
386
+ measure wider than that line's extent × ``_STALE_TAIL_WIDTH_FACTOR``,
387
+ with width estimated by the same conservative metrics the overflow lint
388
+ uses.
389
+ """
390
+
391
+ last: tuple[int, int, int] | None = None
392
+ for seg in line_segs:
393
+ try:
394
+ textpos = int(seg.get("textpos") or "")
395
+ horzsize = int(seg.get("horzsize") or "")
396
+ textheight = int(seg.get("textheight") or "")
397
+ except ValueError:
398
+ return
399
+ if last is None or textpos > last[0]:
400
+ last = (textpos, horzsize, textheight)
401
+ if last is None:
402
+ return
403
+ last_pos, horzsize, textheight = last
404
+ if horzsize <= 0 or textheight <= 0 or not 0 <= last_pos <= len(text):
405
+ return
406
+ tail = text[last_pos:]
407
+ if len(tail) < _STALE_TAIL_MIN_CHARS:
408
+ return
409
+ from hwpx.form_fit.measure import estimate_text_width
410
+
411
+ tail_width = estimate_text_width(tail, textheight / 100.0)
412
+ if tail_width > horzsize * _STALE_TAIL_WIDTH_FACTOR:
413
+ _error(
414
+ issues,
415
+ part_name,
416
+ f"paragraph {paragraph_index} lineseg cache lacks lines for its tail: "
417
+ f"{len(tail)} chars after the last cached line start (textpos={last_pos}) "
418
+ f"measure ~{tail_width / horzsize:.1f}x the cached line extent {horzsize} "
419
+ "— stale layout cache; Hancom reuses it and renders the tail overlapped",
420
+ )
357
421
 
358
422
 
359
423
  def _check_line_seg_text_positions(
@@ -367,15 +431,18 @@ def _check_line_seg_text_positions(
367
431
  for paragraph_index, paragraph in enumerate(
368
432
  element for element in root.iter() if _local_name(element) == "p"
369
433
  ):
370
- text_length = _simple_paragraph_text_length(paragraph)
371
- if text_length is None:
434
+ text = _simple_paragraph_text(paragraph)
435
+ if text is None:
372
436
  continue
437
+ text_length = len(text)
373
438
  for child in paragraph:
374
439
  if _local_name(child).lower() != "linesegarray":
375
440
  continue
441
+ line_segs: list[ET.Element] = []
376
442
  for line_seg in child:
377
443
  if _local_name(line_seg).lower() != "lineseg":
378
444
  continue
445
+ line_segs.append(line_seg)
379
446
  textpos_raw = line_seg.get("textpos")
380
447
  if textpos_raw is None:
381
448
  continue
@@ -396,6 +463,9 @@ def _check_line_seg_text_positions(
396
463
  f"{paragraph_index} has stale lineseg textpos={textpos} "
397
464
  f"beyond text length {text_length}",
398
465
  )
466
+ _check_line_seg_tail_coverage(
467
+ issues, part_name, paragraph_index, text, line_segs
468
+ )
399
469
 
400
470
 
401
471
  def _check_table_editor_acceptance(
@@ -483,7 +483,7 @@ class TextExtractor:
483
483
  return
484
484
 
485
485
  kind_name = "footnote" if kind == "footNote" else "endnote"
486
- inst_id = element.get("instId") or ""
486
+ inst_id = element.get("instid") or element.get("instId") or ""
487
487
 
488
488
  if option == "placeholder":
489
489
  fragments.append(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 6.2.1
3
+ Version: 6.3.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 문서 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -9,7 +9,7 @@ hwpx/mutation_report.py,sha256=Uw9fxft8kioXpQ0feQwSlq4vOj3-418Oywt7qmDmU_8,19494
9
9
  hwpx/package.py,sha256=0rKjGCJbPQvrVBIy07Jpjsu3fI7HhbqFCGWTiTDsJpo,1141
10
10
  hwpx/patch.py,sha256=k_LoVxHzIBNs4kg2V4yBOgAxjsrim7pksG0rmRmLApE,27264
11
11
  hwpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- hwpx/table_patch.py,sha256=GnDkQDQf5w5XOp0uVkw2WtQMNq-2DeZ3Lo0FHrQyz5k,94351
12
+ hwpx/table_patch.py,sha256=GrQNmkdTS7nBolGOX473NSvq-PglM8qVSAH1zDaPJCQ,95805
13
13
  hwpx/templates.py,sha256=28bYqeJVeDb1Cq8G9NZG9Mhnu4K2GamAKC4QhxvUZyA,1187
14
14
  hwpx/_document/__init__.py,sha256=REiNqMbuk_TS4NVC27FIo1rUt3Z8nhJrTBGOgNtxBRg,90
15
15
  hwpx/_document/_legacy.py,sha256=kmiiJ-U8GEUrSvTGG7L-3TaEoLGucCPMgEuGfAJoTb4,59780
@@ -73,8 +73,8 @@ hwpx/opc/security.py,sha256=0wZitXK0J55wkbsWhyMyyufq0syBu_ZsBkqozrp-2DA,9062
73
73
  hwpx/opc/xml_utils.py,sha256=L_fHY1-D5I_TfdRkDQV-bn55EnXc6AqEDWItfMpawVs,3840
74
74
  hwpx/oxml/__init__.py,sha256=Ogl__NFyPORPxboCHqrsn2IrRdcjaQBzmnYm4pPWXpA,6731
75
75
  hwpx/oxml/_document_impl.py,sha256=41cxktJqXHER37fjA5HIFR8kw7dHMoISTxEv53PRBYo,5367
76
- hwpx/oxml/_document_primitives.py,sha256=k0iwhuCvh_ySCTiWAoldXuLO7FPIu8jY6C0fsW3j40I,56555
77
- hwpx/oxml/body.py,sha256=JAjg75SP8Qj14XbF7_I54pLU_jt3N59M969tG1-ZqtY,51850
76
+ hwpx/oxml/_document_primitives.py,sha256=IcGRT7B4gblNgJ5XDivV_DME-5cU5p88hRSAM1L8_Hk,56887
77
+ hwpx/oxml/body.py,sha256=RRopsZ7rvHDMIYNoEwHPty7DXyaZaFRDqJQSI6gVIls,52196
78
78
  hwpx/oxml/canonical_defaults.py,sha256=WHAK7u_W-PDv3P3N1-1-aWAoiUgBh4x-UybFL4NqW-w,3791
79
79
  hwpx/oxml/color.py,sha256=K2lbU_JdqRxFkQelGD6BbBkH1IspJSQyGlKHNz1Nmf4,2393
80
80
  hwpx/oxml/common.py,sha256=TJkafzg7x4T3J29tZchRZk57ZTsrM9PEiqGT3rX3w5o,1044
@@ -90,9 +90,9 @@ hwpx/oxml/header_part.py,sha256=quQowt3JXSavPPE4AlOEKlDAFPLL_dc2wUdpsqLFvzY,6220
90
90
  hwpx/oxml/history_part.py,sha256=U13RujivAo-mo_B-PvBxOx1JtAP_Uc1U-VcMdjjnukk,6555
91
91
  hwpx/oxml/master_page.py,sha256=kS_4afrXtWL9TI_QpsOhhCQeChFT6pGHtynPxUDaeCM,4172
92
92
  hwpx/oxml/master_page_authoring.py,sha256=U9Qt87G6XQ26nEJhHlWBclGyUV_rwGcf4WVuqi-12Dg,6645
93
- hwpx/oxml/memo.py,sha256=GOBYGaZvT4Md7Hq_Hm7c38svHQrEVHHew_tTxoiGAqM,13403
93
+ hwpx/oxml/memo.py,sha256=24wUY2rPMdKkHceMGFKQg9tEoiCZjM6HLAPwydPTQE4,13595
94
94
  hwpx/oxml/namespaces.py,sha256=SPYOj0N3qm6TCcl2xiEj7hkVdCVHqx0GpkF3kKDKoZg,6838
95
- hwpx/oxml/note_authoring.py,sha256=OJBzvAa3mfJJT3V9KcgsbW5f6DCDR_NKKM84WoqMvck,7198
95
+ hwpx/oxml/note_authoring.py,sha256=xOoYeX640XkwdftUQeHsdojGHHbiLbobZPi3aahh5ek,7198
96
96
  hwpx/oxml/numbering.py,sha256=9a0ARGW1DkKsTF7mEEXM4FA5loQmzIBuqE4APA8UFIQ,669
97
97
  hwpx/oxml/numbering_kinds.py,sha256=dikaExXr8GZ0gNL4KM5xxvGQOZDCkCdcA5qKDLsstsg,4034
98
98
  hwpx/oxml/objects.py,sha256=8gUCyH8dMDqiz_TmISKu_ZxIIsboZOc3wJ9A6KENKz4,54118
@@ -132,10 +132,10 @@ hwpx/tools/idempotence.py,sha256=UNIgfItSCoMFBxSfd3vhA-1AMJ5YJ4eGLtdOpWtfZtc,527
132
132
  hwpx/tools/ir_equality.py,sha256=zku8kKVNwrADcwLXWvmQ4U8VlhOgliPWyv9Ca2ao1yg,4860
133
133
  hwpx/tools/layout_preview.py,sha256=-gXLXBgLnIxxFpRh--oyKOodsekzoFZZ_4xSZW2jB2U,22723
134
134
  hwpx/tools/mail_merge.py,sha256=hGELwzgLm2Qxm320ldONt9npgAP50TkPRP_ZHLOGw3Y,18590
135
- hwpx/tools/markdown_export.py,sha256=trOvT9SzBCL5hMVUyuRe9_dT0-ibhTAPor7ty0vk7_g,21515
136
- hwpx/tools/object_finder.py,sha256=ovXlbuPiQFdGzNSU08SuEzNFoeYcIb7GfF6vB9MRwBE,13800
135
+ hwpx/tools/markdown_export.py,sha256=yEMWK66izjwxDJCSbDlVCrQU90GWkd_QCu3Y2ki_i7M,21538
136
+ hwpx/tools/object_finder.py,sha256=sKP2aF-uSk8-dPT4_Ew_Su5NlSpKVgw2FiP_iqkdU-c,13825
137
137
  hwpx/tools/package_reconcile.py,sha256=y1Hl7hbPh4YaV59LTdDLzQwgn4g1qEnFmSjmajnrEbA,2416
138
- hwpx/tools/package_validator.py,sha256=kj0hudG1kcc4_diBEFBc8UWz6KtusC8brCqXTYkZHcU,37619
138
+ hwpx/tools/package_validator.py,sha256=OMbJ8dzB7fdNrhE7S8cKdMEkeLim0joQ2TsPutlLxnU,40476
139
139
  hwpx/tools/page_guard.py,sha256=Qg9vHP46nnq9W5GCyaHbpNY5Ndt7ecJHAdSs8BfRggQ,9622
140
140
  hwpx/tools/read_fidelity.py,sha256=f_a3Kftee3MRaCGraC6KjQDZ-iz3hv3b8-GGC5VCgyw,10528
141
141
  hwpx/tools/recover.py,sha256=EOVAzMFAqR9YAT3sinZKCdjSkKygo4dKrs6T6SbGA7o,4963
@@ -147,16 +147,16 @@ hwpx/tools/table_cleanup.py,sha256=0_f6NnvNp3QD4owKd_bRX6FZbeUmoQC7a4_VGzF2SCE,1
147
147
  hwpx/tools/table_navigation.py,sha256=rtbrWFKpJhqC3LD0ZXImyHgjmDR2hjHCFy3_S-qNBwA,16479
148
148
  hwpx/tools/template_analyzer.py,sha256=iH7egGRYNb8UesDB1TJ4tvW41-mlmv5Ap3VacqJFSlE,21355
149
149
  hwpx/tools/text_extract_cli.py,sha256=BmsDAwNXpDPhEayb9ez2ORtGNzPd_Xxduy4_cLXhnUw,2188
150
- hwpx/tools/text_extractor.py,sha256=9Whxq3ncGZ37prvSgwvp5TAhh2PIySl4at9NCcm5leI,27904
150
+ hwpx/tools/text_extractor.py,sha256=PZ8rCsCJMDK8wjOgfcakfoTQ5iVviH2IhWF_9l_NzY4,27929
151
151
  hwpx/tools/toc_author.py,sha256=eS1Zm5OBoonuD_H92JxcN2o276d5XmcJDqpfscd4wSg,15445
152
152
  hwpx/tools/toc_fidelity.py,sha256=rvoKH8QJ65WNVrxS0d-i2AtODvBy-4O8NRudfQ244v4,19890
153
153
  hwpx/tools/validator.py,sha256=U856izL9NcJZOiKDYoCpwOSaFNQ2Un8Jn4pzL20A96Q,7100
154
154
  hwpx/tools/_schemas/header.xsd,sha256=mJXuFMuHGT1JnFFaluUpYUglwjMCNlfbFCRVM26eHXE,664
155
155
  hwpx/tools/_schemas/section.xsd,sha256=MgvavVHG05RDfUnVPxVU10H4FQOja5ON04_m9Uk_m7E,522
156
- python_hwpx-6.2.1.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
157
- python_hwpx-6.2.1.dist-info/licenses/NOTICE,sha256=he3PBLXnIZK0sNsozc-SAQ2jvWjV61goylWUZ8HPi3s,3837
158
- python_hwpx-6.2.1.dist-info/METADATA,sha256=UF4HklQVOIHQuJPGLtYjXo9DKg2hI9ioq0mYUKdj8Pg,13652
159
- python_hwpx-6.2.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
160
- python_hwpx-6.2.1.dist-info/entry_points.txt,sha256=JUKRxbly9UaeHV7YzOea23y8IiqSTcrhUlooP3fS_Zc,405
161
- python_hwpx-6.2.1.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
162
- python_hwpx-6.2.1.dist-info/RECORD,,
156
+ python_hwpx-6.3.0.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
157
+ python_hwpx-6.3.0.dist-info/licenses/NOTICE,sha256=he3PBLXnIZK0sNsozc-SAQ2jvWjV61goylWUZ8HPi3s,3837
158
+ python_hwpx-6.3.0.dist-info/METADATA,sha256=Ybcxl0xVXkDcdsqWplRYdERcAEZjAxQLqayu3AUnqV8,13652
159
+ python_hwpx-6.3.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
160
+ python_hwpx-6.3.0.dist-info/entry_points.txt,sha256=JUKRxbly9UaeHV7YzOea23y8IiqSTcrhUlooP3fS_Zc,405
161
+ python_hwpx-6.3.0.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
162
+ python_hwpx-6.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (83.0.0)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5