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 @@ -126,6 +126,9 @@
한/글은 그 도형을 문단 위가 있었을 자리에서 그만큼 위에 놓고, 그 띠에 걸린 앞 줄(아래끝이 띠 위끝보다 아래인 줄)을
띠 아래끝으로 내린 뒤 그다음 줄을 이어서 놓는다. 도형의 문단 줄은 그 뒤에 온다. 추정도 이제 그렇게 놓는다(옮긴 줄이
쪽 끝을 넘거나 그 사이에 표·개체·각주가 있으면 지원 밖).
- FormFit과 쪽 수 추정이 표에 없는 글꼴 이름(설치되지 않은 글꼴 등)을 한컴바탕의 폭으로 잰다. 한/글은 찾지 못한 글꼴을
한컴바탕으로 배치한다. 전에는 글자 종류의 평균 폭을 썼다. 글꼴 이름이 없으면 전처럼 평균 폭이다.
- 한컴바탕의 `※`는 788, `…`는 983(1024 단위)으로 한/글이 배치하는 폭이다.
- 쪽 수 추정이 빼기 부호로 쓴 개체 오프셋(`vertOffset="-1500"`처럼, 한/글이 쓰는 부호 없는 32비트 꼴이 아닌 것)을 한/글처럼
0으로 읽는다. 한/글은 그런 값을 0으로 읽어 개체를 문단(또는 종이) 기준 자리에 그대로 그리고, 저장할 때 0으로 고쳐 쓴다.
전에는 그만큼 위(왼쪽)로 놓인 것으로 읽어 뒤 줄 자리가 한/글과 달랐다.
Expand Down
22 changes: 16 additions & 6 deletions src/hwpx/form_fit/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,15 @@
# is half the em, rounded down, at the 장평, rounded half up. Bold text keeps the
# regular advances. A glyph a face lacks takes the advance of the face Hancom
# lays it out from (``_glyph_table.FALLBACK``), moved into the face's own units
# and rounded. A face or glyph listed nowhere falls back to the class averages,
# unrounded.
# and rounded. A face no table lists takes 한컴바탕's advances, as Hancom lays out a
# face it does not find; a glyph listed nowhere falls back to the class averages,
# unrounded, and so does every glyph when no face is named.
_LAYOUT_UNIT = 4
#: Hancom's default tab stops, this far apart (HWPUNIT) from the column's left edge (the section's
#: ``tabStopVal``).
_DEFAULT_TAB_STOP = 4000
#: The face Hancom lays out a face it does not find with.
_UNLISTED_FACE = "한컴바탕"

_HP = "{http://www.hancom.co.kr/hwpml/2011/paragraph}"
_HH = "{http://www.hancom.co.kr/hwpml/2011/head}"
Expand Down Expand Up @@ -526,8 +529,8 @@
768, 682, 682, 725, 682, 640, 725, 768, 384, 469, 768, 640, 938, 768, 725, 640,
725, 682, 640, 768, 768, 725, 981, 682, 682, 640, 512, 341, 512, 1024, 512, 597,
512, 554, 512, 554, 554, 384, 554, 554, 298, 298, 554, 298, 853, 554, 554, 554,
554, 426, 512, 384, 554, 554, 810, 597, 597, 469, 597, 597, 597, 810, 341, 850,
379, 379, 276, 276, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 780, 1024, 1024, 1024,
554, 426, 512, 384, 554, 554, 810, 597, 597, 469, 597, 597, 597, 810, 341, 983,
379, 379, 276, 276, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 788, 1024, 1024, 1024,
1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 831, 831, 831, 447,
1024, 1024, 881, 441, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
Expand Down Expand Up @@ -1094,6 +1097,13 @@ def _design_units(face: str, ch: str | None) -> tuple[int, int] | None:
return None


def _laid_out_face(face: str) -> str:
"""The face whose advances *face* takes: itself when a table lists it (or none is named), else
``_UNLISTED_FACE``."""

return face if not face or face in _DESIGN or face in UNITS_PER_EM else _UNLISTED_FACE


