Skip to main content

mojivs

CI Python License: MIT

日本語 | English

指定した枠にテキストをぴったり充填する日本語レンダラ(IVS / 異体字対応)

同じ「枠にフィット」を、広い枠・ちょうどの枠・狭い枠にそれぞれ枠いっぱいまで充填した例

このライブラリの目的は、指定した枠に文字をきれいに埋めることです。 render_to_box に文字列と (幅, 高さ) のピクセル矩形を渡すだけで、その枠ちょうどに収めた画像を返します。 高さは枠に合わせて自動でスケールし、幅は — テキストが広ければ字を横に圧縮し、狭ければ 字間を均等に配って — 枠を隙間なく充填します。バナー・ボタン・サムネイル・帯など「決まった 大きさの箱に、文字をはみ出さず・余白も残さず流し込みたい」用途に向いています。

from mojivs import IVSFont

font = IVSFont("Gothic.otf")
# 文字列と (幅, 高さ) を渡すだけ。返る画像はちょうどそのサイズ
font.render_to_box("枠にフィット", (400, 80)).save("boxed.png")

そして、枠に流し込む中身が日本語である以上、字が正しい字形で出ることも同じくらい 大切です。mojivs は IVS(Ideographic Variation Sequence / 異体字) と SVS(Standardized Variation Sequence / 標準異体字) を、フォント自身の cmap format-14(UVS)サブテーブルから解決します。

辻+VS17 の描画比較:Pillow(BASIC) は無視、Pillow+libraqm と mojivs は正しい字形

これは OpenType 標準の異体字格納方式なので、Adobe-Japan1 に限らず、Hanyo-Denshi / Moji_Joho など他の IVD コレクションや、CID-keyed / name-keyed いずれのフォントにも 対応します(フォントが format-14 を持たない場合は Adobe-Japan1 IVD へフォールバック)。 Pillow の ImageFont は既定(BASIC レイアウト)では異体字セレクタを無視します (libraqm/HarfBuzz を入れれば描けます)。mojivs は libraqm/HarfBuzz なしで、fonttools だけで IVS/SVS を解決します。 たとえば「辻」「葛」「髙」「﨑」「鯛」などの異体字を、 正しい字形で枠に充填できます。

辻󠄀  辻󠄁      ← 同じ「辻」でも異体字セレクタで字形(一点しんにょう/二点しんにょう)が変わる

特徴

  • 枠にぴったり充填(render_to_box) — 指定した (幅, 高さ) のピクセル矩形ちょうどに1行を収める。広ければ字を圧縮、狭ければ字間を均等配分して、はみ出しも余白も残さず枠を埋める
  • IVS / SVS を字形に反映 — フォント自身の cmap format-14(UVS)から解決。Adobe-Japan1 に限らず任意のフォントに対応(未搭載時は Adobe-Japan1 IVD にフォールバック)
  • フォントは一度だけ解析 — cmap・グリフ集合・メトリクスをキャッシュし、描画のたびに再構築しない
  • 横書き・縦書き・複数行 — \n で行/列を分割、縦書きは vmtx/VORG と縦書き用約物に対応
  • 欧文回転・縦中横 — 縦書きで欧文/数字を90°回転(orientation)、桁数字を正立横組みに(tate_chu_yoko)
  • アウトライン(縁取り)・背景色・文字間隔・行間・揃えに対応
  • PNG / SVG / PDF 出力 — 同一のレイアウトエンジンから複数フォーマットへ
  • 選べるバックエンド — 既定は依存ゼロの純Python版 builtin。高速化したいときは backend="cairo" / backend="freetype"(縁取り時は自動で cairo にフォールバック)
  • 依存がとても少ない — コアは fonttools / pillow の2つだけ。pip install mojivs だけで既定の描画が動きます。pycairo / freetype-py(+ numpy)は速度と縁取りのための任意アクセラレータ、PDF 出力の reportlab も任意 extra

出力サンプル

指定ピクセルの矩形にフィット(render_to_box) — 枠いっぱいに字間を均等配分

箱にフィット

縁取り(stroke)+背景

縁取り付きテキスト

縦書き+縦中横(tate-chu-yoko)

縦書きと縦中横

インストール

追加の extra なしでそのまま動きます(fonttools と pillow だけを取得)。

pip install mojivs                    # 既定の builtin バックエンドで描画OK(システムライブラリ不要)

必要に応じて任意アクセラレータを足します。

pip install mojivs[cairo]             # cairo バックエンド(縁取り stroke に必須)
pip install mojivs[freetype]          # FreeType バックエンド(最速の塗り・numpy 同梱)
pip install mojivs[cairo,freetype]    # 両方

