typeset は、縦書き・横書きの日本語組版を 1 つのエンジンで行い、同じ組版結果を PDF / SVG / PNG に出す
C++17 ライブラリです。Python パッケージ jtypeset から使え、Markdown を書いて日本語の PDF を作る
コマンド jtypeset-md が付いています。
- 日本語組版の基本(JLReq)に沿った約物の詰め・禁則・追い込み/追い出し・ぶら下げ・両端揃え
- ルビ(グループ/モノ/熟語)・縦中横・圏点・割注・字取り
- 見出しの採番と PDF のしおり、目次、索引、図表番号と相互参照、脚注、箇条書き、コードブロック、表(ページまたぎ・rowspan)、 画像の回り込み、段組と段抜き、柱・ノンブル
- LaTeX 数式(同梱の MicroTeX。数式フォントのグリフとして PDF に埋め込む)と、グラフなど外部レンダラの出力(SVG)の差し込み
- 文字の装飾(下線・打消し線・影・二重縁取り・グラデーション)、欧文のハイフネーション、双方向テキスト(アラビア文字・ヘブライ文字)
- フォントはファイルから直接読み、PDF にはサブセットで埋め込む(埋め込み許可 fsType を確認)。カラー絵文字(COLR / ビットマップ)も PDF / SVG / PNG で同じ色に
- ゲーム向けのタグ記法(
<b><ruby><color><outline><link>…)と、リンク・ヒットテスト・キャレット・段階表示のための取り出し口
想定している用途は台本・小説・技術文書・レポート、それにゲームのテキスト表示などのツール作りです。詳しい説明は ドキュメントサイト にあります(Markdown → PDF の手引き、C++ / Python のリファレンス)。
pip install jtypeset # 本体(Windows / Linux / macOS の CPython 3.9〜3.13 の wheel)
pip install "jtypeset[md]" # Markdown → PDF(markdown-it-py など)
pip install "jtypeset[md,highlight]" # + Pygments(コードブロックの色付け。無くても単色で組める)jtypeset-md report.md # → report.pdf
jtypeset-md novel.md --vertical --paper A5 --font fonts/mincho.otf # 縦組み・A5・フォント指定
jtypeset-md report.md --toc --png 120 # 先頭に目次、各ページの PNG も出すMarkdown の先頭に YAML front matter を書くと、判型・書字方向・フォント・目次・柱/ノンブル・数式ハンドラなどを指定できます。
---
title: 組版の手引き
author: 組版基盤チーム
paper: A5
writing: vertical
toc: true
---
# はじめに {#sec-intro}
本文。ルビは|組版《くみはん》のように書ける[^1]。第 {ref:sec-body} 章を参照。
[^1]: 脚注は段の末尾に集まる。見出し・箇条書き・表・画像・脚注・数式($…$)・ルビ・<!-- pagebreak -->・[toc]・[index] に対応しています。
一覧と例は Markdown → PDF の手引き と
samples/markdown/report.md を参照してください。
フォントを指定しなければ、カレントの data/ の Noto → OS のフォント(Windows: 游明朝/游ゴシック、macOS: ヒラギノ、Linux: Noto CJK)の順に探します。
import jtypeset as ts
fonts = ts.FontSet()
fonts.load_file("NotoSerifJP-Regular.otf", "serif-ja")
body = ts.TextStyle(["serif-ja"], 11.0)
seq = ts.PageSequence()
seq.master.size = ts.paper.A5
seq.master.writing_mode = ts.WritingMode.VERTICAL_RL
flow = ts.Flow()
p = ts.Paragraph("吾輩は猫である。名前はまだ無い。", body)
p.annotate(ts.Annotation.ruby(0, 2, "わがはい"))
flow.add_paragraph(p)
pages = ts.FlowLayouter(fonts).layout(flow, seq)
ts.save_pdf(pages, "neko.pdf")
pages[0].save_png("neko_p1.png", dpi=144)段落・見出し・表・画像・目次・索引・脚注・外部オブジェクトの使い方は
Python ガイド と、help(jtypeset) / 型スタブ(IDE の補完)を参照してください。
依存は vcpkg で入れます(FreeType / HarfBuzz(subset 込み)/ libunibreak / zlib / stb / doctest / tinyxml2)。
フォント層の glyphware は CMake の FetchContent で取得します(開発中は GLYPHWARE_DIR でローカルツリーを指せます)。
export VCPKG_ROOT=/path/to/vcpkg
make fontdata # テスト・サンプル用の Noto フォント(絵文字含む)を data/ にダウンロード
make prebuild # cmake --preset(Windows: x64-windows。Linux / macOS の preset もある)
make build # cmake --build
make test # ctest(doctest)
./build/x64-windows/Release/sample_report.exe # サンプルはリポジトリルートで実行(./data/ を読む)- Python バインディングも一緒にビルド:
make prebuild CMAKEOPT=-DTYPESET_BUILD_PYTHON=ON(pip install pybind11が必要)。 ビルドツリーのbuild/<preset>/python/ReleaseをPYTHONPATHに足すとimport jtypesetできます - wheel:
pip wheel . -w dist --no-deps(scikit-build-core。VCPKG_ROOTがあれば toolchain は自動で補います) - MicroTeX(LaTeX 数式)は既定でビルドされ Python 拡張にも入る(
-DTYPESET_HANDLER_MICROTEX=OFFで外せる) - リファレンス:
make docs(Doxygen)、make pydocs(型スタブ+ pdoc)、make site(MkDocs でドキュメントサイトをbuild/siteに)
サンプル(make samples でまとめて実行): sample_dl(表示リスト直書き)、sample_inline(縦横同一文)、
sample_script(台本)、sample_novel(小説 2 段)、sample_tech(技術文書)、
sample_report(レポート: 目次・採番・図表番号・脚注・索引)、sample_objects(外部オブジェクト)、
sample_text_style(装飾とフォント)、sample_game(タグ記法と取り出し口)、sample_intl(多言語・ハイフネーション)。
出力 output_*.png/.pdf/.svg はリポジトリルートに出ます。
一覧と画像は サンプル、詳細は ビルド。
Flow(段落・見出し・表・画像・目次・索引…)
→ page : ページマスタ・段・流し込み・回り込み・柱・ノンブル・改ページ制御・多パス(採番・参照・目次・索引)
→ inl : Itemizer → HarfBuzz → Box / Glue / Penalty(JLReq)→ 行分割(Greedy / Knuth–Plass)→ 行
→ dl : 表示リスト(GlyphRun / Path / Rect / Image / Group)
→ backend : ラスタ(PNG)/ PDF(Identity-H、サブセット、Flate、しおり)/ SVG(defs + use)
obj : 外部オブジェクト(数式・グラフ)の差し込み口。関数/外部コマンド、SVG 読み込み
- 1 つのエンジンで縦横: 行内を論理座標(送り方向と、行の中心線からのずれ)で組み、最後に 1 箇所で物理座標に写す。 縦組みの正立/横倒し(UAX #50)、ルビや圏点の付く側(縦: 右、横: 上)もここで決まる
- Box / Glue / Penalty: 和文を「1 文字 1 マス」ではなく TeX と同じ箱・のり・ペナルティの列として解く。 JLReq の文字クラスとアキ量表から Glue を作り、禁則は Penalty、ぶら下げは幅が負の Penalty、 追い出しは和字間の伸びで表す。Greedy と Knuth–Plass を選べ、行長は行ごとに変えられる(回り込み・吹き出し)
- 表示リストで分離: 組版結果は描画命令の列になり、3 つの出力先が同じものを描く。 グリフ変形(回転・平体長体・斜体)の組み立ても 1 箇所。Inkscape / poppler で描き直して一致を確認している
- ページ組版: 段落を段に入る行数だけ組んで残りを次の段へ続ける。orphans / widows、keepWithNext(見出しの巻き取り)、 段抜きと段の高さ揃えは「ページの再開点から段を縮めて組み直す」試行で解く。見出し番号・図表番号・相互参照・目次・ 索引・総ページ数は 2〜3 パスで安定させる
- フォント: glyphware(FreeType + HarfBuzz)。フォントファイルのバイト列から HarfBuzz のフォントを自前に作り、 フォントユニットで位置を受け取る。PDF は hb-subset でサブセット化し、OS/2 の fsType(埋め込み許可)を確認する。 minikin と ICU は使わない(UAX #14 は libunibreak、双方向は SheenBidi)
続きは 技術解説 と C++ ガイド。
経緯・判断は 検討.md / 設計.md / 実装.md に記録しています。
MIT License(LICENSE)。依存: FreeType(FTL)、HarfBuzz(MIT)、SheenBidi(Apache-2.0、glyphware 経由)、
libunibreak(zlib)、zlib、stb(MIT / PD)、doctest(MIT、テストのみ)。Python の任意依存: markdown-it-py(MIT)、
mdit-py-plugins(MIT)、PyYAML(MIT)、Pygments(BSD)。MicroTeX ハンドラは MIT(付属フォントは OFL / Knuth ライセンス)。