Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@

### 고침

- 쪽 수 추정이 줄보다 넓은 글자처럼 취급한 표·그림을 한컴오피스 2024의 한/글처럼 놓는다.
- 표 앞의 조판 부호(책갈피·누름틀·머리말·쪽 번호 위치 등)와, 문단 첫머리에 앵커된 위아래 배치 개체는 표의 줄에
선다. 전에는 그 앞에 빈 줄 하나를 따로 두어 뒤 문단이 그만큼 내려갔다.
- 글자로 번호를 찍는 조판 부호(번호 넣기)는 전처럼 표 위에 자기 빈 줄을 둔다.
- 표 뒤의 조판 부호는 전처럼 표 아래 빈 줄에 선다.
- 어울림 개체 옆 좁은 띠에 든 이런 개체는 띠보다 넓어도 띠 안 그 자리에 놓인다. 전에는 개체 아래 전체 폭 줄로
내렸다.
- 쪽 수 추정이 문단 위로 올려 놓은 도형(문단 기준 음수 `vertOffset`, 파일에는 부호 없는 32비트로 남는다)을 그만큼 위에
놓인 것으로 센다. 전에는 그 값을 큰 양수로 읽어 문단 줄이 40억 넘게 높아지고, 한 쪽짜리 문서가 세 쪽으로 나왔다.
한/글은 그 도형을 문단 위가 있었을 자리에서 그만큼 위에 놓고, 그 띠에 걸린 앞 줄(아래끝이 띠 위끝보다 아래인 줄)을
Expand Down
47 changes: 20 additions & 27 deletions src/hwpx/layout/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2053,10 +2053,10 @@ def _apart_from_wide(measure: _Measure, text: str, widths: list[float], sizes: l
metrics: tuple[tuple[int, int], ...], size: int, shape: _Shape,
place: int) -> tuple[tuple[tuple[int, int], ...], int | None]:
"""Hancom lays an object set as a character that is wider than its line on that line alone: the object
placed top and bottom anchored before it at the paragraph's start, or after it at the paragraph's end, stands
on an empty line of its own above or below it, as tall as the paragraph's characters (*size*). *text* holds
the paragraph's characters and objects set as characters, not the anchored one, whose place in it is
*place*. (*metrics* with that line, its index.)
placed top and bottom anchored before it at the paragraph's start stands on that line, and one after it at
the paragraph's end on an empty line of its own below it, as tall as the paragraph's characters (*size*).
*text* holds the paragraph's characters and objects set as characters, not the anchored one, whose place in
it is *place*. (*metrics* with that line, its index.)

Otherwise the anchor stands on the line holding the character before it (the first line when it comes
first), as Hancom keeps it at the end of that line when the next one starts after it: (*metrics*, that line).
Expand All @@ -2071,8 +2071,8 @@ def wide(index: int) -> bool:
return index in objects and objects[index][0] > widths[min(line, len(widths) - 1)]

empty = (size, _pitch(shape.kind, shape.value, size))
if place == 0 and wide(0): # the anchor first: its empty line above
return (empty, *metrics), 0
if place == 0 and wide(0): # the anchor first: on the object's line, no line of its own
return metrics, 0
if place == len(text) and wide(len(text) - 1): # the anchor last: its empty line below
return (*metrics, empty), len(metrics)
return metrics, max((number for number, start in enumerate(starts) if start < place), default=0)
Expand Down Expand Up @@ -2272,9 +2272,13 @@ def _paragraph(measure: _Measure, page: _Page, paragraph: Any, wrap: _Wrap | Non
pitch = _pitch(shape.kind, shape.value, size)
own, own_pitch = size, pitch # the paragraph's own line, as tall as its characters
count, size, pitch, table = _object_line(measure, objects[0], count, size, pitch, page.column_width, shape)
if controls is not None: # the controls before and after it on empty lines of their own, each as tall as
if controls is not None: # the controls after it on an empty line of their own, as tall as the largest
before, after = controls[1], controls[2] # characters of the runs holding them; those before it too
if not any(child.find(f"{HP}autoNum") is not None for run in before for child in run # when they draw
if _local(child) == "ctrl"): # a number, else they stand on the table's line
before = []
heights = [max(measure.char_height(run.get("charPrIDRef")) for run in side) if side else None
for side in controls[1:]] # the largest characters of the runs holding them
for side in (before, after)]
empty = [None if height is None else (height, _pitch(shape.kind, shape.value, height))
for height in heights]
cached = tuple(line for line in (empty[0], (size, pitch), empty[1]) if line is not None)
Expand Down Expand Up @@ -2341,8 +2345,8 @@ def _controls_beside(obj: Any, runs: list[Any], width: float) -> tuple[Any, list
"""(*obj*'s run, the runs holding controls before it, those holding controls after it) when *obj* is a table
set as a character wider than the line (*width*) and controls (``hp:ctrl``, a page number, a hidden page
number, a field, a bookmark, a header and so on -- but not a column definition) stand beside it in its
paragraph, else ``None``: Hancom sets the controls before it on an empty line of their own, the table on the
next line and the controls after it on the line after that."""
paragraph, else ``None``: Hancom sets the controls before it on the table's line (on an empty line of their
own above it when they show a number) and the controls after it on the line after that."""

if _local(obj) != "tbl" or obj.find(f"{HP}pos").get("treatAsChar") != "1" or _extent(obj, "width") <= width:
return None
Expand Down Expand Up @@ -2537,12 +2541,9 @@ def _wrapped_paragraph(measure: _Measure, page: _Page, paragraph: Any, wrap: _Wr
if wrap is not None and wrap.push: # objects set as characters on a line reaching it go below it with it
return _push(_paragraph(measure, page, paragraph), wrap, starts=False)
if wrap is not None and objects and not placed and not _run_text(runs).strip():
para = _paragraph(measure, page, paragraph) # one object set as a character, alone
if len(objects) > 1:
para = _paragraph(measure, page, paragraph) # one object set as a character, alone: beside it, even
if len(objects) > 1: # wider than the room
raise _Unsupported("objects set as characters beside a square-wrapped object")
if _object_extent(objects[0], measure)[0] > _room_beside(wrap, page.column_width - shape.left - shape.right):
pushed, after = _push(para, replace(wrap, push=True), starts=False) # below it, the whole width
return replace(pushed, wrap_stays=wrap.stays, wrap_free=wrap.stays), after
beside = sum(1 for line in range(para.lines) if para.span(0, line) < wrap.bottom)
return replace(para, wrap_lines=beside, wrap_stays=wrap.stays, wrap_free=wrap.stays), \
wrap.lower(para.span(0, para.lines) + para.next)
Expand Down Expand Up @@ -2750,9 +2751,9 @@ def _banded_object_lines(measure: _Measure, text: str, widths: list[float], size
end: int, head: int) -> tuple[tuple[tuple[int, int], ...], int]:
"""(height, advance) of each line of text holding objects set as characters (*objects*: place -> width and
height) beside a square-wrapped object's band, and how far the first line goes down. A line whose top is in
the band is as wide as the room beside the object, but the first one holding an object wider than that room
goes below the band, the whole width, the rest of the room left empty (its line before spaced that much
more). Every line taken as narrow at first, the lines are broken again until the narrow ones settle."""
the band is as wide as the room beside the object, one holding an object wider than that room too (the object
alone on it, reaching past the room). Every line taken as narrow at first, the lines are broken again until
the narrow ones settle."""

advances = {index: width for index, (width, _) in objects.items()} or None
mixed = sizes if len(set(sizes)) > 1 else None
Expand All @@ -2775,15 +2776,7 @@ def _banded_object_lines(measure: _Measure, text: str, widths: list[float], size
pitch = _pitch(shape.kind, shape.value, size)
advance = pitch if shape.kind == "FIXED" else height + pitch - size
if top < wrap.bottom and top + height > wrap.top:
room = widths[min(line, 1)] - wrap.cut
if any(objects[index][0] > room for index in span if index in objects): # below the band
if metrics:
metrics[-1] = (metrics[-1][0], metrics[-1][1] + wrap.bottom - top)
else:
lead = wrap.bottom - top
top = wrap.bottom
else:
found.add(line)
found.add(line)
metrics.append((height, advance))
top += advance
if found == narrow:
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27 changes: 15 additions & 12 deletions tests/test_layout_page_estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
"pages_empty_paragraph_object_overlap_20pt_2000": 1,
# An empty paragraph holding a table set as a character and a table placed top and bottom from its top (6000
# tall), the line 42520 wide: a character table wider than the line takes that line alone, the placed
# table's anchor going on an empty line of its own above it (anchor first) or below it (anchor last), the
# placed table at that line's top; one as wide as the line shares its line with the anchor.
# table's anchor going on that line (anchor first) or on an empty line of its own below it (anchor last),
# the placed table at that line's top; one as wide as the line shares its line with the anchor.
# Five pictures 20000 x 11000 set as characters, each in its own paragraph (one page bare), with an effect:
# Hancom lays each out as large as it and its glow (the radius on each side) or its outer shadow (moved its
# distance in its direction, spread by its radius), the line that much taller
Expand All @@ -68,7 +68,7 @@
"pages_para_shape_unsigned_offset_1500_h3000": 1,
"pages_wide_table_as_character_anchor_first": 1, # 44000 x 3000
"pages_wide_table_as_character_anchor_last": 1,
"pages_wide_table_as_character_tall_anchor_first": 2, # 44000 x 60000: on the next page below the empty line
"pages_wide_table_as_character_tall_anchor_first": 3, # 44000 x 60000: on the next page's top, the anchor on its line
"pages_wide_table_as_character_tall_anchor_last": 2, # the placed table's rows flowing on to the next page
"pages_line_wide_table_as_character_anchor_first": 1, # 42520 x 3000
"pages_line_wide_table_as_character_anchor_last": 1,
Expand Down Expand Up @@ -100,12 +100,12 @@
"pages_push_band_group_as_character_alone": 1, # the group and the polygon, no text
# A picture wrapped square from an empty paragraph's left, 41631 x 27112, leaving 6557 beside it; the
# next paragraph holds a table set as a character alone:
"pages_square_band_table_as_character_alone": 1, # 47058 wide: below the picture, the whole width
"pages_square_band_table_as_character_alone": 1, # 47058 wide: beside it all the same, reaching past the room
"pages_square_band_narrow_table_as_character_alone": 1, # 5000 wide: beside it, and the lines after
# ... or text whose lines in the room beside the picture are as wide as it, and an object set as a
# character among it: the first line holding one wider than the room goes below the picture, the whole
# width, the rest of the room left empty; one narrower stays beside it.
"pages_square_band_table_as_character_among_text": 2, # a table 47058 wide
# character among it: a line holding one stays beside the picture, one wider than the room alone on its
# line, reaching past it.
"pages_square_band_table_as_character_among_text": 1, # a table 47058 wide
"pages_square_band_narrow_table_as_character_among_text": 1, # 5000 wide
"pages_square_band_picture_as_character_among_text": 1, # a picture 47058 x 5000
# A picture 20000 x 27112 wrapped square in the middle of the column (10 pt text spaced 160%), flowing with
Expand Down Expand Up @@ -417,16 +417,17 @@
"pages_table_as_character_200_short_then_three_12pt_spaces": 1, # of 12 pt: the third's line 1200 tall
"pages_table_as_character_200_short_after_two_spaces": 1, # two before it: the table on the next line
# A table set as a character (a row 1282 tall) 1000 wider than the column (42520), alone in its paragraph
# but for a page-number control: the two never share a line, the control's an empty line of the text.
"pages_page_number_then_a_table_wider_than_the_line": 1, # the control first: an empty line 1600 down,
# then the table's
# but for a page-number control: a control before it stands on the table's line, one after it on an empty
# line of the text under it.
"pages_page_number_then_a_table_wider_than_the_line": 1, # the control first: the table's line alone
"pages_table_wider_than_the_line_then_a_page_number": 1, # the table first: the control's line under it
"pages_page_number_then_a_table_that_fits": 1, # 1000 narrower than the column: one line
"pages_table_wider_than_the_line_alone": 1, # no control: one line
"pages_section_start_then_a_table_wider_than_the_line": 1, # the section's and columns' controls before
# it: one line
# The same table beside one of the other controls: the same two lines, whichever the control. Before the
# table, an empty line 1600 down and the table's line under it; after it, the table's line first.
# The same table beside one of the other controls: the same lines, whichever the control. Before the
# table, on its line; after it, on an empty line under it -- but a number shown as a character before it
# takes an empty line 1600 down of its own, the table's line under it.
"pages_page_hiding_then_a_table_wider_than_the_line": 1, # a hidden page number
"pages_table_wider_than_the_line_then_a_page_hiding": 1,
"pages_auto_number_then_a_table_wider_than_the_line": 1, # a page's number shown as a character
Expand All @@ -445,6 +446,8 @@
"pages_table_wider_than_the_line_then_a_click_here_field": 1,
"pages_index_mark_then_a_table_wider_than_the_line": 1, # an index mark
"pages_table_wider_than_the_line_then_an_index_mark": 1,
"pages_bookmark_around_a_table_wider_than_the_line": 1, # before and after it: the table's line, the empty
"pages_click_here_field_around_a_table_wider_than_the_line": 1, # line under it
"pages_picture_as_character_300_short_then_three_spaces": 1, # a picture 3000 tall: the same
"pages_table_cell_table_as_character_300_short_then_three_spaces": 1, # in a cell of a flowing table
"pages_table_as_character_then_a_line_break": 1, # a line break after it: an empty line below it
Expand Down
Loading