チュートリアル — 最初の1枚から本番リリースまで
このページは、PDFを1枚出すところから、テンプレートをDockerイメージに梱包して本番に載せるところまでを一直線に進みます。コマンドは全部、CIで実際に実行されているものの転記です。
1. まず1枚出す(Docker)
イメージにはCLI・MCPサーバ・フォント/ロケールパック・全同梱例が入っています。
docker run --rm ghcr.io/kengos/shojiku:edge > receipt.pdfテンプレートを手元に取り出して編集し、プレビューを見る手順はクイックスタートにあります。編集→検証→プレビューのループはそちらが本編です。
2. アプリに組み込む(公開レジストリのSDK)
5言語のパッケージが公開済みです。どのSDKも同じ形をしています:クライアントを作り、テンプレート名とパラメータを渡し、返ってきたバイト列を書き出す。エラーは例外ではなく、failure の種別とメッセージで返ります。
pip install shojikugem install shojikunpm install shojikudotnet add package Shojiku<!-- 本体 + 実行プラットフォームのclassifier(Netty/LWJGLと同じ流儀) -->
<dependency>
<groupId>jp.kengos</groupId>
<artifactId>shojiku</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>jp.kengos</groupId>
<artifactId>shojiku</artifactId>
<version>0.1.0</version>
<classifier>linux-x64</classifier>
</dependency>レンダリングは全言語で同じ3行です。Pythonなら:
import json, shojiku
client = shojiku.Client(
templates="templates/", font_dirs=["packs/fonts"], locale_dirs=["packs/locale"]
)
result = client.generate("receipt-ja", json.load(open("params.json")))
if not result.success:
raise SystemExit(f"{result.failure.kind} | {result.failure.message}")
open("out.pdf", "wb").write(result.artifact.bytes)フォントとロケールのパックはリポジトリの packs/ をアプリに同梱します。使うロケールのパックだけに絞って構いません。
3. ロゴ画像と自社フォントを足す
実務のテンプレートで最初に必要になるのは、この二つです。
画像はテンプレートの隣に置いて src で参照します。基準ディレクトリはテンプレートファイルのある場所(CLIなら --assets-dir で変更可)で、data: URIやインラインSVG、paramsから差し込む動的画像も使えます。正確な仕様はimage.mdへ。
- type: image
box: { w: 120, h: 40 }
src: assets/logo.svgフォントはパックです。フォントファイルと manifest.yml(ライセンス1つ + 顔ごとのsha256)を packs/fonts/<id>/ に置き、ロケールのオーバーレイで uses に足します。読み込み時にsha256と埋め込み権利(fsType)が検証されます。正確な仕様はfonts.mdへ。
# packs/fonts/my-corporate/manifest.yml
version: 1
license: Proprietary
redistributable: false
faces:
- id: my-corporate
file: MyCorporate-Regular.ttf
sha256: <sha256sum の出力># packs/locale/ja-jp.yml(ビルトインja-JPへのオーバーレイ)
fonts:
uses: [biz-ud, ipamj-mincho, noto-sans-mono, my-corporate]これで fontFamily: my-corporate が使えます。uses は全体を書き直す点(追記ではない)と、ロケールが uses していないパックの fontFamily は黙ってフォールバックする点に注意。下のDockerfileレシピは packs/ を丸ごとCOPYするので、自作パックも同じ行に乗ります。
4. 本番に載せる(Dockerfileレシピ)
テンプレートができたら、アプリ・テンプレート・パックを1つのイメージに焼き込みます。以下は5言語ぶんの実レシピで、make proof-deploy が公開レジストリに対して実際にビルド&レンダリングして検証しているファイルそのものです。
# Production shape: your app + its template + the packs, rendered by the
# shojiku wheel from PyPI. The params come out of a SQLite database built
# into the image (seed.py) — swap that for your real database connection.
#
# docker build -t receipt-renderer .
# docker run --rm receipt-renderer > receipt.pdf
FROM python:3.12-slim-bookworm
RUN pip install --no-cache-dir shojiku
WORKDIR /app
# Vendor the template set and the font/locale packs your documents use
# (packs/ comes from the Shojiku repository; trim it to your locale's packs).
COPY templates/ templates/
COPY packs/ packs/
COPY params-base.json seed.py render.py ./
RUN python seed.py
CMD ["python", "render.py"]# Production shape: your app + its template + the packs, rendered by the
# platform gem from RubyGems.
#
# docker build -t receipt-renderer .
# docker run --rm receipt-renderer > receipt.pdf
FROM ruby:3.3-slim-bookworm
RUN gem install -N shojiku
WORKDIR /app
COPY templates/ templates/
COPY packs/ packs/
COPY render.rb ./
CMD ["ruby", "render.rb"]# Production shape: your app + its template + the packs, rendered by the
# shojiku npm package (napi addon resolved per platform).
#
# docker build -t receipt-renderer .
# docker run --rm receipt-renderer > receipt.pdf
FROM node:22-bookworm-slim
WORKDIR /app
RUN npm init -y >/dev/null && npm install --no-audit --no-fund shojiku
COPY templates/ templates/
COPY packs/ packs/
COPY render.mjs ./
CMD ["node", "render.mjs"]# Production shape: publish a trimmed console app with the Shojiku NuGet
# package (native engine per RID under runtimes/), then run it on the
# runtime-only image.
#
# docker build -t receipt-renderer .
# docker run --rm receipt-renderer > receipt.pdf
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY Renderer.csproj Program.cs ./
RUN dotnet publish -c Release -o /out
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /out ./
COPY templates/ templates/
COPY packs/ packs/
CMD ["dotnet", "Renderer.dll"]# Production shape: a Maven build with the Shojiku jar + the PLATFORM
# CLASSIFIER jar (the one deliberate line — Maven never resolves a
# classifier on its own; pick the classifier of the image you run on).
#
# docker build -t receipt-renderer .
# docker run --rm receipt-renderer > receipt.pdf
FROM maven:3-eclipse-temurin-21 AS build
WORKDIR /src
COPY pom.xml .
COPY src/ src/
RUN mvn -q package dependency:copy-dependencies -DoutputDirectory=/deps
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /src/target/renderer-1.jar app.jar
COPY --from=build /deps/ deps/
COPY templates/ templates/
COPY packs/ packs/
CMD ["java", "-cp", "app.jar:deps/*", "Render"]Pythonのレシピは一歩進めて、paramsをイメージ内のSQLiteから引いています。静的な事実(発行者ブロックやQR)はテンプレート側のparamsに置き、取引の行だけをDBから合成する形です:
"""The production shape: static document facts (issuer block, QR) stay in
the vendored base params; the transactional half (header + lines + totals)
comes out of SQLite. Writes the PDF to stdout."""
import json
import sqlite3
import sys
import shojiku
con = sqlite3.connect("params.db")
head = con.execute("select number, issued_at, recipient, purpose from receipt").fetchone()
lines = con.execute("select name, quantity, unit_price from line").fetchall()
params = json.load(open("params-base.json"))
items = [
{"name": n, "quantity": q, "unit_price": p, "amount": q * p} for (n, q, p) in lines
]
total_ex = sum(i["amount"] for i in items)
tax = total_ex // 10
params.update(
{
"receipt": {"number": head[0], "issued_at": head[1]},
"recipient": {"name": head[2]},
"purpose": head[3],
"items": items,
"amount": {"total_in_tax": total_ex + tax, "total_ex_tax": total_ex, "tax": tax},
}
)
client = shojiku.Client(
templates="templates/", font_dirs=["packs/fonts"], locale_dirs=["packs/locale"]
)
result = client.generate("receipt-ja", params)
if not result.success:
raise SystemExit(f"render failed: {result.failure.kind} | {result.failure.message}")
sys.stdout.buffer.write(result.artifact.bytes)5. 署名して、検証する
配る前に署名し、受け取った側が検証します。どちらもネットワークを使いません。パスフレーズを引数で渡すフラグは意図的にありません(argv は他プロセスから読めるため)。
shojiku sign --input out.pdf --key signer.pem --cert signer.crt --output signed.pdf
shojiku verify --input signed.pdf --anchor signer.crtverify は署名が実際に覆うバイト範囲を含むJSONレポートを出力し、文書が検証できなければ非ゼロで終了します。信頼するcertは --anchor で毎回明示します。マシンの証明書ストアは参照しません。