aboutsummaryrefslogtreecommitdiffstats
path: root/filters
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2024-06-15 23:10:33 +0200
committerRobin Jarry <robin@jarry.cc>2024-06-16 14:04:29 +0200
commit99bc69918ea782603894de8ab5f66d53a10046a2 (patch)
tree96353f25576b6439f251571fbf741dfce1b48b5b /filters
parent73bcb4661460d44817de2904fec14a69af819f43 (diff)
downloadaerc-99bc69918ea782603894de8ab5f66d53a10046a2.tar.gz
wrap: fix wide CJK characters support
Neither Chinese, Japanese nor Korean use spaces as words separators. It is OK to hard wrap before any symbol (except CJK punctuation which should probably stay attached to its previous symbol and not be left alone at the start of a line). I did not include Japanese Hiragana and Katakana symbols as word breaks on purpose since these are phonetic alphabets. It didn't make sense to wrap a line in the middle of a "word" made of these phonetic symbols. Update the code to allow considering either a space or a CJK known symbol as a split point for hard wrapping. Add test case with Chinese, Japanese and Korean generated lorem ipsum style text. Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Runxi Yu <me@runxiyu.org>
Diffstat (limited to 'filters')
-rw-r--r--filters/vectors/wrap-cjk.expected75
-rw-r--r--filters/vectors/wrap-cjk.in30
-rw-r--r--filters/wrap.c39
3 files changed, 139 insertions, 5 deletions
diff --git a/filters/vectors/wrap-cjk.expected b/filters/vectors/wrap-cjk.expected
new file mode 100644
index 00000000..eebe4436
--- /dev/null
+++ b/filters/vectors/wrap-cjk.expected
@@ -0,0 +1,75 @@
+# Chinese
+
+涾一祄琌幄睟亍瑳褭冘,蚳乜枲挻媗楪乇酳冘仡。毰侘癿澺搥諨厊一釽埸貣侗夯昺丌,筥
+跐亍阽疪暔芫氿乇邳嵺苵殳刳。紬一炰郴啿蓱亍銍瑿毌,脧乜俉舲棬罧屮碞冇汃。殳珨攽
+祪陎齵僆裞扠尥畇妧矻杶芏瑗屮抰廑嫊。罣一洒剫鈂搣丌餈蹅圠,絇乜苭梇睌痯乇獃气
+仚。琇一峓趹絟痯丌慱懌夬,聇乇羍耛堙祽亍瑱冇圣。釽炖灱鴞飶橝邗一絘珴彧呥仩峛
+乇,嗂揤兀泲垛嗒侇汃亍沭鉽毘仈姈。舺一柼婐喑媐乜漮蕣丱,蚹兀玵窔棤跬丌蒔丮庂。
+
+夬珓芶婤迠靃萰輈匢朳姷妡泆坨忻鈱乇沴瞁翜。丏茯炓笵苹靃瑒窣囡囡胘甹厒竻汦壾兀拊
+箤雸。夬栴刲烸垌鑫粲綍仵汔胍尪怓枟佁搹丌帔摓椴。惔一帡梜淼蒍乜嵼懁尐,崞乇虼軗
+鈚頍屮髧殳卌。厹迼岪孲茌瓥旓惷艸厊郕巠怓昃伻艀亍欥跿楪。挻一峊娹晪誄兀銆濏仂,
+軜屮昦捵棝幋屮搿尐尕。郴一羑牾睄趓屮蒝徻亓,剭丌怷祩硤酯丌鉺卬帄。
+
+夃垸弢莗枹蠷僉椴扥阣胇玔抪岟杍稗兀泂蓒媐。雂怮吇謔詵鋾扡一蛬焐浰妽尻狪亍,赨鈀
+丌欥赲蜎屇阞丌芛獑罘冇芧。庴一茇紬鈏溦乇漭錉冘,梪兀秖軜湱僉乜鄣殳氻。掾沝仵螛
+塣螇艼一觝衒脁屇宁庛乇,朠棔兀屇挍搷咂仚亍怭馺俍卬岠。冘舯枎翋怹鸂雎祹朾朻耎肒
+泀狖帊萿亍呯榡搧。絓芩朿褢楩瘼妅一揘崚桏厒屴俬乜,摁嵫乜怬娀廆姁氶乇抶蓗柲丮
+芼。袼一虴梮喵搨乜蜑廦丱,紨丌枲翊傔葑亍漊气圣。剫一耏烸蛪鈳亍膇鋻旡,酕丌柣啒
+晲裬乜緄仉屴。渃泆妅嶵酨叡玎一罥桷捊劼汃癹乇,煐厤亍泝籺萻咍仚兀矼墔柧尐抾。
+
+偢一苲猞敪窣乜漊憴尐,眥兀屌笰湒楢兀槊巿氶。旡秝盰絅笁鼞楜雺虍阢垌犿昈姎抌壼乜
+矼褙蛖。厹茦皯唲釓鸂瘃暊阠扡咠忨妺沷佖閟丌肏睾睒。跁一茌掑褁摀亍輎閾巿,紻丌柜
+崌蛝雎乜慺厹夯。氪一罘粣湒僁兀嫛踼爿,脕亍羑翊羡犐兀戩勼扐。蛅一庢粣缾艅乜嶀篣
+仈,秶乜紃荾埵寖乇戩丮圣。仉罜怢庴氠齵巰輆刐庄宬岒盰矷灴葝丌昍綩銢。冇倎侚捥柼
+瓛貄媱厊艼柎卣弣芠玗楈乜抭蜺嗊。
+
+# Japanese
+
+応ルモユム格分ぎっ中側な例披ロオソリ盛申みこへ教更反トホ写3録47男ソ朝開ク中
+属ノ速法む米指ふさりこ考震フ明訟ばだ著敢渓窮胡よ。予ムヒト選技32浴ぜが子円もみ
+歳旅せてうけ実石ミサアト未9首放レミ募権大ん同市体づイえず業歯そつ情道タネモ回
+着ぞあイい本要イ肉見線館そむふ。
+
+死まこリば治9高詳かすぼク精報今ヒユエ水87学ニミナ急望とげがか芸会どげ原
+員カリミ信絶ホネワシ奪己あうは般裁午ハ招候森嘉ほ。決リぞ初定物るべ橋投レげ料
+代ひまわト員野トイるゅ表高んをご付検モ健棋ッよ情作ヘサラ本代ヘシタソ毎
+権びつずぱ続碁約ヤオマカ破見ヱ暮私フ住18逃見ゅは。変む記78実落ご破府ケ広千ンラ
+供案ミ払処査ぽとそ向7窒特さ阪音隣ラウヨ稿分ひだ。
+
+神セモル京森97問らた文客44盾粘3聴きッい権不サ雑種フワ治出えよ善重さ真56更
+教ヲマヱネ記内ラの出変げ温評マエトチ名需さラの。及済ニ場策カホ掲解ア養20済
+入にン惑提ク何一ぼおあた公図とフぴン任必レウム案雇止メ球化ヒ募座ヤシコ方列岸
+十たイぜ。料真ぎンぐ無基たをょか更写ちろッむ中聞んずぽ赤日リ当就ヱルハ行
+会リふイ夜学ウ面治ンク者京つりッ望築トみラつ易若計害阪倶りス。
+
+名54徳リれ覧立モナフ息討三報ひそねで保朝ヨヒト表10外ずょ新戦固ド立経健けフふめ
+前判むざぞて中然どレづ平卸呪娯なみ。稚ヨサラ通略クラソス欲寺日ちろうー持稿ば両
+原ヨハミ相定ラ投画ひ著64担ケノヲア堂造にほひう名天並ヱソマ能加ネ道厳
+殺びのリり。水づじ購東むづるめ売独れ書門ルナメラ応務ド写6読ラハヤエ実案造ヒス
+経運ふで万歴剛もーっ著逆てろびひ法通ワニ改市キヤラ近教ルセ級督積利江ンねお。
+
+
+# Korean
+
+국가의 세입·세출의 결산, 국가 및 법률이 정한 단체의 회계검사와 행정기관 및
+공무원의 직무에 관한 감찰을 하기 위하여 대통령 소속하에 감사원을 둔다.
+일반사면을 명하려면 국회의 동의를 얻어야 한다. 공공필요에 의한 재산권의
+수용·사용 또는 제한 및 그에 대한 보상은 법률로써 하되, 정당한 보상을 지급하여야
+한다.
+
+대통령은 국가의 안위에 관계되는 중대한 교전상태에 있어서 국가를 보위하기 위하여
+긴급한 조치가 필요하고 국회의 집회가 불가능한 때에 한하여 법률의 효력을 가지는
+명령을 발할 수 있다. 국가유공자·상이군경 및 전몰군경의 유가족은 법률이 정하는
+바에 의하여 우선적으로 근로의 기회를 부여받는다.
+
+대법관의 임기는 6년으로 하며, 법률이 정하는 바에 의하여 연임할 수 있다. 모든
+국민은 학문과 예술의 자유를 가진다. 제3항의 승인을 얻지 못한 때에는 그 처분
+또는 명령은 그때부터 효력을 상실한다. 이 경우 그 명령에 의하여 개정 또는
+폐지되었던 법률은 그 명령이 승인을 얻지 못한 때부터 당연히 효력을 회복한다.
+
+대통령은 국무회의의 의장이 되고, 국무총리는 부의장이 된다. 국무총리는 대통령을
+보좌하며, 행정에 관하여 대통령의 명을 받아 행정각부를 통할한다. 모든 국민은
+근로의 권리를 가진다. 국가는 사회적·경제적 방법으로 근로자의 고용의 증진과
+적정임금의 보장에 노력하여야 하며, 법률이 정하는 바에 의하여 최저임금제를
+시행하여야 한다.
diff --git a/filters/vectors/wrap-cjk.in b/filters/vectors/wrap-cjk.in
new file mode 100644
index 00000000..364376c8
--- /dev/null
+++ b/filters/vectors/wrap-cjk.in
@@ -0,0 +1,30 @@
+# Chinese
+
+涾一祄琌幄睟亍瑳褭冘,蚳乜枲挻媗楪乇酳冘仡。毰侘癿澺搥諨厊一釽埸貣侗夯昺丌,筥跐亍阽疪暔芫氿乇邳嵺苵殳刳。紬一炰郴啿蓱亍銍瑿毌,脧乜俉舲棬罧屮碞冇汃。殳珨攽祪陎齵僆裞扠尥畇妧矻杶芏瑗屮抰廑嫊。罣一洒剫鈂搣丌餈蹅圠,絇乜苭梇睌痯乇獃气仚。琇一峓趹絟痯丌慱懌夬,聇乇羍耛堙祽亍瑱冇圣。釽炖灱鴞飶橝邗一絘珴彧呥仩峛乇,嗂揤兀泲垛嗒侇汃亍沭鉽毘仈姈。舺一柼婐喑媐乜漮蕣丱,蚹兀玵窔棤跬丌蒔丮庂。
+
+夬珓芶婤迠靃萰輈匢朳姷妡泆坨忻鈱乇沴瞁翜。丏茯炓笵苹靃瑒窣囡囡胘甹厒竻汦壾兀拊箤雸。夬栴刲烸垌鑫粲綍仵汔胍尪怓枟佁搹丌帔摓椴。惔一帡梜淼蒍乜嵼懁尐,崞乇虼軗鈚頍屮髧殳卌。厹迼岪孲茌瓥旓惷艸厊郕巠怓昃伻艀亍欥跿楪。挻一峊娹晪誄兀銆濏仂,軜屮昦捵棝幋屮搿尐尕。郴一羑牾睄趓屮蒝徻亓,剭丌怷祩硤酯丌鉺卬帄。
+
+夃垸弢莗枹蠷僉椴扥阣胇玔抪岟杍稗兀泂蓒媐。雂怮吇謔詵鋾扡一蛬焐浰妽尻狪亍,赨鈀丌欥赲蜎屇阞丌芛獑罘冇芧。庴一茇紬鈏溦乇漭錉冘,梪兀秖軜湱僉乜鄣殳氻。掾沝仵螛塣螇艼一觝衒脁屇宁庛乇,朠棔兀屇挍搷咂仚亍怭馺俍卬岠。冘舯枎翋怹鸂雎祹朾朻耎肒泀狖帊萿亍呯榡搧。絓芩朿褢楩瘼妅一揘崚桏厒屴俬乜,摁嵫乜怬娀廆姁氶乇抶蓗柲丮芼。袼一虴梮喵搨乜蜑廦丱,紨丌枲翊傔葑亍漊气圣。剫一耏烸蛪鈳亍膇鋻旡,酕丌柣啒晲裬乜緄仉屴。渃泆妅嶵酨叡玎一罥桷捊劼汃癹乇,煐厤亍泝籺萻咍仚兀矼墔柧尐抾。
+
+偢一苲猞敪窣乜漊憴尐,眥兀屌笰湒楢兀槊巿氶。旡秝盰絅笁鼞楜雺虍阢垌犿昈姎抌壼乜矼褙蛖。厹茦皯唲釓鸂瘃暊阠扡咠忨妺沷佖閟丌肏睾睒。跁一茌掑褁摀亍輎閾巿,紻丌柜崌蛝雎乜慺厹夯。氪一罘粣湒僁兀嫛踼爿,脕亍羑翊羡犐兀戩勼扐。蛅一庢粣缾艅乜嶀篣仈,秶乜紃荾埵寖乇戩丮圣。仉罜怢庴氠齵巰輆刐庄宬岒盰矷灴葝丌昍綩銢。冇倎侚捥柼瓛貄媱厊艼柎卣弣芠玗楈乜抭蜺嗊。
+
+# Japanese
+
+応ルモユム格分ぎっ中側な例披ロオソリ盛申みこへ教更反トホ写3録47男ソ朝開ク中属ノ速法む米指ふさりこ考震フ明訟ばだ著敢渓窮胡よ。予ムヒト選技32浴ぜが子円もみ歳旅せてうけ実石ミサアト未9首放レミ募権大ん同市体づイえず業歯そつ情道タネモ回着ぞあイい本要イ肉見線館そむふ。
+
+死まこリば治9高詳かすぼク精報今ヒユエ水87学ニミナ急望とげがか芸会どげ原員カリミ信絶ホネワシ奪己あうは般裁午ハ招候森嘉ほ。決リぞ初定物るべ橋投レげ料代ひまわト員野トイるゅ表高んをご付検モ健棋ッよ情作ヘサラ本代ヘシタソ毎権びつずぱ続碁約ヤオマカ破見ヱ暮私フ住18逃見ゅは。変む記78実落ご破府ケ広千ンラ供案ミ払処査ぽとそ向7窒特さ阪音隣ラウヨ稿分ひだ。
+
+神セモル京森97問らた文客44盾粘3聴きッい権不サ雑種フワ治出えよ善重さ真56更教ヲマヱネ記内ラの出変げ温評マエトチ名需さラの。及済ニ場策カホ掲解ア養20済入にン惑提ク何一ぼおあた公図とフぴン任必レウム案雇止メ球化ヒ募座ヤシコ方列岸十たイぜ。料真ぎンぐ無基たをょか更写ちろッむ中聞んずぽ赤日リ当就ヱルハ行会リふイ夜学ウ面治ンク者京つりッ望築トみラつ易若計害阪倶りス。
+
+名54徳リれ覧立モナフ息討三報ひそねで保朝ヨヒト表10外ずょ新戦固ド立経健けフふめ前判むざぞて中然どレづ平卸呪娯なみ。稚ヨサラ通略クラソス欲寺日ちろうー持稿ば両原ヨハミ相定ラ投画ひ著64担ケノヲア堂造にほひう名天並ヱソマ能加ネ道厳殺びのリり。水づじ購東むづるめ売独れ書門ルナメラ応務ド写6読ラハヤエ実案造ヒス経運ふで万歴剛もーっ著逆てろびひ法通ワニ改市キヤラ近教ルセ級督積利江ンねお。
+
+
+# Korean
+
+국가의 세입·세출의 결산, 국가 및 법률이 정한 단체의 회계검사와 행정기관 및 공무원의 직무에 관한 감찰을 하기 위하여 대통령 소속하에 감사원을 둔다. 일반사면을 명하려면 국회의 동의를 얻어야 한다. 공공필요에 의한 재산권의 수용·사용 또는 제한 및 그에 대한 보상은 법률로써 하되, 정당한 보상을 지급하여야 한다.
+
+대통령은 국가의 안위에 관계되는 중대한 교전상태에 있어서 국가를 보위하기 위하여 긴급한 조치가 필요하고 국회의 집회가 불가능한 때에 한하여 법률의 효력을 가지는 명령을 발할 수 있다. 국가유공자·상이군경 및 전몰군경의 유가족은 법률이 정하는 바에 의하여 우선적으로 근로의 기회를 부여받는다.
+
+대법관의 임기는 6년으로 하며, 법률이 정하는 바에 의하여 연임할 수 있다. 모든 국민은 학문과 예술의 자유를 가진다. 제3항의 승인을 얻지 못한 때에는 그 처분 또는 명령은 그때부터 효력을 상실한다. 이 경우 그 명령에 의하여 개정 또는 폐지되었던 법률은 그 명령이 승인을 얻지 못한 때부터 당연히 효력을 회복한다.
+
+대통령은 국무회의의 의장이 되고, 국무총리는 부의장이 된다. 국무총리는 대통령을 보좌하며, 행정에 관하여 대통령의 명을 받아 행정각부를 통할한다. 모든 국민은 근로의 권리를 가진다. 국가는 사회적·경제적 방법으로 근로자의 고용의 증진과 적정임금의 보장에 노력하여야 하며, 법률이 정하는 바에 의하여 최저임금제를 시행하여야 한다.
diff --git a/filters/wrap.c b/filters/wrap.c
index d0d3cfc3..c3109747 100644
--- a/filters/wrap.c
+++ b/filters/wrap.c
@@ -344,6 +344,33 @@ static void join_paragraph(
#define BUFFER_SIZE 8192
/*
+ * Check if a line can be split at the given character point.
+ */
+static bool is_split_point(const wchar_t c)
+{
+ if (iswspace((wint_t)c))
+ return true;
+
+ /* CJK Radicals Supplement */
+ if (c >= 0x2e80 && c <= 0x2fd5)
+ return true;
+ /* CJK Compatibility */
+ if (c >= 0x3300 && c <= 0x33ff)
+ return true;
+ /* CJK Unified Ideographs Extension A */
+ if (c >= 0x3400 && c <= 0x4db5)
+ return true;
+ /* CJK Unified Ideographs */
+ if (c >= 0x4e00 && c <= 0x9fcb)
+ return true;
+ /* CJK Compatibility Ideographs */
+ if (c >= 0xf900 && c <= 0xfa6a)
+ return true;
+
+ return false;
+}
+
+/*
* Write a paragraph, wrapping at words boundaries.
*
* Only try to do word wrapping on things that look like prose. When the text
@@ -356,6 +383,7 @@ static void write_paragraph(struct paragraph *p)
const wchar_t *indent = L"";
wchar_t *text = p->text;
bool more = true;
+ int wchar_count;
wchar_t *line;
size_t width;
@@ -365,6 +393,7 @@ static void write_paragraph(struct paragraph *p)
if (width + remain <= margin || p->prose_ratio < prose_ratio) {
/* whole paragraph fits on a single line */
line = text;
+ wchar_count = (int)wcslen(text);
more = false;
} else {
/* find split point, preferably before margin */
@@ -375,32 +404,32 @@ static void write_paragraph(struct paragraph *p)
if (width + w > margin && split != SIZE_MAX) {
break;
}
- if (iswspace((wint_t)text[i])) {
+ if (is_split_point(text[i])) {
split = i;
}
}
if (split == SIZE_MAX) {
/* no space found to split, print a long line */
line = text;
+ wchar_count = (int)wcslen(text);
more = false;
} else {
- text[split] = L'\0';
+ wchar_count = (int)split;
line = text;
- split++;
/* find start of next word */
while (iswspace((wint_t)text[split])) {
split++;
}
if (text[split] != L'\0') {
+ remain -= (size_t)wcswidth(text, split);
text = &text[split];
- remain -= split;
} else {
/* only trailing whitespace, we're done */
more = false;
}
}
}
- wprintf(L"%ls%ls%ls\n", p->quotes, indent, line);
+ wprintf(L"%ls%ls%.*ls\n", p->quotes, indent, wchar_count, line);
indent = p->indent;
}
}