- アラビア語のWebタイポグラフィは、文字の連結、双方向テキスト、数字・句読点処理、行揃えが絡み合うレンダリング基盤の問題であり、単なるCSSバグとして扱うのは難しい
- 古典アラビア語の組版では、単語間の空白ではなく文字内部の画を伸ばす**カシダ(kashida)**によって両端揃えを実現していたが、現代ブラウザの
text-align: justifyは主に単語間の空白を広げる - アラビア文字は、保存された1つのコードポイントが文脈に応じて独立形・語頭形・語中形・語末形に変化し、OpenType機能とシェーピングエンジンなしでは文字が分離した形でレンダリングされる
- UnicodeのArabic Presentation Forms、数字体系、UAX #9双方向アルゴリズム、不可視の制御文字は、検索失敗・電話番号の反転・カーソル移動の混乱といった実際の製品上の問題につながる
- HarfBuzz、Amiri、W3C Arabic Layout Requirements などの重要な基盤は構築されたが、ブラウザのアラビア語両端揃えと
jstf活用には依然として実装の空白が残っている
出発点: 「CSSバグ」に見えたアラビア語組版の問題
- 顧客向けダッシュボードの混在コンテンツを含むアラビア語段落が、デザインカンプのように両端揃えされず、左端がギザギザのままレンダリングされていた
- 同じブロックのラテン文字版は「fine」に見えたが、アラビア語では行が右から始まるため、ギザギザの端は左側に現れる
text-align: justifyを適用しても、デザインチームが承認した形のように単語内部の画を伸ばして行を埋めることはできなかった- 同じ製品では以前にも、PDF内の名前の文字分離、検索インデックスの失敗、レガシーUnicodeコードポイント問題など、アラビア語処理の問題が繰り返されていた
- 問題の本質は特定のスタイルシートの欠陥ではなく、Webにおけるアラビア語タイポグラフィの現状にある
筆写の伝統が解決していた問題
- 古典アラビア語の筆写の伝統では、単語間の空白を広げるのではなく、文字形内部の連結画を伸ばして行を両端揃えしていた
- この方式はtaṭwīl、あるいは現代の技術用語ではkashidaと呼ばれ、特定の文字対の間の連結画を長く引き伸ばす方式である
- 17世紀のNaskhによる整ったページは、左右の余白が揃い、単語間隔が広がらないため、緻密で規則的なテクスチャを作り出している
- Ibn Muqlaが整理したal-khaṭṭ al-mansūbは、葦ペン先の菱形の点、alifの高さ、円弧の比率などによって文字形を体系化した
- この伝統では、行揃えは空白配分の問題ではなく、文字形と代替グリフを選ぶシェーピングの問題だった
1文字、4つの形
- アラビア語は常に筆記体のようにつながる文字であり、印刷体と手書きを分けるブロック体の区別がない
- 各文字は隣接文字に応じて独立形、語頭形、語中形、語末形に変化し、6文字は前方へ接続しないため、単語内部の流れを断ち切る
- Unicodeは抽象文字を保存し、フォントは位置別グリフを提供し、シェーピングエンジンは
isol、init、medi、fina、rlig、mark、mkmkといったOpenType機能を適用する محمدのような単語は、保存上は4つのコードポイントだが、レンダリング時には複数のグリフ選択とOpenType参照を経て、1本の連続した画として見える- HarfBuzzのようなシェーピングエンジンが存在しない、あるいはPDF生成器がそれを通さない場合、同じコードポイントでも互いに分離した独立形の文字としてレンダリングされる
Unicodeの化石: Arabic Presentation Forms
- DOSや初期Windows時代の8ビットコードページでは、抽象文字ではなく、語頭形・語中形のような形そのものを別個の文字としてエンコードしていた
- Unicodeは往復互換性のためにこれを受け入れ、U+FB50からU+FEFFまでのArabic Presentation Formsブロックとして残している
- 新しい文書にこれらのコードポイントが入るべきではないが、PDFテキスト抽出器は現在でもこれを出力することがある
- 同じ名前が現代UnicodeとPresentation Formsでそれぞれ保存されると、画面上では同じに見えても、文字列比較や検索では異なるものとして扱われる
- NFKC正規化を適用すれば、Presentation Formsを抽象文字へ畳み込んで検索漏れを減らせる
シェーピングと双方向処理を飛ばしたソフトウェア
- シェーピングエンジンと双方向アルゴリズムを飛ばすソフトウェアは、文字を1つずつ独立形で描き、行を左から右へ配置する
- このような出力は、店の看板、搭乗券、ウォーターマーク、古い映画関連のアラビア語表記などで、アラビア語読者が実際に目にする形である
- 古いPhotoshop、デフォルト設定のmatplotlib、npmのさまざまなPDF生成器、レシートプリンターがこうした問題を起こしうる
- Pythonでよく使われる回避策の
arabic_reshaperとpython-bidiは、Presentation Formsブロックを使って、あらかじめシェーピング済みの形を文字列に焼き込む - この回避策は、レンダラーが担うべき仕事を文字列側に前倒しする方式であり、根本的にはテキストスタックの欠陥を露呈している
3種類の数字と句読点の問題
- 世界が「Arabic numerals」と呼ぶ0–9は、ほとんどのアラビア語読者が日常的に使う数字の字形ではない
- エジプト、スーダン、レバント、イラク、湾岸地域では、UnicodeのARABIC-INDIC DIGITS
٠١٢٣٤٥٦٧٨٩を使用する - マグレブ地域ではラテン数字グリフを用い、イラン・アフガニスタン・パキスタンではEXTENDED ARABIC-INDIC DIGITS
۰۱۲۳۴۵۶۷۸۹を用いる - 数字はUAX #9で強い文字ではなく弱い文字として扱われ、先行する強い文字の方向性に応じてヨーロッパ数字またはアラビア数字へ再分類される
- アラビア語の単語の後にある
010-1234-5678のような電話番号は、ハイフンが中立として扱われることで、画面上で5678-1234-010のように順序が入れ替わることがある - プラットフォームが提供する解決策は、番号を
または<bdi>で囲み、方向性を分離する方法である - アラビア語圏の小数点と桁区切りにはU+066B
٫とU+066C٬が使われ、ASCIIの.と,は見た目がほぼ同じでもコードポイントと双方向特性が異なる
印刷からWebまで続く回避と単純化
- 1514年にFanoで印刷された Kitāb Ṣalāt al-Sawāʿī は最初のアラビア語活字本であり、文字連結の分離や点位置の誤りが見られる事例である
- 1537年のVeniceのPaganini Qurʾānは、組版ミスと本文ミスが重なって商業的に失敗し、1部が1987年にVeniceの修道院図書館で発見された
- オスマン帝国の印刷禁止の話は、Bayezid IIとSelim Iの勅令原文が残っておらず、ヨーロッパ旅行者の記録に依存しているという問題がある
- CairoのBulaq Pressは1820年にMuhammad Aliが設立し、数百個の活字片と多大な忍耐によってアラビア語金属活字の品質を引き上げた
- 1924年のCairo QurʾānはAmiria Pressで金属活字によって制作され、20世紀のテキストとタイポグラフィの標準化に貢献した
- 1950年代後半、Kamel MrowaとLinotypeは90チャンネルのマガジンに合わせるため、語頭形を語中形に、語末形を独立形に統合し、合字を減らしたSimplified Arabicを作った
- Simplified Arabicは安価で高速な新聞制作を可能にし、1世代のうちにアラビア語ニュースルームを席巻した
Webがまだ描けないカシダ
- CSS Text Module Level 3の初期草案には、
text-justifyの値としてkashidaがあり、Internet Explorer 5.5は2000年にこれを実装していた - IE 5.5は
text-kashida-spaceプロパティも提供していたが、他のブラウザが実装しなかったため、その値は仕様から外れた - 現代のChrome、Firefox、Safariの
text-align: justifyは、アラビア語では単語間の空白を広げる方式で動作する - CSS Working Groupのアラビア語整列のIssueは少なくとも2015年から開かれており、W3C Arabic Layout Requirementsの作業も同年に始まった
- カシダ整列では、伸びたグリフが幅を変え、その幅の変化が改行位置と必要な伸長幅を再び変えるため、シェーピングとレイアウトが行単位で反復的に交渉しなければならない
- OpenTypeには1990年代から、フォントが整列の優先順位を知らせる
jstfテーブルがあったが、シェーピングエンジンはほとんどこれを読まず、フォント制作者もほとんど提供していない - Microsoft WordとInDesign Middle East Editionはカシダ整列を提供するが、人々が最も多く読むブラウザレンダラー群は文字を伸ばせない
Tatweelハックと合字・母音記号の問題
- Webでよくある回避策は、テキスト自体にU+0640 TATWEEL文字を挿入し、伸びた画のように見せる方法である
- Tatweelはコンテンツそのものを変更するため、検索一致、コピー&ペースト、スクリーンリーダー、カラムのリフローで問題が生じる
- Tatweelが描く画は、フォントと文字の規則に従って配置されるカシダではなく、作者が推測で入れたタイプライター的な棒にすぎない
- OpenTypeの合字は
rlig、liga、dligに分かれ、lām-alifのような必須合字が壊れると、文字は単に見栄えが悪いだけでなく誤りになる - U+200C ZERO WIDTH NON-JOINERを文字間に入れると、保存される文字自体は維持されるが、レンダリングでは各文字を独立形に強制する
- Safariは
"rlig" 0、"liga" 0を無視するため、必須合字を無効化するデモは影響を受けない - AmiriはKhaled Hosnyが2011年にSIL Open Font Licenseで公開したNaskhフォントで、2022年の1.0リライト後は、曲線的なカシダと精巧な母音記号の積み重ねを提供する
line-height: 1とoverflow: hiddenが組み合わさったカードコンポーネントでは、完全に母音記号付きのアラビア語の上側の母音記号が切り落とされることがある
双方向アルゴリズムと嘘をつくカーソル
- アラビア語段落内のバージョン番号、英語識別子、URL、フランス語の単語などは、Unicode Bidirectional AlgorithmであるUAX #9を呼び出す
- アラビア文字は強い右から左の文字、ラテン文字は強い左から右の文字、数字は文脈に従う弱い文字、空白と句読点は中立文字として扱われる
- アルゴリズムは文字ごとに方向クラスを割り当て、弱い文字と中立文字を段階的に解釈した後、埋め込みレベルを割り当て、同じレベルのランを反転する
- 画面上の視覚順序とメモリ内の論理順序が異なるため、カーソル移動、マウスクリック、選択動作はこの2つの順序の間を絶えず変換しなければならない
- ラン境界には論理位置と視覚位置という2つの合法的なカーソル位置があり、Chrome、Firefox、Qt、Outlookはこれを互いに異なる形で扱うことがある
- アラビア語と英語が混在したテキストの作成は、2026年になっても主要なエディタ、メールクライアント、チャットアプリで、依然として認知コストの高い体験のままである
الصفحات 10-20のような範囲は、規則W2と中立ハイフン処理のために「20から10まで」のように見えることがあり、先頭にU+200E LEFT-TO-RIGHT MARKを入れることで修正できる
機能している基盤と残された空白
- Khaled HosnyはAmiriを制作し、HarfBuzzのコマンドラインツール
hb-shapeを書き、HarfBuzzの共同メンテナでもある - Behdad EsfahbodはHosny以前にHarfBuzzの多くの部分を書き、現在のブラウザでアラビア文字を正しく描くシェーピングエンジンに貢献している
- BrillはSemiticsカタログに必要な音訳文字をすべて含めるため、John HudsonにBrill書体を依頼し、2011年に非商用無料で公開した
- Sakhr AX-170は1984年ごろROM上でアラビア語を表示したSaudi-Kuwaiti製MSXコンピュータで、右から左に書くArabic BASIC識別子をサポートしていた
- HarfBuzz、Amiri、Scheherazade、GNU UnifontのPresentation Forms対応、Noto Arabic、W3C Arabic Layout文書は、少数の個人・団体・ボランティアの努力に大きく依存している
- ブラウザベンダーはHarfBuzzが無償で完成した後にこれを取り入れたが、筆写の伝統的な整列方式を画面上で実装するレイアウトループにはほとんど貢献していない
- 残るギャップは、いくつかのレイアウトエンジンで実装すべき、よく理解されたアルゴリズムであり、顧客ダッシュボードのギザギザした左余白は、この投資不足がユーザーに見えている形である
1件のコメント
Lobste.rs のコメント
本当に素晴らしい記事。
しかもこの文字
﷽が 1つのコードポイント だというのが気に入った。コピーしてみると面白い。意味は「最も慈悲深く、最も慈愛あまねきアッラーの御名において」とのこと。
﷽についての本文中のこの一文は詩的だ。「レンダリングエンジンを誰も信頼できず、レンダリングがエンコーディングに焼き込まれていた時代の記念碑。朗唱するハエが琥珀の中に永遠に保存されているようなものだ」
そこから参照されている Wikipedia リンクの参考文献をたどるのも面白かった: https://lobste.rs/c/dq2ucz
要するに、この文字が Unicode に入ったのはパキスタンのコードページに存在していたからで、そこに入ったのは法的文書にその文句を含める法的要件があったからだ。Urdu はインド・ヨーロッパ語族の言語なので、当時の技術では Basmallah を書くためにアラビア語コードページへ「外部呼び出し」のように切り替えるのは難しかったのだろう。
残念ながら、すべてのコメントがそのコミュニティの良さを示しているわけではない。
メタ: こういう記事には
typographyタグ が必要だという、またひとつの好例だ。lobste.rs ではタグは主に フィルタリング 用だ。
IE の
text-justifyプロパティ か。あの時代はいろいろ面白いものがあった。text-justify: newspaperもあったが、数十年後にこれを Knuth-Plass かそれに近いものとして説明する人もいたものの、実際にそうだったとは思えない。https://mediumwell.com/wp-content/uploads/… は、当時
text-justify: newspaperと主張されていた挙動が、現在の仕様におけるtext-justify: inter-characterと一致している様子を示している。IE はかなり早い時期から本当に優れた機能を多く備えていて、他のブラウザはそうした機能を「難しすぎる」箱に放置していた。結局戻ってこなかったものもあれば、15年あるいは30年たってようやく戻ってきたものもある。Firefox は 2017 年に
text-justify: inter-characterを獲得し、Chromium は数か月前になってようやくその部分を実装し、Safari にはいまだにない。とてつもなく素晴らしく、有益な記事だった。記事全体に広い文脈を与える 歴史的背景 がとりわけ良かった。
歴史関連の学位と IT の経歴の両方を持つ身として、これは自分の二つの関心を完璧に突いてくる記事だった。
記事のどこかが自分の LLM 検知器 を鳴らしていて、それが残念。内容に深みがあり、現代の技術スタックであまり文書化されていない部分を扱っているからこそなおさらだ。
LLM っぽく読める例としてはこんな箇所で、記事全体にそういう感じがある。
「どのブラウザもこれを提供しない理由は構造的なものであり、その構造は障壁としてかなり優雅ですらある。Latin の両端揃えは、シェーピング済みテキストを固定されたものとして扱い、単語を計測し、余った空間を間隔に流し込み、それで終わる。シェーピングとレイアウトはそれぞれの箱の中に留まり、稼働中のあらゆるテキストスタックはその分離を中心に設計されている。Kashida の両端揃えはその箱をこじ開ける。」
@lr0 に聞いてみたいのだが、この記事の本文は LLM によって生成・推敲・翻訳されたものなのだろうか。もしそうなら、最終出力に対して LLM が持つ制御の度合いを調整したほうがよいかもしれない。以前のブログ記事、たとえば https://lr0.org/blog/p/gpt/ や https://lr0.org/blog/p/linux_new_users/ は、もっと人間らしく感じられた。