uv でも同じパッケージをそのまま利用できます(PyPI から取得)。

uv pip install mojivs                 # 既存の環境へインストール
uv add "mojivs[cairo]"                # uv 管理プロジェクトに追加(cairo も欲しいとき)
  • builtin(既定) — 純Python + Pillow のラスタライザ。C 拡張もシステムライブラリも不要で、pip install mojivs だけで塗り+背景を描画できます。cairo/freetype と出力はほぼ同一です。

  • cairo — 縁取り(stroke)に必須で、塗りも高速化します。pycairo はネイティブの cairo ライブラリを必要とします:

    # macOS
    brew install cairo pkg-config
    # Debian / Ubuntu
    sudo apt-get install libcairo2-dev pkg-config
    
  • freetype — 最速の塗り経路。freetype-py は自己完結した wheel を配布するため、システムの cairo ライブラリは不要です(合成に使う numpy も一緒に入ります)。

縁取り(stroke)は cairo だけが描けるため、backend="builtin" / "freetype" でも stroke 指定時は自動で cairo にフォールバックします。この場合は mojivs[cairo] が必要です。

# 開発版(両バックエンド+テスト/型チェック)
pip install -e ".[dev]"

使い方

from mojivs import IVSFont

font = IVSFont("fonts/HaranoAjiFonts-master/HaranoAjiGothic-Medium.otf")

# 1. 指定ピクセルの矩形にぴったり充填(このライブラリの主役)
#    返る画像はちょうど (幅, 高さ)。高さは枠に合わせてスケール。
font.render_to_box("辻鯛テ体", (400, 80), color="#000").save("boxed.png")
#    広い枠 → 字間を均等に配って充填。狭い枠 → 字を横に圧縮して収める。
font.render_to_box("辻鯛テ体", (640, 80)).save("boxed_wide.png")   # 均等配分
font.render_to_box("辻鯛テ体", (200, 80)).save("boxed_narrow.png") # 圧縮
#    縁取り・背景も指定できる(縁取りぶんは枠の内側に収める)
font.render_to_box(
    "SALE 50%", (480, 120), color="#fff", stroke="#e5484d", stroke_width=6, background="#1a1a1a"
).save("boxed_banner.png")

# 2. フォント本来のメトリクスで素直に描画(em サイズ指定・枠に収めない)
img = font.render("辻\U000e0100鯛", size=96, color="#1a1a1a")
img.save("out.png")

# 3. 縁取り付き
font.render(
    "異体字レンダリング",
    size=96,
    color="#ffffff",
    stroke="#e5484d",
    stroke_width=6,
    background="#1a1a1a",
).save("outlined.png")

# 4. 複数行(\n)・揃え(align)
font.render("異体字\nレンダリング", size=64, align="center").save("multiline.png")

# 5. 縦書き(\n で改列、右の列から左へ。約物は縦書き用字形に置換)
font.render("辻\U000e0100鯛の\n「縦書き」。", size=72, direction="vertical").save("vertical.png")

# 5b. 縦書きの欧文回転(既定 orientation="mixed":欧文/数字は90°回転、漢字仮名は正立)
font.render("縦書きABC\n令和6年です", size=64, direction="vertical").save("mixed.png")

# 5c. 縦中横(桁数字を正立の横組みで1マスに。tate_chu_yoko=最大桁数)
font.render("平成31年\n5月1日", size=64, direction="vertical", tate_chu_yoko=2).save("tcy.png")

# 6. バックエンド選択(既定は依存ゼロの builtin。速くしたいときだけ切り替え)
#     出力は3バックエンドでほぼ同一。縁取り指定時は自動で cairo にフォールバックします。
font.render("高速レンダリング", size=96, backend="freetype").save("ft.png")  # 要 mojivs[freetype]

# ベクター出力(SVG は依存追加なし。PDF は reportlab が必要)
open("out.svg", "w").write(font.to_svg("辻\U000e0100鯛", size=96))
font.to_pdf("辻\U000e0100鯛", "out.pdf", size=96)     # pip install mojivs[pdf]

# 描画せずに対応状況を確認
font.supports("辻鯛テ体")   # -> True
font.missing("辻鯛𠮷")       # -> 未対応クラスタのリスト

\U000e0100 は異体字セレクタ VS17(U+E0100)です。文字列にそのまま含めます。

枠にフィットさせる(render_to_box)

このライブラリの主役です。render_to_box(text, (幅, 高さ)) は、テキスト1行を指定した ピクセル矩形ちょうどに収めた画像を返します(返り値のサイズは常に (幅, 高さ))。

  • 高さ — フォントの行の高さを枠の高さに合わせて等比スケールします。
  • 幅 — スケール後の自然な幅が…
    • 枠より広いときは、各字を横方向に圧縮して枠内に収めます(縦横比は詰まります)。
    • 枠より狭いときは、余白を字間として均等に配分し、枠を左右いっぱいまで埋めます。
  • 縁取り(stroke) — 縁取りの太さぶんは枠の内側に確保するので、線が枠からはみ出しません。

