1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

架空文字のための“フォント+正規表現DSL+レンダリング”をElixirで実装した話

1
Last updated at Posted at 2026-04-10

架空文字とは

ここでは架空の自然風言語、東果語の記述に用いられているという設定の文字のことを差す。

旧版(javascript+deno.ts)につき表記が一部乱れてこそいるが、詳しくはhttps://dead-hare-65--current.deno.dev/src/client/info/thaqqa_detail.html をご覧いただけたら幸い。

この文字体系は以下の特徴を持つ:

  • 文字の位置(頭字・中字・末字)によって字形が変化する
  • 文脈に応じて複数文字が1つのグリフに結合する
  • 単純な文字置換では表現できない

そのため、一般的なフォントや文字列処理では扱いが難しく、専用の処理系が必要となる。

ちなみに、東果語についての概要はこちらからご覧いただける: https://migdal.jp/asagikaki/%E6%9D%B1%E6%9E%9C%E8%AA%9E%E3%81%AE%E3%83%A1%E3%83%A2-4ai8

Elixirを選んだ理由

当初、javascript/typescriptを用い構築していたのだが、バックエンドとする際はどうしても正規表現を多く用いる箇所がボトルネックとなっていた。
(現在、当tsプログラムは諸事情から非公開としている。)

処理特性としてElixirが適していると考え、2026年1月頃に移植を決断。

嬉しいことに、Elixirのパターンマッチにより

  • 条件分岐の削減
  • データ構造に基づく自然な記述
  • DSLとの親和性

も実現できた。

作成したモジュールなど

SVGモジュール

文字のグリフ生成のためにSVGを直接操作する必要があり、用途からして軽量なもので良いと判断し、自前で作成。
SVG.CommandのlistからSVG.Pathを定義し、操作・計算を行う仕組みである。

将来には実際のSVGデータの高さ・幅を取得する機能を拡張する予定。
(現状、Pathに用いられているCommandの位置から高さ・幅を取得する機能は用意済であり、後記するモジュールの内部計算でも活用している。)

FontRegistry

架空文字の情報を記録・編纂するモジュール。二相コミット的な構造を持ち、実際の操作はFontRegistryPrefabモジュールのstructで執り行う。これは今後のpost/delete/put対応に備えたものである。

どのフォントにも共通する処理や情報を定義したstatic_datatables、フォント毎に異なる情報を定義したfontsの二つを持つ。

Font,DataTable,Column,Data

FontRegistryの内部表現用モジュール。 内包関係としては下図の通り。

FontRegistry
 ├─ static_datatables
 └─ fonts
     └─ Font
         └─ DataTable
             ├─ Column
             └─ Data

ColumnやDataは一般的なデータベースでいうColumn,Rowと同じ機能を果たす。
Columnはtypeとsubtypeのパロメータを持ち、typeで型を指定した後、subtypeにより取りうる値を制限する形を取っている。
RegexやPath StructのデータはVolatility typeとし、読み込み・FontRegistryの更新時に自動生成し活用、jsonとして保存する際は反映しないものとした。

Regex_Khhyl

東果文字の内部表現に特化したDSL。
正規表現を拡張したもので、ASTを生成した後に通常の正規表現へ変換される。

例:

  • S(*)
    → 東果語における任意の一音節文字

  • S(init,s0,a0,t0)
    → 頭字形の "sat"

  • S(mid,,abig0,)
    → 中字形の大文字 "a"

特徴:

  • DataTableと連携した条件抽出が可能
    例: S(init,,[_.isVowel==true],)
      →頭字形であり、母音として設定されている母音のみを持つ音節
  • 文脈依存の文字処理を宣言的に記述できる

通常の正規表現ではこのような条件を記述する場合、手動でのパターン列挙が必要となるが、本DSLでは宣言的に記述できる。

実際にDSLから毎回regexへ変換すると処理が嵩む為、Regex_Khhylからコンパイルされた正規表現データを用いて文字列加工・GPOS処理を行っている。

処理フロー

API

get /thaqqa/render/
?sentence=highirith, kako karutaraa qahhowi uzhute kaghangetlaa. \nittlutu kadha, eqhoghi reewe qarhilas.
&font=Default
&lang=khehyelu
&dir=vertical"

このようにすると、描画されたSVGのパスデータが文字列として返ってくる。

#(長いため、一部省略。)
  resp_body: "M 189.79999999999998,331.71436874000005 m 0.0,0.8 m 0.0,0.0 m 0.0,-2.24015624 m 1.28,-0.1875 c -0.5511536560000001,7.360000000034007e-7 -1.096401504,0.287206248 -1.3951562480000002,0.519531248 l 0.230312496,0.2959375040000001 c 0.23268152800000003,-0.18094352 0.7543887920000001,-0.439398712 1.162968792,-0.44015624799999997" <> ...,

不正な文字列であったり、内部エラーを伴う場合は適当なHTTPレスポンスコードと共にエラーを返す。

詳細な仕様はまだ検討の段階。
編集APIとしてはJWTによるトークン認証を用いたものとする予定。

今後の展望

  • APIを用いたフロントエンドアプリの開発。
  • DSLの最適化。
    • 特に、DataTable連携時の探索コスト削減並びに正規表現の肥大化の抑制。
  • スタイル・書体の部分適用
    • 文字構成要素単位でのスタイル変更。
    • この場合はPathデータをスタイル毎に分離して返却する設計とする。
  • プロジェクトが肥大化した場合に備えた、認証・保存機構の強化
    -JWT認証からの拡張(パスワード制かIP制限が候補)
    -ファイル保存時の衝突回避システム
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?