Skip to content

fix(table): write columns no narrower than Hancom draws them - #419

Open
airmang wants to merge 21 commits into
mainfrom
fix/table-column-floor
Open

airmang wants to merge 21 commits into
mainfrom
fix/table-column-floor

Conversation

@airmang

@airmang airmang commented Oct 6, 2026

Copy link
Copy Markdown
Owner

바뀐 점

새 표와 열 너비 바꾸기가, 한/글이 그리는 것보다 좁은 열을 그대로 쓰던 것을 고친다. 이제 한/글이 그리고 저장하는 폭으로 쓴다. 새 표 폭이 양의 int가 아니면 거부한다.

한/글의 동작

  • 표의 열을 적어도 그 칸들의 왼쪽·오른쪽 여백과 283을 더한 폭으로 그린다.
    • 새 표는 510 + 510 + 283 = 1303이다. 칸이 제 여백(0 + 0, 1000 + 1000)을 쓰면 283, 2283이다.
  • 칸에 적힌 좁은 폭은 그대로 둔다. 표는 그린 열 폭의 합으로 저장한다.
    • 폭 0으로 쓴 2×2 표는 2606이 된다.
    • 42520 폭 표에 열 비율 0과 1을 주면(열 0과 42520) 43823이 된다.
    • 첫 열 1302와 둘째 열 20000은 21303이 되고, 1304와 20000은 21304가 된다.

고치기 전

  • doc.add_table(2, 2, width=0)(또는 음수)은 표 폭과 칸 폭을 0으로 썼다.
  • 폭을 1000으로 준 두 열 표는 열마다 500으로 썼다.
  • set_column_widths([0, 1])은 열 0을 0으로 썼다.
  • 좁은 칸 안에 폭을 주지 않고 넣은 표는 그 칸의 남은 폭(바닥보다 좁을 수 있음)으로 썼다.

모두 한/글에서는 더 넓게 그려지고 표 폭이 바뀌어 저장됐다.

코드

  • oxml/table_sizes.py
    • COLUMN_FLOOR_EXTRA(283)와 NEW_TABLE_COLUMN_FLOOR(새 표 여백 510 + 510 + 283)를 더한다.
    • new_table_width: 새 표 폭을 적어도 열마다 바닥 폭으로 늘린다. 0 초과 2³¹ 미만의 int가 아니면 거부한다(table-width-value, bool도 거부).
    • set_column_widths: 나눈 열 폭이 그 열 칸들의 바닥(가장 넓은 좌우 여백 + 283)보다 좁으면 바닥으로 쓰고, 표 폭(sz)을 늘어난 합으로 쓴다. 한 열짜리 칸만 바닥을 정한다.
  • HwpxOxmlTable.create, HwpxOxmlParagraph.add_table, 칸의 add_table, HwpxDocument.add_table
    • 준 폭은 문단·런·테두리 채우기를 더하기 전에 검사한다.
    • 폭을 주지 않은 표는 칸 안에서도 바닥 아래로 쓰지 않는다.
  • errors.py와 docs/error-codes.md에 코드를 등록하고, 변경 로그 항목을 더한다.

테스트

  • tests/test_table_column_floor.py를 더하고, 한/글 저장본 여섯을 함께 넣는다.
    • 저장본: 첫 열 1302·1304(여백 510 + 510), 282(여백 0 + 0), 2282(여백 1000 + 1000)인 두 열 표, 폭 0인 2×2 표, 열 비율 0과 1을 준 표
    • 한/글이 열을 바닥 폭으로 그리고 칸 폭은 그대로 둔 채 표 폭을 늘려 저장했는지 시험한다.
    • 새 표가 그 폭으로 쓰이는지 시험한다. 폭 1·2605는 2606이 되고, 2606·2609는 그대로다. 좁은 칸 안의 표도 본다.
    • 0·음수·2³¹·실수·bool 폭이 오류 코드와 함께 거부되고 문서가 그대로인지 시험한다. 문서·칸·문단의 add_table을 모두 본다.
    • set_column_widths([0, 1])이 한/글이 저장한 표 폭(43823)을 쓰는지 시험한다.
    • 칸 여백이 0인 열이 283을 바닥으로 쓰는지(한/글 저장 20283) 시험한다.
  • tests/test_set_column_widths_uniform_split.py: 반올림을 고정하던 폭 13짜리 두 열 표는 이제 바닥 폭으로 늘어난다. 같은 반올림(짝수 쪽, 나머지는 마지막 열)을 바닥 위의 폭 2609로 시험한다.

이 PR은 #415, #416, #417, #418 위에 쌓았다. 앞 PR이 병합되면 그 커밋들은 이 PR의 diff에서 빠진다.

🤖 Generated with Claude Code

airmang and others added 12 commits October 6, 2026 07:25
add_picture wrote any align it was given into hp:pos@horzAlign ("BOGUS",
"  RIGHT ", "TOP"), which Hancom reads as LEFT; a non-string failed only after
the image and its paragraph were added. The alignment is now checked first,
in any case, against the schema's horizontal alignments, and anything else is
refused with shape-position-frame before anything is stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A negative width or height of a rectangle, ellipse, arc, picture or
equation box, or a negative rectangle corner ratio, was written as given;
Hancom reads it as 0. Such values, and sizes of 2**31 or more, are now
refused with typed errors before any paragraph, run or image is added
(shape-size-value, shape-rect-ratio-value). An equation's base_unit
outside 1..2**31-1 raises shape-equation-base-unit-value (a ValueError,
as before) before its paragraph is added. HwpxOxmlShape.resize refuses a
negative size too, and HwpxOxmlParagraph.add_picture builds the picture
before adding its run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hancom reads a caption's gap and the gap between columns of the same
width as signed 16-bit numbers and a new number as an unsigned one,
wrapping anything else (a column gap of 32768 makes the columns overlap;
a negative one written as text is read as 0). set_caption's gap outside
-32768..32767, a same_gap (or page setup column_gap_mm) outside 0..32767
and a restart number outside 0..65535 are now refused before anything
changes (shape-caption-gap-value, page-column-gap-value,
page-new-num-value). The caption side check moves to shape_position
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hancom reads a column's width and gap (hp:colSz) as a share of the text
width out of 32768 and lays the column out width * text width / 32768
wide, whatever the shares add up to. column_widths were written as
given, so widths in HWP units adding up to the text width came out about
1.3 times as wide and ran off the paper. They are now taken as
proportions and written as shares adding up to 32768 (column_shares);
widths that already add up to 32768 are written unchanged. Negative,
non-int, all-zero or malformed pairs are refused before anything changes
(page-column-widths-value). The page estimate takes a column's width as
its share of 32768 of the text width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hancom draws a table column at least its cells' left and right margins
and 283 wide (1303 in a new table), keeps the narrower width written in
the cells and saves the table as wide as it draws the columns. A new
table now gives each column at least that floor (a nested table without
a width in a narrow cell too), and set_column_widths writes a column
below its floor at the floor and widens the table by as much. A new
table width that is no int in 0 < width < 2**31 is refused before any
paragraph, run or border fill is added (table-width-value).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
airmang and others added 9 commits October 6, 2026 16:27
add_equation's base unit check now raises a typed error, so
oxml/paragraph.py raises one untyped error fewer. The census lock
records 202 untyped raises (paragraph.py 18), as the ratchet's
self-test expects the lock to match the tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
new_table_width returns None only when no width is given. Overloads
now say so, so the width HwpxOxmlTable.create builds the table with is
an int for mypy and pyright.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant