Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

### 고침

- 표 행·열·칸 구조 편집(객체 경로와 `apply_table_ops`)이 쪽을 넘어 나뉜 표의 저장 높이(`hp:sz`)를 그대로 둔다. 한/글은
그런 표에 첫 쪽 조각의 높이만 저장하므로(같은 행의 표가 놓인 자리에 따라 30463·17551·5865로 저장됨) 행 높이 증감을
더할 근거가 없다. 전에는 증감을 그대로 더해, 평가계획 채우기가 13행 표(행 28762, 저장 13222)에서 9행을 지우면
높이가 0이 됐다(#539 후속). 행 높이만큼 저장된 표는 전처럼 증감만큼 움직인다.

- 줄 캐시(`write_line_caches`)가 머리에 정의되지 않은 글머리표·번호를 가리키는 문단 모양(예: 글머리표 idRef 0)의
문단에는 캐시를 쓰지 않는다. 한/글은 정의가 없어도 글머리 자리를 잡아 첫 줄에 글자를 덜 넣는데, 전에는 글머리 없이
나눈 줄을 써서 한/글이 그 줄을 눌러 담을 수 있었다.
Expand Down
9 changes: 8 additions & 1 deletion src/hwpx/oxml/table_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,20 @@ def _rows_height(self) -> int | None:
def _grow_height(self, before: int | None) -> None:
"""Move the table's saved height (``hp:sz``) by what its rows gained or lost in the edit, as
Hancom saves the height of the rows it lays out: the rows left as they were keep the height Hancom
gave them. With rows the estimate does not follow, the height stays as it was."""
gave them. With rows the estimate does not follow, the height stays as it was.

A table saved shorter than its rows is one Hancom split across pages: it saves the height of the
part on the table's first page (tables of the same rows save different heights by where they start),
and the rows' gain or loss does not say how that part changes. Its height also stays as it was, rather
than moving by the whole delta, which can take it to nothing."""

after = self._rows_height()
size = self.element.find(f"{_HP}sz")
if before is None or after is None or size is None or after == before:
return
height = int(size.get("height", 0) or 0)
if height < before:
return
size.set("height", str(max(0, height + after - before)))


Expand Down
38 changes: 38 additions & 0 deletions tests/test_table_structure_edits.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,41 @@ def test_a_table_holding_a_table_keeps_it_through_an_edit() -> None:
assert [table.cell(row, 0).tables[0].cell(0, 0).text for row in (0, 1)] == ["inner", "inner"]
with HwpxDocument.open(document.to_bytes()) as reopened:
assert reopened.tables[0].row_count == 3


BLANK_3HAK = Path(__file__).parent / "fixtures" / "m105_evalplan" / "blank_form_3hak.hwpx"


def _saved_heights(data: bytes) -> list[int]:
with zipfile.ZipFile(io.BytesIO(data)) as archive:
section = etree.fromstring(archive.read("Contents/section0.xml"))
return [int(table.find(HP + "sz").get("height")) for table in section.iter(HP + "tbl")]


def test_a_table_hancom_split_across_pages_keeps_its_saved_height_through_row_edits() -> None:
# In this Hancom-saved evaluation plan, tables 19-21 have the same 15 rows and save 30463, 17551 and 5865:
# a table split across pages saves the height of its part on the first page, not its rows' height. Table 8
# (13 rows of 28762 saved as 13222) is one of them. Moving that height by the rows' delta took it to 0
# when the evaluation-plan fill deleted nine of its rows.
source = BLANK_3HAK.read_bytes()
saved = _saved_heights(source)
assert saved[19:22] == [30463, 17551, 5865]
op = {"op": "delete_row", "table_index": 8, "rows": list(range(4, 13))}

by_bytes = apply_table_ops(source, [op])
assert by_bytes.ok, by_bytes.skipped
assert _saved_heights(by_bytes.data)[8] == saved[8] == 13222

document = HwpxDocument.open(source)
document.tables.all[8].delete_rows(list(range(4, 13)))
assert _saved_heights(document.to_bytes())[8] == 13222


def test_a_table_saved_as_tall_as_its_rows_still_moves_by_their_delta() -> None:
# Table 9 of the same form fits its page: it saves its rows' 25961, so a deleted row takes its height.
source = BLANK_3HAK.read_bytes()
assert _saved_heights(source)[9] == 25961

result = apply_table_ops(source, [{"op": "delete_row", "table_index": 9, "rows": [9]}])
assert result.ok, result.skipped
assert 0 < _saved_heights(result.data)[9] < 25961
Loading