そのため、フォントサイズを都度計算しなくても「決まった箱に文字を隙間なく流し込む」用途に そのまま使えます。横書き1行専用です(縦書き・複数行・自動改行は render / shape を使用)。

# 高さ80pxに固定し、幅を変えるだけで詰め方が変わる
font.render_to_box("在庫僅少", (600, 80))   # 広い → 字間を均等配分
font.render_to_box("在庫僅少", (220, 80))   # 狭い → 字を圧縮

コマンドライン(CLI)

インストールすると mojivs コマンドが使えます(python -m mojivs でも同じ)。

# 異体字セレクタは \U... で指定(--escape で解釈)
mojivs render '辻\U000e0100鯛' --font Gothic.otf --escape -o out.png --size 96

# 縦書き+縦中横。SVG は拡張子で自動判定
mojivs render '平成31年' --font Gothic.otf --direction vertical --tate-chu-yoko 2 -o out.svg

# フォントの対応状況を確認
mojivs supports '辻鯛テ体' --font Gothic.otf   # -> true
mojivs missing  '辻鯛𠮷'   --font Gothic.otf   # 未対応クラスタを1行ずつ出力

主なオプション: --size --color --stroke --stroke-width --background --direction --align --orientation --tate-chu-yoko --backend(builtin|cairo|freetype) --on-missing(raise|skip)。詳細は mojivs render --help。

出力フォーマットとレイアウト

レイアウトは shape() が計算し、PNG/SVG/PDF はその結果を各フォーマットに描画します。 render / to_svg / to_pdf は共通のレイアウト引数 (size・direction・align・line_spacing・letter_spacing・padding・ orientation・tate_chu_yoko)と スタイル引数(color・stroke・stroke_width・background)を受け付けます。

  • orientation(縦書き時): "mixed"(既定・欧文/数字を90°回転)|"upright"(すべて正立)
  • tate_chu_yoko(縦書き時): 0 で無効。N で連続する半角数字を最大 N 桁ずつ正立の横組み(縦中横)に。
  • backend(render / render_to_box のみ): "builtin"(既定・依存ゼロの純Python)|"cairo"|"freetype"。ラスタライズ方式の選択で、レイアウト結果は変わりません(SVG/PDF は対象外)。
from mojivs import IVSFont

font = IVSFont("...otf")
shaped = font.shape("辻\U000e0100鯛\nテ体", size=64, align="center")
shaped.width, shaped.height          # ピクセルサイズ
shaped.glyphs                        # 配置済みグリフ(PlacedGlyph)の列

色の指定

color / stroke / background は次を受け付けます。

  • 16進文字列: "#000", "#1a1a1a", "#1a1a1aff"
  • 0–255 のタプル: (26, 26, 26) または (26, 26, 26, 255)
  • None(stroke / background の既定。縁取り・背景なし)

API

IVSFont(font, *, font_number=0)

フォントを 1 度だけ読み込み、以降のルックアップをキャッシュします。 font はパスまたは fontTools.ttLib.TTFont。

共通のレイアウト引数: size / direction("horizontal"|"vertical")/ align("start"|"center"|"end")/ line_spacing / letter_spacing / padding / orientation("mixed"|"upright")/ tate_chu_yoko(int)。 共通のスタイル引数: color / stroke / stroke_width / background。 ラスタライズ系(render / render_to_box)は追加で backend("builtin" 既定 | "cairo" | "freetype")を受け付けます。

メソッド 説明
render(text, *, ...) -> Image 自然なメトリクスで RGBA 画像を返す
render_to_box(text, box, *, ...) -> Image box=(幅, 高さ) ちょうどの画像に収める(横1行)
shape(text, *, ...) -> ShapedText 配置済みグリフ(PlacedGlyph)とサイズを返す
to_svg(text, *, ...) -> str SVG 文書文字列を返す
to_pdf(text, path, *, ...) -> None 1ページの PDF を書き出す(reportlab 必須)
supports(text) -> bool 全クラスタを描画できるか
missing(text) -> list[str] 描画できないクラスタを順に返す
glyph_name(base, selectors=None) -> str | None 基底文字+セレクタのグリフ名を解決

on_missing は "raise"(既定・UnsupportedCharacterError)または "skip"。

mojivs.ivs(描画に依存しない解決レイヤ)

