python-hwpx 2.24.0__py3-none-any.whl → 2.24.1__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.
@@ -2796,14 +2796,23 @@ class HwpxOxmlTableCell:
2796
2796
  preserve_format: bool = True,
2797
2797
  split_paragraphs: bool = False,
2798
2798
  ) -> None:
2799
+ previous_text = self.text
2800
+ sanitized_value = _sanitize_text(value)
2801
+ if sanitized_value and sanitized_value != previous_text:
2802
+ sublist = self._ensure_sublist()
2803
+ if (sublist.get("lineWrap") or "").upper() == "SQUEEZE":
2804
+ # SQUEEZE can compress a longer filled value until Hancom
2805
+ # renders adjacent glyphs on top of each other. New content
2806
+ # should wrap/reflow; untouched template cells keep their mode.
2807
+ sublist.set("lineWrap", "BREAK")
2799
2808
  if split_paragraphs:
2800
- self._set_split_paragraph_text(value)
2809
+ self._set_split_paragraph_text(sanitized_value)
2801
2810
  self.element.set("dirty", "1")
2802
2811
  self.table.mark_dirty()
2803
2812
  return
2804
2813
 
2805
2814
  text_element = self._ensure_text_element()
2806
- text_element.text = _sanitize_text(value)
2815
+ text_element.text = sanitized_value
2807
2816
  for node in self.element.findall(f".//{_HP}t"):
2808
2817
  if node is text_element:
2809
2818
  continue
hwpx/table_patch.py CHANGED
@@ -182,6 +182,9 @@ def build_grid(table: bytes) -> tuple[dict[tuple[int, int], _Cell], GridReport]:
182
182
 
183
183
 
184
184
  _P_SPAN_RE = re.compile(rb"<(?:[A-Za-z_][\w.-]*:)?p\b.*?</(?:[A-Za-z_][\w.-]*:)?p>", re.DOTALL)
185
+ _SQUEEZE_WRAP_RE = re.compile(
186
+ rb'<(?:[A-Za-z_][\w.-]*:)?subList\b[^>]*?\blineWrap="(?P<value>SQUEEZE)"'
187
+ )
185
188
 
186
189
  def _first_paragraph_span(cell: bytes) -> tuple[int, int] | None:
