Fontconfig (5日目) - The programmable configuration language “fonts.conf” =========================================================================== .. post:: Dec 05, 2022 :tags: fontconfig 見える……見えるぞ……ここまで読んでくださった方々が一斉に戻るボタンを押すのを……。 いやマジで、Fontconfigの設定ファイルは文法がXMLでとても読み辛い書き辛いですけれども命令型言語なんですってば。 本当は「The programming language “fonts.conf”」としたかったのですけれども、チューリング完全ではないものを「programming language(プログラミング言語)」と言っちゃうのは若干憚られ、 `Dhall `_ に倣って「programmable configuration language(プログラム可能な設定言語)」ということで。 以後、設定ファイル名としての *fonts.conf* はイタリックで、言語名としての“fonts.conf”はクォーテーションで書き分けます。 なお言語名のほうは何か呼び名が欲しいので私が勝手に呼んでいるだけです。 Fontconfigのドキュメントでは「font configuration file(s)」としか書かれてないです。 定義 ---- “fonts.conf”はXMLの上に構築された言語です。 詳しい定義は `/usr/share/xml/fontconfig/fonts.dtd `_ を参照ください。 できること ---------- 変数が使えます。 破壊的更新が可能です。 つまり関数型言語風なDhallやパターンマッチと置換で構成されるXSLT等とは大きく異なり、上から順番に実行される命令形言語です。 リスト操作ができます。 むしろ全ての変数はリストです。 四則演算や集合演算、文字列操作、比較等ができます。 条件分岐ができます。 他のファイルをincludeできます。 ただし同じファイルを2回includeしても無視されます。 つまり再帰はできません。 できないこと ------------ チューリング完全ではありません。 具体的にはループや再帰が書けません。 ですのでうっかり無限ループさせてしまうようなことはないのでご安心ください。 再帰的なデータ構造もありません。 リストを入れ子にしたりはできません。 サブプログラム定義、関数定義、ラムダ式のようなものもありません。 間接参照のようなものもありません。 つまりプログラムの再利用可能な部品化はできません。 外部に影響を及ぼすような副作用は行えません。 能動的にファイルに出力したりログを吐いたりもできません。 Hello, World ------------ それではお約束のHello, World行ってみましょう。 .. code-block:: xml :caption: hello.conf Hello, World! 上記の内容を適当なディレクトリに *hello.conf* という名前で保存してください。 実行結果は ``fc-pattern -c`` で得ることができます。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=hello.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) MESSAGE: "Hello, World!"(w) はい、“fonts.conf”には出力を行う機能がありませんので実行結果は変数こと属性を通じて確認するしかありません。 Fontconfigが定義している属性はFontconfigの動作に関わりますのでサンプルでは適当なユーザー定義属性を使います。 環境変数を用いて */etc/fonts/conf.d/* を読み込ませず実行していますが ``lang`` と ``prgname`` だけはFontconfig自身が追加してきますのでどうやっても付いてきます。 ですので無視してください。 ちなみに環境変数 ``LANG`` をunsetしても英語(en)になるだけでした。 代入 ---- 変数……変数と言い張るのも疲れましたので属性に戻します……属性の破壊的更新には ```` を使用します。 ```` は ```` の中に置く必要があります。 上の *hello.conf* は最低限の記述ですので色々とXML属性……この属性はアトリビュートですねややこしいですね……XMLアトリビュートの省略があります。 省略せずに書くとこうなります。 .. code-block:: xml :caption: hellofull.conf Hello, World! +++++++ ```` のXMLアトリビュート ``target`` は ``pattern``\ 、 ``font``\ 、 ``scan`` 何れかの値を取ります。 ``pattern`` はフォントパターンで指定された属性を判定/変更します。 ``font`` は( ``fc-query`` で確認できる)実際のフォントの側の属性を判定/変更します。 ``scan`` はよくわかりませんがキャッシュに影響を与えるようです。 少なくとも今日はサンプルの実行に ``fc-pattern -c`` を使いますので全部 ``pattern`` です。 デフォルトは ``pattern`` ですので省略して構わないわけです。 ++++++ ```` のXMLアトリビュート ``name`` は変更する属性名、 ``mode`` は変更の方法、 ``binding`` は優先度です。 ``mode`` は色々とありますがリスト操作の際に説明します。 デフォルトは ``assign`` で代入です。 ``binding`` は昨日見ましたように ``weak``\ 、 ``strong``\ 、 ``same`` とあります。 実行結果のMESSAGEに ``(w)`` が付いてますようにデフォルトは ``weak`` です。 ファミリー名以外では意味がないのではと思ってます。 (気付いたことがありましたら修正します。) 計算式 ------ 計算をしてみましょう。 .. code-block:: xml :caption: calc1.conf 1 2 X 0.5 実行。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=calc1.conf fc-pattern -c '' Pattern has 4 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: 3(i)(w) Y: 1.5(f)(w) 1+2=3、3×0.5=1.5ですね。 , , , , , ... +++++++++++++++++++++++++++++++++++++++++++++++++++++ これらのXMLタグは値を書く箇所に使えます。 ```` や ````\ 、 ```` は値そのものです。 値として定数文字列を用いる ```` や ```` 等もあります。 3日目に軽く触れましたがフォントパターン ``:weight=bold`` が ``:weight=200`` なのと同様に ``bold`` は ``200`` と同じです。 ```` や ```` 等は計算式です。 比較演算や ```` 等もあります。 ````\ 、 ```` 等の丸め関数もあります。 ```` で他の属性の値を参照することもできます。 条件分岐 -------- コマンドラインで指定されたパターンの値によって分岐してみましょう。 .. code-block:: xml :caption: cond1.conf medium Regular medium Bold この例は ``weight`` の値がmedium以下か、mediumを超えているかで分岐します。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=cond1.conf fc-pattern -c ':weight=regular' Pattern has 4 elts (size 16) weight: 80(f)(s) lang: "ja"(w) prgname: "fc-pattern"(s) MESSAGE: "Regular"(w) $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=cond1.conf fc-pattern -c ':weight=bold' Pattern has 4 elts (size 16) weight: 200(f)(s) lang: "ja"(w) prgname: "fc-pattern"(s) MESSAGE: "Bold"(w) Fontconfig側で定義されていない属性をコマンドラインで渡そうとするとエラーになりましたので ``weight`` を使用しました。 ユーザー定義属性を使って分岐することももちろんできます。 ++++++ ``name`` アトリビュートで指定した属性の値と ```` 自体の子ノードの値を ``compare`` アトリビュートで指定した方法で比較します。 条件が成立すればそのまま進み、不成立であれば ```` を抜けます。 ```` の中に複数の ```` と ```` を入れることもできます。 しかし ```` 自体を入れ子にすることはできずelse節にあたる内容を書く場所もないため同じようなことを繰り返し書かないといけない場面も多く不便です。 リスト操作 ---------- 全ての属性はリストです。 リスト操作は ```` の ``mode`` で色々できます。 後ろに追加。 .. code-block:: xml :caption: list1.conf 1st 2nd appended 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list1.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: "1st"(w) "2nd"(w) "appended"(w) 前に追加。 .. code-block:: xml :caption: list2.conf 1st 2nd prepended 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list2.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: "prepended"(w) "1st"(w) "2nd"(w) リストに対してデフォルトの動作である ``assign`` をするとどうなるのでしょう。 .. code-block:: xml :caption: list3.conf 1st 2nd overwritten 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list3.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: "overwritten"(w) リストの全ての値を破棄しての上書きでした。 条件分岐とリスト操作 -------------------- 条件分岐とリスト操作の組み合わせにはひとつミソがあります。 ```` はデフォルト(アトリビュート ``qual`` が ``any``)ではリストの中の何れかの値が一致すればそれで成立します。 そして、続く ```` ではリストの中の一致した箇所が操作対象になります。 例えば ``append`` であれば最初に一致した値の次に挿入されます。 .. code-block:: xml :caption: list4.conf 1 2 3 1.5 appended 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list4.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: 1(i)(w) 2(i)(w) "appended"(w) 3(i)(w) 複数一致しても最初に一致した箇所のみが変更されます。 もう一例。 上書きの場合。 .. code-block:: xml :caption: list5.conf 1 2 3 1.5 overwriten 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list5.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: 1(i)(w) "overwriten"(w) 3(i)(w) の順番 ++++++++++++ ```` が複数ある場合はどうなるのでしょう。 これは最初の ```` で一致した位置となります。 つまり ```` の順番も重要です。 .. code-block:: xml :caption: list6.conf 1 2 3 1.5 3 overwriten 実行結果。 :: % FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list6.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: 1(i)(w) "overwriten"(w) 3(i)(w) ```` を入れ替えてみます。 .. code-block:: xml :caption: list7.conf 1 2 3 3 1.5 overwriten 実行結果。 :: $ FONTCONFIG_PATH=$PWD FONTCONFIG_FILE=list7.conf fc-pattern -c '' Pattern has 3 elts (size 16) lang: "ja"(w) prgname: "fc-pattern"(s) X: 1(i)(w) 2(i)(w) "overwriten"(w) リスト全体の操作 ++++++++++++++++ ```` の後でもリスト全体を操作したいこともあります。 その場合は ``assign``\ 、 ``prepend``\ 、 ``append``\ 、 ``delete`` の代わりに ``assign_replace``\ 、 ``prepend_first``\ 、 ``append_last``\ 、 ``delete_all`` を使用します。 インクルードファイル -------------------- ```` を使用して設定ファイルから他のファイルをincludeできます。 ただし同じファイルを2回以上includeしようとしても無視されます。 ですので再帰には使えません。