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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@

- FormFit과 쪽 수 추정에서 `!`의 반 em을 공백처럼 배치 단위(1/1800인치)로 내림한다. 9·11·13pt처럼 글자 크기의 4분의 1이
홀수인 크기에서 `!`가 4 넓게 잡혀, 그 뒤 탭 폭이 한/글보다 4 좁았다.
- FormFit과 쪽 수 추정이 공백 뒤의 닫는 문장부호(`!`·`?`·`)`·`%`·`」`·`·` 등 줄 머리에 오지 않는 문자 모두)로 줄을
시작한다. 한/글이 그렇게 놓는다. 전에는 말줄임표(`…`)만 그랬고, 다른 문자는 공백 앞 낱말까지 다음 줄로 넘겨 한 줄에
낱말 하나가 덜 들어갔다. 앞 글자에 붙은 문장부호는 전처럼 그 글자와 함께 다음 줄로 간다.
- `.hwp`를 HWPX로 읽을 때 변경 추적 설정(`hh:trackchageConfig@flags`)을 설정 레코드의 첫 값 그대로 쓴다. 한컴오피스 2024가
쓰는 값이다(대개 56). 전에는 레코드가 있으면 늘 4를 썼다. `.hwp`로 쓸 때도 HWPX의 flags를 그 자리에 쓴다(없으면 56).
- 메모 필드(`notes.add_memo`)와 `.hwp`에서 읽은 필드의 `hp:fieldBegin`에 한/글처럼 `metaTag=""`를 단다(메일 머지
Expand Down
12 changes: 6 additions & 6 deletions src/hwpx/form_fit/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
_EMOJI_BMP = frozenset("✅❌❎❗❓❔❕➕➖➗⏰⏳⛔✋✨⭐⬛⬜")
#: The variation selectors and the zero-width joiner: no advance.
_ZERO_WIDTH = frozenset("\ufe0e\ufe0f\u200d")
#: Closing punctuation that never starts a line; it moves down with the
#: character before it; but the ellipsis starts a line after a space.
#: Closing punctuation that never starts a line right after a character: it
#: moves down with that character. After a space it may start a line.
_NO_LINE_START = frozenset("!%),.:;?]}¢°’”‰′″℃〉》」』】〕…·、。")
#: Opening punctuation that never ends a line.
_NO_LINE_END = frozenset("([{‘“〈《「『【〔")
Expand Down Expand Up @@ -1608,9 +1608,9 @@ def hancom_line_starts(
do. Once the line's text reaches the margin (its 자간 included), its spaces
hang and the next word starts the next line however far the spaces could
shrink: only the word that crosses the margin shrinks them. The line then
ends at the last break opportunity that fits — never before a closing or
after an opening punctuation mark — or mid-word when no opportunity is
left. *sizes*, when given, holds each character's size in pt (runs of
ends at the last break opportunity that fits — never before a closing
punctuation mark attached to the character before it, nor after an opening
one — or mid-word when no opportunity is left. *sizes*, when given, holds each character's size in pt (runs of
several sizes): every character, and every space that shrinks, then
takes its own size.
*styles*, when given, holds each character's own style (runs of several
Expand Down Expand Up @@ -1678,7 +1678,7 @@ def hancom_line_starts(
options = [
index for index in breaks
if start < index <= end
and (text[index] not in _NO_LINE_START or text[index] == "…" and text[index - 1] in _HANGING_SPACES)
and (text[index] not in _NO_LINE_START or text[index - 1] in _HANGING_SPACES)
and text[index - 1] not in _NO_LINE_END
]
start = max(options) if options else end
Expand Down
Binary file not shown.
Binary file not shown.
24 changes: 24 additions & 0 deletions tests/test_layout_page_estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
# Tables of short sentences, one cell holding "… " among its words: Hancom starts a line with the ellipsis after
# a space (attached to the character before it, it keeps to that one).
"pages_ellipsis_starts_a_cell_line": 4,
# Paragraphs of sentences among which " 」" or " %" stands after a space: Hancom starts a line with the mark
# there, as with any closing punctuation after a space (attached to a character, it moves down with it).
"pages_closing_bracket_after_a_space_starts_a_line": 2,
"pages_percent_after_a_space_starts_a_line": 2,
"pages_no_break_spaces": 2, # rows of "가나" and a no-break space: each is half an em and keeps the row
# one word
"pages_typed_ideographic_spaces": 5, # cells of "가나다라마" and a U+3000 typed in the text, a full em
Expand Down Expand Up @@ -1079,6 +1083,26 @@ def test_without_line_caches_formfit_breaks_the_lines_the_same(name: str) -> Non
_assert_like_hancom(estimate_pages(_without_caches(data)), data, HANCOM_PAGES[name])


@pytest.mark.parametrize("name", ["pages_closing_bracket_after_a_space_starts_a_line",
"pages_percent_after_a_space_starts_a_line"])
def test_a_closing_mark_after_a_space_may_start_a_line(name: str) -> None:
# The line count alone does not show where a line starts: each paragraph's lines start where Hancom's do.
root = HwpxDocument.open(io.BytesIO((FIXTURES / f"{name}.hwpx").read_bytes()))._root
measure = page_layout._Measure(root)
page = page_layout._page(root.sections[0].element)

for paragraph in root.sections[0].element.findall(f"{HP}p")[1:]:
runs = paragraph.findall(f"{HP}run")
size, refs, _ = page_layout._text_size(measure, runs)
style = measure.style(paragraph.get("paraPrIDRef"), refs, paragraph)
shape = measure.shape(paragraph.get("paraPrIDRef"))
widths = page_layout._indented(page.column_width - shape.left - shape.right, shape, style)

starts = measure.line_starts(page_layout._run_text(runs), widths, size, style)

assert starts == [int(segment.get("textpos")) for segment in paragraph.findall(f"{HP}linesegarray/{HP}lineseg")]


@pytest.mark.parametrize(
"name",
[
Expand Down
Loading