187
190
  """Byte span of the cell's first ``<hp:p>...</hp:p>`` (its own subList's
@@ -196,6 +199,22 @@ def _all_paragraph_spans(cell: bytes) -> list[tuple[int, int]]:
196
199
  return [(m.start(), m.end()) for m in _P_SPAN_RE.finditer(masked)]
197
200
 
198
201
 
202
+ def _squeeze_wrap_edit(cell: bytes) -> tuple[int, int, bytes] | None:
203
+ """Return a minimal edit that makes a filled cell wrap instead of squeeze.
204
+
205
+ Hancom's ``lineWrap=SQUEEZE`` keeps long replacement text on the existing
206
+ line by compressing glyph advances. Template placeholders often fit, but
207
+ real filled values can become nearly unreadable even though their charPr
208
+ spacing is normal. Only the addressed cell's direct subList is considered;
209
+ nested tables remain untouched.
210
+ """
211
+
212
+ match = _SQUEEZE_WRAP_RE.search(_mask_nested_tables(cell))
213
+ if match is None:
214
+ return None
215
+ return match.start("value"), match.end("value"), b"BREAK"
216
+
217
+
199
218
  # --- font shrink-to-fit helpers (byte-preserving charPr materialisation) ------
200
219
 
201
220
  def _header_part_name(parts: Mapping[str, bytes]) -> str | None:
@@ -602,6 +621,17 @@ def fill_cells(
602
621
  continue
603
622
  if not cell_edits:
604
623
  continue
624
+ # A template's short placeholder may legitimately fit under
625
+ # lineWrap=SQUEEZE, while a real filled value is compressed until
626
+ # glyphs appear to overlap. Changed non-empty values must wrap and
627
+ # reflow; preserve SQUEEZE for no-op/clear operations.
628
+ if text.strip():
629
+ wrap_edit = _squeeze_wrap_edit(cell_bytes)
630
+ if wrap_edit is not None:
631
+ ws, we, replacement = wrap_edit
632
+ section_edits.append(
633
+ (ts + cell.start + ws, ts + cell.start + we, replacement)
634
+ )
605
635
  section_edits.extend(cell_edits)
606
636
  applied.append(CellApplied(section_path, ti, row, col, first_orig or "", text))
607
637
  if section_edits:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 2.24.0
3
+ Version: 2.24.1
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -20,7 +20,7 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  License-File: NOTICE
23
- Requires-Dist: lxml<6,>=4.9
23
+ Requires-Dist: lxml<7,>=4.9
24
24
  Provides-Extra: visual
25
25
  Requires-Dist: pymupdf>=1.24; extra == "visual"
26
26
  Requires-Dist: pillow>=10.0; extra == "visual"
@@ -202,6 +202,15 @@ pip install -e ".[dev]"
202
202
  pytest
203
203
  ```
204
204
 
205
+ ## 감사의 말
206
+
207
+ 아래 공개 표준·프로젝트에 빚지고 있습니다.
208
+
209
+ - **[OWPML — 개방형 워드프로세서 마크업 언어 (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — HWPX가 기반하는 한국 산업 표준
210
+ - **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — OWPML 요소 구조 참조 모델 · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — 오라클 샘플 코퍼스
211
+ - **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — 멱등성·검증 게이트 설계 영감
212
+ - **범정부오피스** — 공무 문서 편집 워크플로 아이디어
213
+
205
214
  ## License
206
215
 
207
216
  Apache License 2.0. See LICENSE and NOTICE.
@@ -10,7 +10,7 @@ hwpx/guidance_scan.py,sha256=lN7wMUz0BaAgHCXdfK1CPEEmIqaFCuwpyL6XAQH6mqE,22921
10
10
  hwpx/package.py,sha256=0rKjGCJbPQvrVBIy07Jpjsu3fI7HhbqFCGWTiTDsJpo,1141
11
11
  hwpx/patch.py,sha256=8G1wgGVgCp0KyhQb-y0ZI2FJEatCwLt_JpepHJmYhYQ,23799
12
12
  hwpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- hwpx/table_patch.py,sha256=RLcZyYl6WDmwpSXvvCgwltltMPE47SF5HP99cnDT0vo,70945
13
+ hwpx/table_patch.py,sha256=FCpCNQSX9jta12gmmSyDshB-okANYSL3O5YS0Rr1mX4,72358
14
14
  hwpx/template_formfit.py,sha256=dlxf98FatT5Nl4hN15TuMXqj5NlDTGSnLaqXVo_PoBo,23424
15
15
  hwpx/templates.py,sha256=28bYqeJVeDb1Cq8G9NZG9Mhnu4K2GamAKC4QhxvUZyA,1187
16
16
  hwpx/builder/__init__.py,sha256=8tcxClbeuNle_R_2nDAauyy518OCegG8qVPjwIKRUZA,849
@@ -82,7 +82,7 @@ hwpx/opc/relationships.py,sha256=tPWLHRMlw0Spvtwou2jCDRfHdcm9FEKKLd95YVHLwYI,697
82
82
  hwpx/opc/security.py,sha256=hsA73sZxUoGIgy2zue9EnV7ChHMfasnplmqzxFs3Mp4,4419
83
83
  hwpx/opc/xml_utils.py,sha256=L_fHY1-D5I_TfdRkDQV-bn55EnXc6AqEDWItfMpawVs,3840
84
84
  hwpx/oxml/__init__.py,sha256=7koxG5Xc6ia1dWmAqUJJETkAL5wzamSrNVcKuiUH58I,5466
85
- hwpx/oxml/_document_impl.py,sha256=teZ-5dIaeu2DwIARiBop5u61bSuUkE3ND-lkRkZbBGk,249701
85
+ hwpx/oxml/_document_impl.py,sha256=K4Wc_LNn6xanOF9n-DxIoWNjiXiNA5fGd469fx76kWs,250246
86
86
  hwpx/oxml/body.py,sha256=3PzGiYmm03si5OVjGtS-R_tT1rWmsYwE8Ds8ZLhIIio,30609
87
87
  hwpx/oxml/canonical_defaults.py,sha256=WHAK7u_W-PDv3P3N1-1-aWAoiUgBh4x-UybFL4NqW-w,3791
88
88
  hwpx/oxml/common.py,sha256=TJkafzg7x4T3J29tZchRZk57ZTsrM9PEiqGT3rX3w5o,1044
@@ -161,10 +161,10 @@ hwpx/visual/diff.py,sha256=0X5T9IgwRZU3td-7vnPrlowovtGud7P_ymq0KVehlKk,5677
161
161
  hwpx/visual/masks.py,sha256=oXhgynAb4uKjJtZ2BGHHdAjyvWGqSFlZFQ-iJxzHiuo,1832
162
162
  hwpx/visual/oracle.py,sha256=VNO-tAB-lzTdW5XtSELxg_Aau369qRFBsJvNryHF_D8,31319
163
163
  hwpx/visual/report.py,sha256=2RhXN1KBYOZTim9FNpeUhaaDHR7oFxI6Z2DLUkDIiwE,1717
164
- python_hwpx-2.24.0.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
165
- python_hwpx-2.24.0.dist-info/licenses/NOTICE,sha256=k48h6EaGQE8Y1c0dS9sIOOcz4YqkbcImWClF7pBOgsg,2473
166
- python_hwpx-2.24.0.dist-info/METADATA,sha256=ApSL2b2I4Rt-VzvIohR_KreJfls9YMgbDzJ4TMOXNjw,10738
167
- python_hwpx-2.24.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
168
- python_hwpx-2.24.0.dist-info/entry_points.txt,sha256=4U6WXYWHxEiWp2VRHo97fvOYNh7ebu6roonk7chxKcY,453
169
- python_hwpx-2.24.0.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
170
- python_hwpx-2.24.0.dist-info/RECORD,,
164
+ python_hwpx-2.24.1.dist-info/licenses/LICENSE,sha256=_ubz4wv-BkkT3l3gu-QuH7JGeVjuRYGZoZK95eNsCHU,9688
165
+ python_hwpx-2.24.1.dist-info/licenses/NOTICE,sha256=k48h6EaGQE8Y1c0dS9sIOOcz4YqkbcImWClF7pBOgsg,2473
166
+ python_hwpx-2.24.1.dist-info/METADATA,sha256=GAvO0M817KGuKIfRoMOErZl7m-89GoIUDMT0bCTfFDs,11399
167
+ python_hwpx-2.24.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
168
+ python_hwpx-2.24.1.dist-info/entry_points.txt,sha256=4U6WXYWHxEiWp2VRHo97fvOYNh7ebu6roonk7chxKcY,453
169
+ python_hwpx-2.24.1.dist-info/top_level.txt,sha256=R1iToqDh80Nf2oQhRjTN0rbN2X6kyDUizIocZjkhuxc,5
170
+ python_hwpx-2.24.1.dist-info/RECORD,,