From 0aaa4a29e51fab97de9fa5b6bdbf2c0c6631d7a5 Mon Sep 17 00:00:00 2001 From: airmang <38392618+airmang@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:59:37 +0900 Subject: [PATCH] feat(form_fit): lay out eight more Hancom faces at their glyph advances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FormFit and the page estimate fell back to class averages for every glyph that is not Hangul in HCI Poppy, HCI Hollyhock, 한양중고딕, 한양신명조, 한양견고딕, 휴먼옛체, HY견명조 and HY동녘M. They now take each glyph's advance: - The faces Hancom ships in its own font format take the advances Hancom lays them out at, read from Hancom's line breaks of synthetic documents at several sizes, in the units per em that reproduce every size. - HY견명조 and HY동녘M take their fonts' design advances, as the other HY faces do; a glyph they lack and the grave accent take 함초롬바탕's. A Hancom-saved fixture holds each face's glyphs at the two widths where the line count changes, at 10 pt and 30 pt; FormFit now matches every cell. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 4 + src/hwpx/form_fit/_glyph_table.py | 50 +++++++ src/hwpx/form_fit/measure.py | 124 +++++++++++++++++- .../hancom_saved/formfit_hft_faces_cells.hwpx | Bin 0 -> 41995 bytes tests/test_form_fit_hancom_rules.py | 22 ++++ 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/hancom_saved/formfit_hft_faces_cells.hwpx diff --git a/CHANGELOG.md b/CHANGELOG.md index 4717cd3f..e6be3977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ - `soft_edge`(HWPUNIT)는 그림 가장자리를 그만큼 안쪽부터 흐리게 하고, `reflection=PictureReflection(...)`은 그림 아래에 반사를 그린다(크기: 그림 높이의 0 초과 1 이하, 거리, 시작·끝 투명도 0~1, 흐림). 부드러운 가장자리는 자리를 차지하지 않고, 반사는 대략 크기만큼의 그림 높이와 거리만큼 아래로 자리를 차지한다. +- FormFit과 쪽 수 추정이 한컴 글꼴 8종의 글자 폭을 안다: HCI Poppy·HCI Hollyhock·한양중고딕·한양신명조·한양견고딕· + 휴먼옛체(한/글이 배치하는 폭)와 HY견명조·HY동녘M(글꼴의 설계 폭, 글꼴에 없는 글자와 `` ` ``는 함초롬바탕의 폭). + 전에는 한글 아닌 글자에 글자 종류의 평균 폭을 썼다. 한글 음절은 모두 한 em이다. 이 글꼴로 쓴 칸의 채우기 결과와, + 줄 배치 정보가 없는 문서의 쪽 수가 달라질 수 있다. - FormFit과 쪽 수 추정이 KoPub 글꼴 12종도 글꼴의 설계 폭으로 잰다: KoPub돋움체·KoPub바탕체·KoPubWorld돋움체· KoPubWorld바탕체의 Light·Medium·Bold. 전에는 글자 종류의 평균 폭을 썼다. 한글 음절은 모두 같은 폭이고(돋움체 872/1000, 바탕체 936/1000), 다른 글자도 글꼴의 폭을 쓴다. 이 글꼴로 쓴 칸의 채우기 결과와, 줄 배치 정보가 없는 diff --git a/src/hwpx/form_fit/_glyph_table.py b/src/hwpx/form_fit/_glyph_table.py index 5536f783..54eba8f4 100644 --- a/src/hwpx/form_fit/_glyph_table.py +++ b/src/hwpx/form_fit/_glyph_table.py @@ -68,6 +68,14 @@ "HY헤드라인M": 1024, "Wingdings": 2048, "Symbol": 2048, + "HY견명조": 1024, + "HY동녘M": 1024, + "HCI Poppy": 1024, + "HCI Hollyhock": 1024, + "한양중고딕": 1000, + "한양신명조": 1000, + "한양견고딕": 4096, + "휴먼옛체": 1024, } #: The face Hancom takes a glyph from when a face does not have it. @@ -92,6 +100,8 @@ "HY신명조": "함초롬바탕", "새굴림": "함초롬돋움", "한컴 말랑말랑 Regular": "함초롬바탕", + "HY견명조": "함초롬바탕", + "HY동녘M": "함초롬바탕", } #: Each text face's ascent and descent (OS/2 usWinAscent, usWinDescent) in its units per em: a paragraph @@ -137,6 +147,8 @@ "MD개성체": (879, 145), "휴먼명조": (440, 100), "HY헤드라인M": (879, 145), + "HY견명조": (879, 145), + "HY동녘M": (879, 145), } #: Per face, the Hangul syllables whose design advance differs from the face's other syllables @@ -4605,4 +4617,42 @@ 1064: "Ш", 1105: "Ю", }, + "HY견명조": { + 512: ( + "\u00a0¢£¥¦©«¬®¯µ»ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöù" + "úûüýÿ" + ), + 1024: ( + "¡¤§¨ª\u00ad²³´¶¸¹º¼½¾¿ÆÐØÞßæðøþđĦħıIJijĸĿŀŁłʼnŊŋŒœŦŧˇː˘˙˚˛˝ΑΒΓΔΕΖΗΘΙ" + "ΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωЁАБВГДЕЖЗИЙКЛМНОПРСТУ" + "ФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё―†‡‥′″⁴ⁿ₁₂₃₄℉ℓ№" + "℡™ΩÅ⅓⅔⅛⅜⅝⅞↔↕↖↗↘↙⇒⇔∀∂∃∇∈∋∏∑√∝∞∠∥∧∨∩∪∫∬∮∴∵∼∽≒≠≡≤≥≪≫⊂⊃⊆⊇⊙⊥⌒⒜⒝⒞⒟" + "⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ─━│┃┌┍┎┏┐┑┒┓" + "└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋▒▣▤▥" + "▦▧▨▩▶▷▼▽◀◁◈◎◐◑☎☏☜☞♀♂♠♡♣♤♥♧♨♩♪♬♭\u3000、。〃【】〓〔〕㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓" + "㈔㈕㈖㈗㈘㈙㈚㈛㈜㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉿㎀㎁㎂㎃㎄㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘" + "㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏏㏐㏓㏖㏘㏛㏜㏝!"" + "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^" + "_`abcdefghijklmnopqrstuvwxyz{|}~¢£¬ ̄¥₩" + ), + }, + "HY동녘M": { + 512: ( + "\u00a0¢£¥¦©«¬®¯µ»ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöù" + "úûüýÿ" + ), + 1024: ( + "¡¤§¨ª\u00ad²³´¶¸¹º¼½¾¿ÆÐØÞßæðøþđĦħıIJijĸĿŀŁłʼnŊŋŒœŦŧˇː˘˙˚˛˝ΑΒΓΔΕΖΗΘΙ" + "ΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωЁАБВГДЕЖЗИЙКЛМНОПРСТУ" + "ФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё―†‡‥′″⁴ⁿ₁₂₃₄℉ℓ№" + "℡™ΩÅ⅓⅔⅛⅜⅝⅞↔↕↖↗↘↙⇒⇔∀∂∃∇∈∋∏∑√∝∞∠∥∧∨∩∪∫∬∮∴∵∼∽≒≠≡≤≥≪≫⊂⊃⊆⊇⊙⊥⌒⒜⒝⒞⒟" + "⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ─━│┃┌┍┎┏┐┑┒┓" + "└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋▒▣▤▥" + "▦▧▨▩▶▷▼▽◀◁◈◎◐◑☎☏☜☞♀♂♠♡♣♤♥♧♨♩♪♬♭\u3000、。〃【】〓〔〕㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓" + "㈔㈕㈖㈗㈘㈙㈚㈛㈜㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉿㎀㎁㎂㎃㎄㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘" + "㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏏㏐㏓㏖㏘㏛㏜㏝!"" + "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^" + "_`abcdefghijklmnopqrstuvwxyz{|}~¢£¬ ̄¥₩" + ), + }, } diff --git a/src/hwpx/form_fit/measure.py b/src/hwpx/form_fit/measure.py index 7074520b..b5e5d558 100644 --- a/src/hwpx/form_fit/measure.py +++ b/src/hwpx/form_fit/measure.py @@ -149,7 +149,9 @@ #: Design advances per face in font units: units per em, a Hangul syllable, the #: space, and each glyph of ``_GLYPHS`` (0: not in the face, or one Hancom lays -#: out from the fallback face all the same). +#: out from the fallback face all the same). The faces Hancom ships in its own +#: font format (HCI Poppy, HCI Hollyhock, 한양중고딕, 한양신명조, 한양견고딕, +#: 휴먼옛체) are given at the advances Hancom lays them out at. _DESIGN: dict[str, tuple[int, int, int, tuple[int, ...]]] = { "함초롬바탕": (1000, 970, 300, ( 320, 320, 610, 610, 830, 724, 320, 320, 320, 550, 550, 320, 550, 320, 550, 550, @@ -931,6 +933,126 @@ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, )), + "HY견명조": (1024, 1024, 341, ( + 682, 597, 768, 768, 981, 981, 426, 469, 469, 682, 725, 384, 725, 384, 512, 682, + 682, 682, 682, 682, 682, 682, 682, 682, 682, 426, 426, 640, 725, 640, 682, 981, + 853, 810, 810, 853, 768, 725, 853, 938, 469, 554, 938, 768, 981, 896, 896, 768, + 896, 810, 725, 896, 853, 810, 1024, 853, 810, 725, 469, 512, 469, 597, 512, 0, + 597, 640, 597, 640, 640, 469, 640, 725, 426, 384, 725, 426, 981, 682, 640, 682, + 682, 512, 554, 469, 640, 640, 896, 682, 640, 554, 426, 341, 426, 768, 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, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 0, 0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, 0, 0, 0, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, + 0, 0, 0, 0, + )), + "HY동녘M": (1024, 1024, 341, ( + 363, 331, 823, 648, 917, 782, 191, 332, 332, 519, 846, 311, 846, 311, 356, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 311, 311, 703, 846, 703, 585, 1023, + 654, 676, 686, 739, 623, 573, 705, 721, 309, 440, 688, 558, 901, 729, 732, 670, + 728, 703, 677, 532, 687, 656, 945, 654, 633, 618, 351, 356, 351, 591, 512, 0, + 614, 606, 565, 608, 611, 409, 604, 610, 282, 285, 580, 282, 911, 608, 608, 590, + 590, 383, 564, 388, 601, 601, 759, 537, 537, 477, 505, 505, 505, 846, 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, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 0, 0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, 0, 0, 0, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, + 0, 0, 0, 0, + )), + "HCI Poppy": (1024, 1024, 512, ( + 512, 378, 620, 510, 860, 796, 284, 340, 340, 398, 620, 254, 340, 254, 620, 510, + 510, 510, 510, 510, 510, 510, 510, 510, 510, 254, 254, 620, 620, 620, 454, 764, + 796, 624, 724, 792, 624, 568, 780, 850, 344, 340, 744, 624, 968, 850, 804, 619, + 804, 684, 538, 628, 796, 740, 1022, 684, 684, 684, 340, 620, 340, 620, 510, 284, + 510, 566, 454, 624, 491, 340, 568, 596, 298, 238, 568, 298, 904, 596, 560, 614, + 574, 404, 434, 332, 619, 578, 852, 528, 568, 510, 340, 620, 340, 620, 394, 983, + 510, 510, 284, 284, 512, 512, 512, 512, 512, 512, 512, 512, 788, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 1024, 630, 638, 632, 418, + 993, 1012, 896, 640, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, + )), + "HCI Hollyhock": (1024, 1024, 512, ( + 512, 362, 568, 568, 910, 684, 226, 340, 340, 398, 596, 284, 340, 284, 284, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 284, 284, 596, 596, 596, 568, 1040, + 684, 684, 740, 740, 684, 624, 796, 740, 284, 510, 684, 568, 852, 740, 796, 684, + 796, 740, 684, 624, 740, 684, 966, 684, 684, 624, 284, 284, 284, 480, 568, 226, + 568, 568, 510, 568, 568, 284, 568, 568, 226, 226, 510, 226, 852, 568, 568, 568, + 568, 340, 510, 284, 568, 510, 740, 510, 510, 510, 342, 266, 342, 596, 390, 983, + 340, 340, 226, 226, 512, 512, 512, 512, 512, 512, 512, 512, 788, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 1024, 634, 636, 684, 434, + 993, 1012, 896, 640, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, + 993, 993, 993, 993, + )), + "한양중고딕": (1000, 1000, 500, ( + 500, 420, 535, 529, 867, 643, 240, 288, 281, 369, 518, 246, 518, 245, 263, 500, + 500, 500, 500, 500, 500, 500, 500, 500, 500, 247, 245, 583, 519, 582, 524, 999, + 632, 629, 699, 693, 649, 594, 751, 689, 246, 470, 631, 539, 803, 692, 749, 637, + 754, 692, 636, 584, 688, 632, 904, 619, 619, 585, 253, 283, 253, 448, 500, 256, + 534, 527, 483, 531, 532, 255, 529, 529, 195, 196, 473, 195, 809, 529, 539, 535, + 531, 310, 480, 255, 531, 479, 697, 470, 466, 485, 310, 237, 306, 500, 381, 1000, + 375, 375, 225, 225, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 620, 621, 668, 423, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, + )), + "한양신명조": (1000, 1000, 500, ( + 500, 423, 450, 602, 920, 814, 386, 395, 374, 524, 567, 235, 572, 244, 390, 500, + 500, 500, 500, 500, 500, 500, 500, 500, 500, 276, 292, 729, 570, 734, 511, 920, + 794, 704, 690, 749, 677, 654, 742, 784, 337, 450, 792, 645, 959, 777, 748, 629, + 755, 703, 648, 774, 773, 780, 1037, 754, 769, 599, 362, 397, 352, 429, 500, 382, + 509, 556, 485, 543, 482, 352, 588, 560, 277, 314, 557, 274, 858, 564, 516, 534, + 548, 405, 486, 351, 562, 581, 840, 590, 574, 474, 418, 280, 421, 500, 384, 1000, + 441, 441, 230, 230, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 616, 623, 617, 409, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, + )), + "한양견고딕": (4096, 4096, 2048, ( + 2048, 1980, 2372, 2298, 3718, 2915, 1072, 1509, 1541, 1704, 2126, 1338, 2123, 1332, 1389, 2305, + 2305, 2305, 2305, 2305, 2305, 2305, 2305, 2305, 2305, 1325, 1325, 2954, 2126, 2872, 2503, 4096, + 2956, 2956, 2956, 2956, 2731, 2503, 3188, 2956, 1140, 2278, 2956, 2503, 3414, 2956, 3188, 2731, + 3147, 2956, 2731, 2503, 2956, 2731, 3867, 2731, 2731, 2503, 1379, 1399, 1379, 1752, 2048, 1089, + 2278, 2503, 2278, 2503, 2278, 1366, 2503, 2503, 1140, 1140, 2278, 1140, 3642, 2503, 2503, 2503, + 2503, 1594, 2278, 1366, 2503, 2278, 3188, 2278, 2278, 2048, 1645, 1235, 1639, 2053, 1561, 3933, + 2253, 2253, 1229, 1229, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 3152, 3972, 3972, 3972, + 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 4096, 2540, 2544, 2738, 1734, + 3972, 4048, 3584, 2560, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, + 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, + 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, + 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, 3972, + 3972, 3972, 3972, 3972, + )), + "휴먼옛체": (1024, 1024, 512, ( + 512, 512, 720, 606, 782, 694, 268, 348, 348, 460, 572, 268, 512, 268, 336, 548, + 548, 548, 548, 548, 548, 548, 548, 548, 548, 268, 268, 556, 540, 556, 504, 760, + 632, 578, 652, 624, 548, 536, 668, 606, 250, 482, 612, 532, 794, 644, 692, 606, + 720, 604, 594, 652, 614, 660, 884, 652, 632, 560, 356, 530, 356, 400, 512, 268, + 584, 550, 544, 580, 546, 422, 564, 552, 248, 342, 522, 250, 808, 544, 540, 582, + 548, 424, 484, 422, 544, 560, 776, 520, 524, 524, 380, 300, 380, 544, 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, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 896, 640, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 994, 994, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 994, 994, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 994, 994, 994, 994, 994, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 994, + 994, 994, 994, 994, + )), } diff --git a/tests/fixtures/hancom_saved/formfit_hft_faces_cells.hwpx b/tests/fixtures/hancom_saved/formfit_hft_faces_cells.hwpx new file mode 100644 index 0000000000000000000000000000000000000000..d980463967e0586364e59ec08fe94cbc3c248ba9 GIT binary patch literal 41995 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 literal 0 HcmV?d00001 diff --git a/tests/test_form_fit_hancom_rules.py b/tests/test_form_fit_hancom_rules.py index d90e3b9f..7d0fec45 100644 --- a/tests/test_form_fit_hancom_rules.py +++ b/tests/test_form_fit_hancom_rules.py @@ -70,6 +70,7 @@ MORE_FACES_FALLBACKS = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_more_faces_fallback_cells.hwpx" MORE_FACES_HANGUL = Path(__file__).parent / "fixtures" / "hancom_saved" / "formfit_more_faces_hangul_cells.hwpx" 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" 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 = [ @@ -140,6 +141,27 @@ def test_cells_in_hy_headline_break_where_hancom_breaks_them() -> None: assert len(cells) == 22 +def test_cells_in_hancom_font_faces_break_where_hancom_breaks_them() -> None: + # Hancom laid this document out: each cell holds a glyph and five Hangul syllables in one of the faces Hancom + # ships in its own font format (HCI Poppy, HCI Hollyhock, 한양중고딕, 한양신명조, 한양견고딕, 휴먼옛체) or in + # HY견명조 or HY동녘M, at one of the two widths where its line count changes, at 10 pt and at 30 pt. + doc = HwpxDocument.open(HFT_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) == 224 and len(faces) == 8 + + def test_cells_in_more_faces_break_where_hancom_breaks_them() -> None: # Hancom laid these documents out: each cell holds a glyph and five Hangul syllables in 한컴산뜻돋움, HY중고딕, # HY견고딕, HY신명조, 새굴림 or 한컴 말랑말랑 Regular at one of the two widths where its line count changes, at