Skip to content

feat(table): insert columns by cloning a grid column - #430

Open
airmang wants to merge 6 commits into
mainfrom
feat/table-insert-column
Open

airmang wants to merge 6 commits into
mainfrom
feat/table-insert-column

Conversation

@airmang

@airmang airmang commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

바뀐 점

열 삽입 insert_column_by_clone

표 구조 편집(hwpx.table_patch.apply_table_ops)은 행 삽입(insert_row_by_clone)만 있고 열 삽입이 없었다. 이제 격자 열 하나를 복제해 그 오른쪽(또는 왼쪽)에 열을 넣는다.

apply_table_ops(data, [{"op": "insert_column_by_clone", "table_index": 0, "ref_col": 1}])
apply_table_ops(data, [{"op": "insert_column_by_clone", "table_index": 0, "ref_col": 0,
                        "side": "left", "count": 2, "blank": True}])
  • ref_col: 기준 격자 열. side는 "right"(기본) 또는 "left". count는 넣을 열 수.
  • 새 칸은 행 삽입처럼 기준 칸의 글까지 복제한다. blank: true면 빈 문단 하나만 둔다(한/글과 같음).

한/글의 동작

한/글로 3×3 표를 만들고, 1열 칸에 커서를 둔 채 칸을 추가해 저장했다.

  • 새 열은 기준 열과 같은 폭이다.
    • 표는 그만큼 넓어진다. 본문 폭을 넘어도 줄이지 않는다.
    • 1열만 넓힌 표에서는 새 열도 1열 폭이다.
  • 새 칸은 같은 행 기준 칸의 테두리, 문단 모양, 글자 모양을 따르고 글은 비어 있다.
    • 커서 칸이 아니라 행마다 그 행의 기준 칸을 따른다.
  • 합친 칸
    • 새 열을 가로지르는 칸: colSpan이 는다. 저장된 폭은 옛 값 그대로 두고, 그릴 때는 격자대로 넓힌다.
    • 기준 열에서 끝나는 가로 합친 칸: 새 열에 그 칸의 서식을 따른 칸이 따로 생긴다.
    • 기준 열의 세로 합친 칸: 새 열에도 같은 행들에 걸친 칸 하나가 생긴다.

insert_column_by_clone은 이 규칙을 따른다. 다른 점은 둘이다.

  • 가로지르는 칸의 폭은 열 폭의 합으로 쓴다(그리는 폭과 같음).
  • 기본으로는 글을 복제한다(행 삽입과 같은 뜻).

칸 영역(hp:cellzone)의 열 주소도 함께 옮긴다. 영역 뒤의 열이면 밀고, 새 열을 가로지르면 넓힌다.

비표준 칸의 표에서 멈추던 것

다른 프로그램이 쓴 표 가운데 hp:tc 안에 문단만 있고 hp:subList·hp:cellAddr·hp:cellSpan·hp:cellSz가 없는 표가 있다.

  • 고치기 전
    • 행·열 삭제, 행·열 삽입, 열 폭 설정·맞춤, 표 나누기는 AssertionError로 멈췄다.
    • 행 삽입과 행 순서 바꾸기는 엉뚱한 이유로 거부했다.
  • 이제 이 편집들이 먼저 부르는 _guard_flat이 그런 표를 표 안의 표처럼 거부한다.

표의 행·열을 모두 지우던 것

  • 고치기 전
    • delete_column이 1열 표의 열이나 모든 열을 지우면 ZeroDivisionError로 멈췄다(나머지 열에 나눠 줄 폭을 0개 열에 나눔).
    • delete_row가 모든 행을 지우면 남는 격자 탓으로만 거부했다.
  • 한/글도 1열 표의 열, 1행 표의 행은 지우지 않는다(표 그대로).
  • 이제 둘 다 거부하고 delete_table을 쓰라고 알린다.

