diff --git a/CHANGELOG.md b/CHANGELOG.md index 45347ef9..46fdb47d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,8 @@ - FormFit과 쪽 수 추정이 텍스트 내보내기가 쓰지 않는 번호 형식(`KOREAN_DIGIT`, `HANGUL` 등)의 문단 번호를 만나도 멈추지 않는다. 한/글이 재는 번호(`useInstWidth`)이면 그 숫자를 한글 한 글자 폭으로 잰다. 전에는 번호가 자기 글자 모양을 가진 문단에서 `KeyError`가 났다. +- 쪽 수 추정이 `hp:colPr@sameSz`와 각주 `hp:placement@beneathText`의 `true`를 `1`과 같이 읽는다(xs:boolean). 전에는 + `sameSz="true"`인 같은 폭 다단을 폭이 다른 다단으로 보고 추정하지 않았다. - `.hwp`를 열 때 그림의 투명도와 반전을 한/글이 읽는 자리에서 읽는다. 한/글은 그림 레코드의 투명도를 그림 원래 크기 뒤 마지막 바이트에서 읽고, 바이너리 항목 번호 뒤 바이트는 읽지 않는다(저장할 때 0으로 쓴다). 전에는 그 바이트를 투명도로 옮겨, 거기에 255가 든 문서의 그림이 HWPX에서 보이지 않았다. 반전(`hp:pic@reverse`)은 그림 개체 속성의 diff --git a/src/hwpx/layout/pages.py b/src/hwpx/layout/pages.py index e31c6cf0..5b653a13 100644 --- a/src/hwpx/layout/pages.py +++ b/src/hwpx/layout/pages.py @@ -1427,13 +1427,19 @@ def _first_columns(section: Any) -> Any: return next((cols for cols in section.iter(f"{HP}colPr") if not _in_sub_list(cols)), None) +def _true(value: Any) -> bool: + """Whether an ``xs:boolean`` attribute *value* is true: ``1`` or ``true`` (Hancom writes ``1``).""" + + return value in ("1", "true") + + def _column_layout(settings: Any, text_width: int) -> tuple[int, int, tuple[int, ...], int]: """:func:`_columns` of the column settings *settings* (``hp:colPr``; ``None``: one column).""" count = int(settings.get("colCount", "1")) if settings is not None else 1 if count <= 1: return 1, text_width, (), 0 - if settings.get("sameSz") != "1": # each column takes its share of 32768 of the text width (hp:colSz), + if not _true(settings.get("sameSz")): # each column takes its share of 32768 of the text width (hp:colSz), sizes = settings.findall(f"{HP}colSz") # rounded, whatever the shares add up to total = sum(int(size.get("width", 0)) + int(size.get("gap", 0)) for size in sizes) if len(sizes) != count or total <= 0: @@ -1502,7 +1508,7 @@ def _note_shape(section: Any) -> _NoteShape: if settings is None: return _NoteShape() placement = settings.find(f"{HP}placement") - if placement is not None and placement.get("beneathText") == "1": + if placement is not None and _true(placement.get("beneathText")): raise _Unsupported("footnotes beneath the text") spacing = settings.find(f"{HP}noteSpacing") rule = settings.find(f"{HP}noteLine") diff --git a/tests/test_layout_page_estimate.py b/tests/test_layout_page_estimate.py index 7a5707d2..ce913db3 100644 --- a/tests/test_layout_page_estimate.py +++ b/tests/test_layout_page_estimate.py @@ -1606,6 +1606,27 @@ def test_the_multi_column_sample_breaks_its_lines_at_each_column_width_without_c _assert_like_hancom(estimate_pages(_without_caches(data)), data, 2) +def test_columns_of_the_same_size_written_as_true_are_of_the_same_size() -> None: + # hp:colPr@sameSz is an xs:boolean: "true" is "1" (Hancom writes "1"; other writers "true"). + document = HwpxDocument.new() + document.page.set_columns(2) + for number in range(80): + document.add_paragraph(f"{number} 단 나누기 문단입니다") + data = document.to_bytes() + out = io.BytesIO() + with zipfile.ZipFile(io.BytesIO(data)) as source, zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as target: + for info in source.infolist(): + payload = source.read(info.filename) + if info.filename.startswith("Contents/section"): + payload = re.sub(rb'(colCount="2"[^>]*)sameSz="1"', rb'\1sameSz="true"', payload) + target.writestr(info, payload) + assert b'sameSz="true"' in zipfile.ZipFile(out).read("Contents/section0.xml") + + ones, trues = estimate_pages(data), estimate_pages(out.getvalue()) + + assert (trues.unsupported, trues.pages, trues.lines) == ((), ones.pages, ones.lines) + + def test_a_footnote_in_a_paragraph_without_a_cache_in_columns_of_unequal_width_is_not_followed() -> None: document = HwpxDocument.new() document.set_columns(2, same_size=False, column_widths=[(10632, 873), (21263, 0)])