diff --git a/CHANGELOG.md b/CHANGELOG.md index 071b578b..bc7aaa07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,9 @@ - FormFit과 쪽 수 추정이 한양 글꼴(한양신명조·한양중고딕·한양견고딕)의 굵은 글자를 em의 1/20만큼 넓게 잡는다. 한/글은 그 폭을 더한 뒤 배치 단위로 반올림한다(14pt "가"가 1,400이 아니라 1,472). 전에는 굵은 글자도 보통 폭으로 셌다. 다른 글꼴의 굵은 글자는 전처럼 보통 폭이다. `TextStyle.bold`가 글자 모양의 굵게를 담는다. +- 쪽 수 추정(실험, `estimate_pages`)이 run의 `hp:switch` 안에 든 개체(차트와 그것을 대신하는 OLE 개체 등)가 있는 구역을 + 지원 밖으로 둔다(`an object in a switch (hp:switch)`). 한/글은 그런 개체도 자리를 차지해 곁의 줄을 좁히는데, 전에는 + 개체가 없는 것처럼 추정했다. - FormFit과 쪽 수 추정에서 `!`의 반 em을 공백처럼 배치 단위(1/1800인치)로 내림한다. 9·11·13pt처럼 글자 크기의 4분의 1이 홀수인 크기에서 `!`가 4 넓게 잡혀, 그 뒤 탭 폭이 한/글보다 4 좁았다. - FormFit과 쪽 수 추정이 공백 뒤의 닫는 문장부호(`!`·`?`·`)`·`%`·`」`·`·` 등 줄 머리에 오지 않는 문자 모두)로 줄을 diff --git a/src/hwpx/layout/pages.py b/src/hwpx/layout/pages.py index 6e889721..7a74b26c 100644 --- a/src/hwpx/layout/pages.py +++ b/src/hwpx/layout/pages.py @@ -1592,6 +1592,12 @@ def _check_section(section: Any) -> None: # Hancom starts a new section, on a new page, at a later paragraph holding section settings. if any(next(paragraph.iter(f"{HP}secPr"), None) is not None for paragraph in section.findall(f"{HP}p")[1:]): raise _Unsupported("section settings (hp:secPr) after the first paragraph") + # An object in a run's hp:switch (a chart, or the OLE object standing for it) takes its room as any other + # does, but the estimate looks for objects among the run's own children only. + for switch in section.iter(f"{HP}switch"): + if _local(switch.getparent()) == "run" and any( + _local(child) in _OBJECTS for branch in switch for child in branch): + raise _Unsupported("an object in a switch (hp:switch)") def _objects_reach(runs: list[Any], measure: _Measure) -> int: diff --git a/tests/test_layout_page_estimate.py b/tests/test_layout_page_estimate.py index 0bd6e187..56b5b928 100644 --- a/tests/test_layout_page_estimate.py +++ b/tests/test_layout_page_estimate.py @@ -1267,6 +1267,27 @@ def test_a_page_margin_written_with_a_minus_sign_takes_the_new_page_margin(side: assert estimate.pages == 2 +def test_an_object_in_a_switch_is_not_followed() -> None: + # A chart in a run's hp:switch (with the OLE object standing for it in hp:default), wrapped square: Hancom + # lays the following lines out beside it, but the estimate looks for objects among a run's own children. + document = HwpxDocument.new() + document.add_paragraph("앞 문단") + paragraph = document.add_paragraph("곁 문단") + run = paragraph.element.find(f"{HP}run") + switch = etree.SubElement(run, f"{HP}switch") + for branch, name in (("case", "chart"), ("default", "ole")): + holder = etree.SubElement(switch, f"{HP}{branch}") + obj = etree.SubElement(holder, f"{HP}{name}", {"textWrap": "SQUARE"}) + etree.SubElement(obj, f"{HP}sz", {"width": "32250", "height": "18000"}) + etree.SubElement(obj, f"{HP}pos", {"treatAsChar": "0", "vertRelTo": "PARA", "horzRelTo": "COLUMN"}) + + document.oxml.sections[0].mark_dirty() + assert estimate_pages(document.to_bytes()).unsupported == ("section 0: an object in a switch (hp:switch)",) + run.remove(switch) + document.oxml.sections[0].mark_dirty() + assert estimate_pages(document.to_bytes()).unsupported == () + + def test_page_margins_leaving_no_body_are_unsupported() -> None: data = (FIXTURES / "pages_margin_negative_header_one_page.hwpx").read_bytes() out = io.BytesIO()