코드

  • table_patch.py
    • _insert_column_by_clone, _shift_zone_columns를 더함.
    • 행 삽입과 함께 쓰는 문단 id 도우미 _fresh_ids를 뺌.
    • 문서 전체의 문단 id를 피하는 op 표 _CLONE_OPS를 둠.
    • _guard_flat에 칸 주소·크기 검사를 더함.
    • _delete_columns·_delete_rows가 모든 열·행 삭제를 거부.
  • docs/editor-menu-reverse-map.md: "줄/칸 추가하기" 행에 새 op를 적음.
  • 변경 로그.

테스트

tests/test_insert_column_by_clone.py

한/글 저장본 열여덟 개를 함께 넣는다. 기준 표 8개와, 한/글이 열을 넣은 표 10개다.

  • 한/글이 넣은 열과의 대조(10쌍)
    • 대상: 오른쪽, 왼쪽, 3개, 넓은 열, 합친 칸 셋(가로지름, 기준 열에서 끝남, 세로), 행마다 다른 서식 둘, 본문 폭 표.
    • 한/글이 만든 기준 표에 blank: true로 같은 열을 넣는다.
    • 칸마다 주소, 범위, 폭, 높이, 테두리, 문단·글자 모양, 글을 한/글 결과와 맞춘다. 가로지르는 칸의 폭만 따로 본다.
    • 표 sz와 colCnt도 맞춘다.
  • 그 밖에 시험하는 것
    • 글을 복제하는 기본 동작.
    • 왼쪽 삽입에서의 합친 칸.
    • 문단 id가 문서 안에서 겹치지 않는지.
    • 칸 영역 이동.
    • 범위 밖 열, 표 안의 표, 잘못된 side의 거부.

tests/test_table_ops_cells_without_geometry.py

주소·크기가 없는 칸의 표에서 구조 편집 9가지가 거부로 끝나는지 본다. 고치기 전에는 9가지 모두 틀렸다.

tests/test_table_ops_delete_every_line.py

1열 표의 열, 모든 열, 1행 표의 행, 모든 행을 지우면 거부되고 문서가 그대로인지 본다. 하나만 남기는 삭제는 그대로 된다. 고치기 전에는 거부 4판이 모두 틀렸다.

🤖 Generated with Claude Code

airmang and others added 6 commits October 6, 2026 17:03
apply_table_ops could insert rows (insert_row_by_clone) but no columns.
insert_column_by_clone inserts count columns right of grid column
ref_col (left of it with side "left"), cloning it:

- each new column is as wide as ref_col, and the table grows by it;
- each new cell takes the format of the cell of the same row in
  ref_col, and its text unless blank is set, which leaves one empty
  paragraph as Hangul does;
- a cell merged across the new columns grows its colSpan and width,
  next to a merged cell ending (on the left, starting) at ref_col each
  new column gets an empty cell of its format and rows, and a cell
  merged down ref_col is cloned with its rows;
- cell zones move with their columns.

Eighteen Hangul-saved files: 3x3 tables made by Hangul and the same
tables after Hangul inserted columns (right, left, three, right of a
wider column, merged cells across, ending at and down the column,
formats per row, a table as wide as the text). With blank the inserted
tables match Hangul's cell for cell, but for the stored width of a cell
merged across the new column, which Hangul leaves as it was and draws
by the grid.

The row insertion shares its paragraph id helper (_fresh_ids).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tables written by other programs can hold hp:tc elements with their
paragraphs straight inside, without hp:subList, hp:cellAddr, hp:cellSpan
or hp:cellSz. Deleting a row or a column, inserting rows or columns,
setting or fitting column widths and splitting the table stopped on an
AssertionError there, and inserting a row or reordering rows refused it
for an unrelated reason. _guard_flat, which every one of these edits
calls first, now refuses such a table as it refuses one holding a table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
delete_column on the only column of a one-column table, or on every
column, stopped on a ZeroDivisionError sharing out the freed width
among no columns left. delete_row on every row left a table without
rows and refused it only for the grid it left. Hangul does not delete
the last row or column either; both now refuse and point to
delete_table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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