@lru_cache(maxsize=8192)
def _hangul_units(face: str, ch: str) -> tuple[int, int] | None:
"""``(advance, units per em)`` of the Hangul syllable *ch* in *face*: its own design advance where the
Expand Down Expand Up @@ -1415,11 +1425,11 @@ def char_advance(ch: str, font_pt: float, style: TextStyle | None = None) -> flo
if ch in _HALF_EM_SPACES and not style.use_font_space:
return _laid_out_space(height, ratio, spacing)
if cls == "hangul":
design, base = _hangul_units(style.hangul_face, ch), style.hangul_advance
design, base = _hangul_units(_laid_out_face(style.hangul_face), ch), style.hangul_advance
elif ch in _HALF_EM_GLYPHS:
design, base = (1, 2), _ADVANCE_EM[cls]
else:
design, base = _design_units(style.glyph_face, ch), _ADVANCE_EM[cls]
design, base = _design_units(_laid_out_face(style.glyph_face), ch), _ADVANCE_EM[cls]
if design is not None:
return _laid_out(design[0], design[1], height, ratio, spacing)
return base * font_pt * 100.0 * ratio / 100.0 * (1 + spacing / 100.0)
Expand Down
Binary file not shown.
23 changes: 23 additions & 0 deletions tests/test_form_fit_hancom_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
KOPUB_FACES_CELLS = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_kopub_faces_cells.hwpx"
HFT_FACES_CELLS = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_hft_faces_cells.hwpx"
DODUM_PUNCTUATION = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_dodum_punctuation_cells.hwpx"
UNLISTED_FACES_CELLS = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_unlisted_faces_cells.hwpx"


NO_BREAK_SPACES = Path(__file__).parent / "fixtures" / "hancom_saved" / "pages_no_break_spaces.hwpx"
FIXED_WIDTH_SPACES = Path(__file__).parent / "fixtures" / "hancom_saved" / "pages_fixed_width_spaces.hwpx"
SCRIPT_ROWS = [
Expand Down Expand Up @@ -288,6 +291,26 @@ def test_kopub_cells_break_where_hancom_breaks_them() -> None:
for weight in ("Light", "Medium", "Bold")}


def test_cells_in_faces_no_table_lists_break_as_hancom_batang() -> None:
# The same in faces Hancom does not find (없는글꼴이름, Cre고딕, 가짜명조체), for A, a, 0, (, %, 가, ※ and W at
# 10 and 30 pt: Hancom lays them out as 한컴바탕, ※ at 788 of 1024 units.
doc = HwpxDocument.open(UNLISTED_FACES_CELLS)
cells = list(doc.oxml.sections[0].element.iter(f"{HP}tc"))
faces = set()
for cell in cells:
paragraph = cell.find(f"{HP}subList/{HP}p")
text = "".join(t.text or "" for t in paragraph.iter(f"{HP}t"))
ref = paragraph.find(f"{HP}run").get("charPrIDRef")
style = text_style_from_refs(doc.oxml, paragraph.get("paraPrIDRef"), [ref])
points = int(doc.oxml.char_property(ref).attributes["height"]) / 100
margins = cell.find(f"{HP}cellMargin")
width = int(cell.find(f"{HP}cellSz").get("width")) - int(margins.get("left")) - int(margins.get("right"))
hancom = len(paragraph.findall(f"{HP}linesegarray/{HP}lineseg"))
faces.add(style.glyph_face)
assert estimate_lines(text, width, points, style) == hancom, (style.glyph_face, f"U+{ord(text[0]):04X}", points)
assert len(cells) == 96 and faces == {"없는글꼴이름", "Cre고딕", "가짜명조체"}


def test_faces_in_hancoms_own_folder_take_the_design_advances_of_their_fonts() -> None:
# The faces 한/글 keeps in its own folder are measured from their fonts too: a Hangul syllable takes 900 of
# 1000 units in 한컴 윤고딕 230, 850 in 한컴 백제 M, the full em in 휴먼고딕 (512 units) and HY울릉도M (1024)
Expand Down
Loading