cairo/Pillow を使わず、テキストのクラスタ分割(IVS/SVS セレクタ対応)と、Adobe-Japan1 IVD フォールバック解決だけを行う純粋なレイヤです。字形の一次解決はフォントの format-14 を読む font.py が担います。

from mojivs import ivs

ivs.is_variation_selector("\U000e0100")   # -> True (SVS の U+FE00–FE0F も True)
ivs.cid_glyph_name("辻", ["\U000e0100"])   # -> "cid03056"(Adobe-Japan1 フォールバック)
list(ivs.iter_clusters("A辻\U000e0100"))   # -> [('A', []), ('辻', ['\U000e0100'])]

アーキテクチャ

依存の軽い解決レイヤの上に、レイアウト → 各フォーマット出力を載せる構成です。

mojivs/
├─ ivs.py      … クラスタ分割(IVS/SVS)+ Adobe-Japan1 IVD フォールバック(標準ライブラリのみ)
├─ font.py     … IVSFont:フォント読み込み・cmap format-14(UVS) による字形解決・キャッシュ
├─ shaping.py  … shape():テキスト → 配置済みグリフ(横/縦/複数行)
├─ render.py   … バックエンド振り分け+ cairo によるラスタライズ(PNG)
├─ render_builtin.py … 既定バックエンド(純Python+Pillow・追加依存なし。backend="builtin")
├─ render_ft.py … FreeType バックエンド(任意・freetype-py+numpy。backend="freetype")
├─ export.py   … SVG(fonttools)/ PDF(reportlab・任意)出力
├─ colors.py   … 色のパース
└─ data/ivd.txt … Unicode IVD(同梱データ)

shape() が唯一のレイアウトエンジンで、PNG/SVG/PDF はいずれもその結果を描画します。

サンプルフォント

fonts/ にはテスト・サンプル用として Harano Aji Fonts(Adobe-Japan1-7 対応、独自 ライセンス)の HaranoAjiGothic-Medium.otf 1 面のみを同梱しています。他のウェイト や明朝は下記から取得してください(リポジトリ肥大化を避けるため追跡していません)。 https://github.com/trueroad/HaranoAjiFonts

開発

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

pytest                       # テスト
ruff check src tests         # lint(未使用importや実バグを検出)
ruff format src tests        # 整形
pyright                      # 型チェック

python examples/basic.py     # examples/ に PNG を出力

lint/型チェックの設定は pyproject.toml の [tool.ruff] / [tool.pyright] にあります。 型スタブを持たない C 拡張(pycairo・reportlab)向けに pyright は basic モードで、 「スタブが無い」系の誤検知を抑制しています。CI(GitHub Actions)でも同じ4つを実行します。

制限事項・今後

  • 縦書きの欧文回転/正立は Unicode 範囲によるヒューリスティック(UAX #50 準拠の近似)で判定します。厳密な字ごとの縦書き字形指定には未対応。
  • tate_chu_yoko は半角 ASCII 数字の連続のみを対象とします(No. 等の混在は対象外)。
  • 異体字はフォントの cmap format-14(UVS)から解決するため、format-14 を持つフォントなら Adobe-Japan1・Hanyo-Denshi・Moji_Joho など任意のコレクションと SVS に対応。ただし format-14 を持たず、かつ Adobe-Japan1 CID 命名でもないフォントでは異体字を解決できません(また当然、フォントに該当グリフが無ければ描けません)。
  • 自動改行(ワードラップ)は未対応。\n による明示的な改行/改列のみ。
  • render_to_box は横書き 1 行のみ対応。

ライセンス

コードは MIT。同梱データ・フォントのライセンスは LICENSE を参照。

Metadata

Release files for mojivs 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mojivs 0.4.0
File Size Uploaded
mojivs-0.4.0.tar.gz 293.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mojivs 0.4.0
File Interpreter ABI Platform
mojivs-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 566.2 kB

Release files / mojivs-0.4.0.tar.gz

Download URL mojivs-0.4.0.tar.gz
Size 293.4 kB
Tags Source
SHA-256 checksum
How to use checksums
48caa404f720efc1838bc0c2ef5171efb85c96d748d1c93214822f96157d485d
BLAKE2b-256 checksum
How to use checksums
cceb836a69c468de9d5b2ce188d9697131278b13f9e6b096c162c77f42560c47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / mojivs-0.4.0-py3-none-any.whl

Download URL mojivs-0.4.0-py3-none-any.whl
Size 272.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
505cd7880e568f487e1566155d9b959b157d01b0ca177230a6b0d2aa2d2ff377
BLAKE2b-256 checksum
How to use checksums
1e307c7e3249927d57a78ba4b540c291529eb5c27b269f437aaf8d6aab0ff573
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page