テンプレートのドキュメント

テンプレートは 1 つの JSON オブジェクトです。ページと、再利用できるスタイルの一覧、そして平坦なノードの一覧を記述します。ノードの一覧に入れ子はありません — ノードは id で親を指します。

以下の項目はすべて必須です。ノードの props はすべてのノード種別の props を合わせたものなので、その種別が使わないキーもノードは持っている必要があります。使わないものは空にしてください: 文字列は ""、数値は 0、真偽値は false、配列は [] です。キーを取り除くのはスキーマ違反ですが、空にするのは違反ではありません。

最上位

v integer

スキーマのバージョン。現在は 1 です。これがないドキュメントは、推測せずに拒否します。

unit const

座標の単位。常に 96 dpi の "px" です。つまり 794 × 1123 が A4 1 ページです。

px

lang enum

ドキュメントの言語。CJK の字形を選ぶために使います。省略すると文字から推測しますが、これはかなやハングルが現れる場合に有効です。日本語の本文がすべて漢字で書かれているときは明示してください — 請求書のような見出しは中国語としても成り立つため、推測が外れて中国語の字形で印刷されます。"ko"・"ja"・"zh-Hans"・"zh-Hant" を受け付けます。

kojazh-Hanszh-Hant

page object

用紙・余白・全ページ共通のヘッダーとフッター。

fonts array

このドキュメントが必要とするフォント。空の配列にすると、組み込みのものを使います。

styles array

再利用できる文字とボックスのスタイル。ノードは id で参照します。

nodes array

ドキュメント内のすべてのノードを、1 つの平坦な配列として持ちます。入れ子は parent で表します。

page

size enum

用紙サイズ。a4・a5・letter・legal のいずれかで、それ以外は拒否します。96 dpi では A4 794×1123、A5 559×794、Letter 816×1056、Legal 816×1344 px です。省略すると a4 になります。

a4a5letterlegal

orientation enum

用紙の向き。size のあとに適用されるので、a5 + landscape は横長の A5 です。

portraitlandscape

margin object

印刷可能な余白(px)。ノードの座標は、その内側から始まります。

margin.t number

上の余白。

margin.r number

右の余白。

margin.b number

下の余白。フッターの場所を空けるため、通常は上より大きめにします。

margin.l number

左の余白。

header string

全ページの上部に繰り返すテキスト。空文字列にするとヘッダーなしです。{{ … }} が使えます。

footer string

全ページの下部に繰り返すテキスト。空文字列にするとフッターなしです。{{ … }} に加えて [[page]] と [[pages]] が使えます。

backgrounds array

背景画像。全ページに敷かれます。3 枚までで、使わない場合は空の配列のままにしてください。座標がノードとは異なる点にご注意ください: 背景は用紙全体を基準に置かれ、ノードは余白の内側に置かれます。だからこそ、紙の端から端まで覆う用紙を敷くことができます。

backgrounds[].assetId string

POST /v1/assets でアップロードした asset の id。

backgrounds[].x number

用紙の左上からの横位置(px)。ノードと違い、余白の外側から測ります。

backgrounds[].y number

用紙の左上からの縦位置(px)。

backgrounds[].w number

幅(px)。

backgrounds[].h number

高さ(px)。

backgrounds[].opacity number

0 から 1 で、1 が不透明です。濃い用紙のせいで上に印字した値が読みにくいときは、下げてください。

backgrounds[].rotate number

回転角(度)。傾いたスキャンをまっすぐにするためのものです。既定は 0 です。

backgrounds[].print boolean

false にすると、エディタには表示されますが出力には出ません — あらかじめ印刷された用紙に欄の位置を合わせるためのものです。

backgrounds[].locked boolean

エディタ上で固定します。レンダリングには影響しません。

fonts

family string

フォントファミリー名。styles[].fontFamily が求めるものと一致させます。

src enum

フォントの取得元。現在は "preinstalled" のみ動作します — エディタに並んでいる組み込みのファミリーです。"r2" はアップロードしたフォント用に予約されていますが、まだご利用いただけません。これを求めるドキュメントは代替の書体でレンダリングされ、そのことが warnings に出ます。

preinstalledr2

styles

id string

スタイルの id。ドキュメント内で一意です。nodes[].styleId から参照されます。

fontFamily string

フォントファミリー。日本語・中国語・韓国語が入りうるテキストには "Noto Sans CJK KR" をお使いください — 欧文だけのフォントでは、それらの字形が落ちます。

fontSize number

フォントサイズ(px)。

fontWeight integer

フォントの太さ。400 が標準、700 が太字です。

italic boolean

斜体。

color string

文字色(#RRGGBB)。

bg string

背景色(#RRGGBB)、または "transparent"。

align enum

水平方向の配置。

leftcenterrightjustify

valign enum

ノードのボックス内での垂直方向の配置。

topmiddlebottom

lineHeight number

行の高さ。フォントサイズに対する倍率です。

border string

"1px solid #ddd" のような CSS の border 省略記法、または "none"。

radius number

角の丸み(px)。

opacity number

不透明度(0〜1)。

nodes

id string

ノードの id。ドキュメント内で一意です。n_ の接頭辞は慣習であって、規則ではありません。

name string

エディタのオブジェクトツリーに出す名前です。識別子ではなくラベルなので、一意である必要はなく、どこからも参照されません。空にすると、ツリーには id が表示されます。

parent string

親ノードの id。空文字列にすると、そのノードはページに直接置かれます。存在しない親や循環は違反です。

order integer

同じ親を持つもののあいだでの順序。重なったときにどちらが上に描かれるかも、これで決まります。

type enum

このノードが何であるか。

labelimagelinerectcircletablechartcodehtmlfield

x number

親を基準とした X 位置(px)。

y number

親を基準とした Y 位置(px)。

w number

幅(px)。

h number

高さ(px)。0 は自動の高さです。配列に結び付いた表では、これは確保する高さになります — 「伸びる表」をご覧ください。

show string

ノードを表示する条件。空文字列にすると常に表示します。それ以外はお客様のデータに対して評価される式です。

styleId string

styles[] の id。空文字列にすると既定のスタイルになります。

props object

種類ごとの設定。すべてのキーが必須で、使わないものは空のままにします。

nodes[].props

text string

label と code に使います。label では描く文字、code ではエンコードする値です。{{ … }} が使えます。改行文字はその位置で行を分けます。ほかの種類では空にします。

src string

image のみ。受け付けるのは data: URI だけです。レンダリング中に外部へリクエストを出さないためで、画像はあらかじめ base64 にしてください。テンプレートに http や https のアドレスを書くと、E:render.compile#external-ref でレンダリングが失敗します。{{ data.logo }} のようなバインディング経由でアドレスが届いた場合は、その画像 1 枚だけを飛ばし、応答に external-ref の警告を載せます — 1 件の不正なレコードでバッチ全体が失敗しないようにするためです。ほかの種類では空にします。

fit enum

image のみ。画像をボックスにどう収めるか。ほかの種類では空にします。

""containcoverfill

thickness number

line・rect・circle・table・field に使います。線の太さ(px)です。表では罫線、field ではボックスの枠線になります。ほかの種類では 0 にします。

stroke string

line・rect・circle・table・field に使います。線の色(#RRGGBB)です。表では罫線、field ではボックスの枠線になります。ほかの種類では空にします。

fill string

rect・circle・table に使います。塗りの色(#RRGGBB)です。表では見出しセルの背景になります。ほかの種類では空にします。

repeat string

rect のみ。"data.companies" のようなパスにある配列の要素ごとに、このグループを 1 組ずつ描きます。中にあるものはすべて一緒に繰り返されます。ほかの種類では空にします。

as string

rect のみ。繰り返しグループの中で現在の要素を指す名前です — "co" にすると {{ co.name }} と書けます。既定は "block" です。"item" にはなりません: その名前は表の行のものなので、グループの中にある表が両方を読めるようにしてあります。

break enum

rect のみ。"page" にすると、繰り返しごとに新しいページから始めます。空にすると、続けて流し込みます。

""page

keep enum

rect のみ。"together" にすると、1 組がページ境界で分断されないようにします。1 ページより高い組は、それでも分割されます。

""together

padding number

table のみ。セル内側の余白(px)です。このキーがない場合、既定の余白は表に罫線があるときだけ適用されます。ほかの種類では 0 にします。

padY number

table のみ。セルの内容の上下の余白(px)です。-1 にすると padding に従います — 0 を「従う」の意味にはできません。0 は「余白なし」という実際の値だからです。

padX number

table のみ。セルの内容の左右の余白(px)です。-1 にすると padding に従います。

align enum

table のみ。格子セルの既定の水平配置です。セル自身が指定していれば、そちらが優先されます。繰り返し表では列ごとの columns[].align を使います。

""leftcenterright

valign enum

table のみ。セル内の垂直配置です。空にすると中央になります。セル自身が指定していれば、そちらが優先されます。

""topmiddlebottom

wrap enum

table のみ。セル内の折り返しです。空または "on" ではみ出した文字を次の行に送り、"off" では 1 行のままにして末尾を省略記号で切ります。

""onoff

bind string

table のみ。繰り返す配列のパス("data.items" など)。ほかの種類では空にします。

headerHeight number

table のみ。見出し行の高さ(px)。ほかの種類では 0 にします。

rowHeight number

table のみ。本文行の高さ(px)。ほかの種類では 0 にします。

max integer

table のみ。描く行数の上限で、残りは落とされます。ほかの種類では 0 にします。

columns array

table のみ。左から右への列の定義。ほかの種類では空にします。

columns[].header string

見出しセルの文字。

columns[].cell string

本文セルのテンプレート。item が現在の行を指すので、"{{ item.name }}" でその項目を読みます。

columns[].width number

列の幅(px)。合計が表の幅と一致するようにしてください。

columns[].align enum

セルの配置。

leftcenterright

rows integer

table のみ。格子表(bind が空)の行数です。繰り返し表とほかの種類では 0 にします。

cells array

table のみ。格子表のセルです。指定のない位置は空のセルになるので、すべてを並べる必要はありません。繰り返し表とほかの種類では [] にします。

cells[].r integer

行の位置(0 から)。

cells[].c integer

列の位置(0 から)。

cells[].span integer

横方向の結合幅。既定は 1 です。

cells[].rowspan integer

縦方向の結合高さ。既定は 1 です。

cells[].text string

セルの内容。"{{ data.x }}" で値を結び付けられます。

cells[].align enum

セル内の文字の配置。

leftcenterright

cells[].valign enum

このセルだけの垂直配置。空にすると表に従います。

""topmiddlebottom

cells[].wrap enum

このセルだけの折り返し。空にすると表に従います。

""onoff

cells[].pad number

このセルだけの余白(px)。-1 または未指定にすると表の余白に従います — 0 を「従う」の意味にはできません。0 は「余白なし」という実際の値だからです。表全体に触れずに、1 つのセルだけを詰めたり広げたりするのにお使いください。

cells[].padY number

このセルだけの上下の余白(px)。-1 または未指定にすると pad に、次いで表に従います。

cells[].padX number

このセルだけの左右の余白(px)。-1 または未指定にすると pad に、次いで表に従います。

cells[].src string

セルの中の画像。data:image/ で始まる値のみです — 外部の URL はネットワークへ要求を出すことになるため、レンダラーが拒否します。

cells[].fit enum

セル画像の収め方。空にすると contain です。

""containcoverfill

cells[].bg string

セルの背景色(#RRGGBB)。なしにする場合は空にします。

cells[].head boolean

見出しセル。r=0 の行全体で有効にすると見出し行に、c=0 の列全体で有効にすると見出し列になります。

cells[].border enum

"none" にすると、このセルだけ罫線を消します。

""none

chartKind enum

chart のみ。"bar"(既定)・"line"・"pie"・"donut" のいずれかです。ほかの種類では空にします。

""barlinepiedonut

codeKind enum

code のみ。どの記号体系で描くか: "qr"(既定)・"code128"・"ean13" です。ほかの種類では空にします。

""qrcode128ean13

ecc enum

code のみ、しかも QR のときだけです。誤り訂正のレベル L・M・Q・H で、空にすると M です。レベルを上げるほど傷みに強くなりますが、必要なモジュールも増えるため、同じ文字列でもより密なコードになります。

""LMQH

html string

html のみ。書式付きの本文です。許可された一覧にあるタグだけが残り、それ以外は語は残ってタグだけが失われます。script・style・iframe は中身も失われます。ほかの種類では空にします。

fieldKind enum

field のみ。どの入力欄を置くか: "text"(既定)・"check"・"select"・"list"・"radio"・"button"・"sign" です。"sign" は署名する場所を置くだけで、署名そのものは行いません。ほかの種類では空にします。

""textcheckselectlistradiobuttonsign

fieldName string

field のみ。この入力欄が PDF の中で持つ名前で、記入された内容を読む側が探すキーです。空にすると、ノードの id を使います。同じ名前の欄が 2 つあると、値を共有します。ほかの種類では空にします。

options array

field のみ、しかも "select"・"list"・"radio" のときだけです。選択肢を並べます。それ以外はすべて [] にします。

required boolean

field のみ。入力を必須として印を付けます。こちらで強制はしません — あくまで印であり、強制するかどうかは PDF を開くプログラムが決めます。ほかの種類では false にします。

readonly boolean

field のみ。値は見せますが、変更はできないようにします。ほかの種類では false にします。

式

文字列の項目にはどれでも {{ … }} の式を入れられます。式は、レンダリング要求とともにお送りいただいたデータに対して評価されます。

表のセルの中では、item が結び付けた配列の現在の行を指します。表の外では定義されていません。

ページ番号は [[page]] と [[pages]] という別の書き方で、page.header と page.footer でのみ使えます。レイアウトのあと、ページ数が分かってから埋められます。

書式

format(value, kind, pattern?, locale?)
値を人が読む形に整えます。kind は number・currency・date です。currency のときはパターンの位置に通貨コード(USD、EUR)を、date のときは short・medium・long・iso を渡します。通貨コードが誤っている場合は、ドキュメント全体を失敗させず、そのコードをそのまま印字します。

数値

abs(n)
ゼロからの距離。符号を外します。
ceil(n)
切り上げて整数にします。
floor(n)
切り捨てて整数にします。
trunc(n)
四捨五入せずに小数部を落とします。
sign(n)
-1・0・1 のいずれかを返します。
sqrt(n)
平方根。負の値を渡すとエラーではなく空の値になります。
pow(n, exp)
べき乗を求めます。
round(n, digits?)
指定した小数桁で丸めます(既定は 0 桁)。
min(a, b, ...)
引数のうち最も小さいもの。
max(a, b, ...)
引数のうち最も大きいもの。
clamp(n, lo, hi)
数値を範囲内に収めます: lo を下回れば lo に、hi を上回れば hi になります。

文字列

upper(s)
大文字にします。
lower(s)
小文字にします。
trim(s)
前後の空白を取り除きます。
len(s)
文字数。
sub(s, start, end?)
文字位置で切り出した部分文字列。
replace(s, find, with)
一致するものをすべて置き換えます。検索はリテラルの文字列で、パターンではありません。
contains(s, find)
その文字列を含んでいれば真。
startsWith(s, find)
その文字列で始まっていれば真。
endsWith(s, find)
その文字列で終わっていれば真。
padStart(s, width, fill?)
指定の幅になるまで左を埋めます。000042 のような請求書番号に使います。
padEnd(s, width, fill?)
指定の幅になるまで右を埋めます。
split(s, sep)
区切り文字で文字列を配列に切り分けます。

配列

count(array)
要素の個数。
sum(array, prop?)
合計します。prop を渡すと、各要素のその項目を合計します。
sumProduct(…)
各要素の 2 つの項目を掛け合わせ、その結果を合計します。税率が混在する場合の税額合計 — sumProduct(items, "amount", "vat") / 100 — や、合計を保存せずに求める明細金額 sumProduct(items, "qty", "price") にお使いください。税のことは何も知りません: 税率はお客様のデータが運ぶ数値です。国によって異なり、法律で変わるからです。丸めは最後に一度だけ行うので、税率区分ごとに丸めることを求める制度では、filter で区分ごとに描いてください。
avg(array, prop?)
平均。空の配列はエラーではありません — 値を空欄のままにし、nan の警告をお知らせします。求められなかった数値と同じ扱いです。
minOf(array, prop?)
最も小さい要素、またはその項目の最小値。
maxOf(array, prop?)
最も大きい要素、またはその項目の最大値。
first(array)
最初の要素。
last(array)
最後の要素。
at(array, index)
0 から数えて、その位置にある要素。
join(array, sep?, prop?)
区切り文字でつないで 1 つの文字列にします(既定はカンマと空白)。
filter(array, prop, value)
指定した項目が値と等しい要素だけを残します。
sortBy(array, prop, desc?)
項目で並べ替えます。第 3 引数に true(または文字列 "desc")を渡すと降順になります。
slice(array, start, end?)
位置で指定した範囲の要素。
unique(array, prop?)
重複を取り除きます。

選ぶ

if(test, then, else)
第 1 引数が真なら第 2 引数を、そうでなければ第 3 引数を返します。
coalesce(a, b, ...)
空でない最初の引数を返します。空とは null・undefined・空文字列・空配列のことで、isEmpty と同じ規則です。0 は空ではないので、価格が 0 の場合もそのまま残ります。
ifEmpty(value, fallback)
その値を返しますが、空のときは代わりの値を返します。空とは null・undefined・空文字列・空配列のことです。
isEmpty(value)
値が null・undefined・空文字列・空配列のいずれかであれば真。

日付

dateAdd(date, n, unit)
日付に期間を足して YYYY-MM-DD を返します。unit は day・week・month・quarter・year で、負の数を渡すと引きます。月の計算は月末に丸め込むので、2026-01-31 の 1 か月後は 2026-03-03 ではなく 2026-02-28 です。
dateDiff(from, to, unit?)
2 つの日付のあいだの整数単位を、前から後ろへ数えます。既定は日数です。ゼロ方向に丸めるので、29 日は 0 か月です。
dateStart(date, unit)
その週・月・四半期・年の初日。週は月曜日から始まります。
dateEnd(date, unit)
その週・月・四半期・年の最終日 — 28 日か 29 日か 30 日か 31 日かを、ご自身で数えずに月末を求める方法です。
datePart(date, part)
日付から数値を 1 つ取り出します。part は year・month・day・weekday・quarter・week・dayOfYear です。月は 1〜12、曜日は月曜日の 1 から日曜日の 7 までで、どちらも JavaScript の 0 始まりではなく ISO-8601 に従います。
today(timezone?)
今日の日付を YYYY-MM-DD で返します。1 回のレンダリングの中では固定されるので、二度呼んでも日付がまたがることはありません。既定は UTC ですが、これはどこでも同じ日とは限りません: ソウルの 24 日午前 8 時は、UTC ではまだ 23 日です。ドキュメントに印字する日付を読む人の日付にしたいときは、today("Asia/Seoul") のように IANA のタイムゾーンを渡してください。返すのは日付だけで、時刻は返しません。タイムゾーンなしで印字された時刻は、誰にも読めない時刻だからです。

書式付きの本文

html ノードはマークアップのかたまりを受け取ります — ラベルを縦に並べる方法ではできないことです。1 行増えるだけで、手で置いた位置がすべてずれてしまうからです。規約・注記・脚注など、流し込みたいものにお使いください。

こちらはマークアップを濾すのではなく、組み直します。お送りいただいた HTML を解析し、こちらの一覧にあるタグと属性だけを書き戻します。この違いは重要です: こちらが理解しないものは、こちらが思いついていたかどうかに関わらず、そもそも出力に現れません。

使えるもの: p・div・span・br・hr・blockquote・pre・h1〜h6・b・strong・i・em・u・s・small・sub・sup・code・a・ul・ol・li・table とその行とセル・img です。一覧にないタグは、タグだけが失われて語は残ります — 書いた文章が消えることはありません。script・style・iframe とその仲間は中身も失われます。その中身は文章ではないからです。

属性はさらに狭く、style(宣言ごとに検査し、position は除外)、セルの colspan と rowspan、リンクの href、画像の src・alt・width・height だけです。class と id はありません — ページを組んでいるクラスと衝突するからです。画像は data: URI である必要があり、これは image ノードと同じ規則です。遠くの画像を描くとは、レンダリング中にそれを取りに行くことだからです。リンクの宛先はどこでも構いません。リンクは取りに行くことではありません。

html の中の {{ … }} は、データに何が入っていても文字になり、決してマークアップにはなりません。属性の中では、解決した値もリテラルと同じように検査されるので、値が style に url(…) を、href に javascript: を紛れ込ませることはできません。上限は 20,000 文字・入れ子 32 段・タグ 2,000 個で、それを超えた分は切り、warnings でお知らせします。

チャート

chart ノードは、表と同じ 2 つの項目をそのまま使います: bind が配列を指し、columns が各要素から何を読むかを表します。columns[0].cell がラベル、columns[1].cell が値で、columns[1].header が表題になります。新しい項目は chartKind の 1 つだけで、"bar"・"line"・"pie"・"donut" があります。

軸はきりのよい最大値を選びます — 10 の累乗の 1 倍・2 倍・5 倍です。目盛りが、人が選ぶであろう数値として読めるようにするためです。数値でない値は、レンダリングを失敗させずゼロとして数えます。読めないセルが空欄になるのと同じ考え方です。pie と donut は絶対値を使います。負の扇形には意味がないからです。

チャートは SVG で描かれるので、PDF の中でもベクターです。要素が多いときは、ラベルを重ね書きせず間引きます。凡例は pie と donut の下に 1 行で置かれ、動きません — 自分で場所を探す凡例は、レイアウトまで一緒に動かしてしまうからです。

QR コードとバーコード

code ノードは、文字を読み取り可能な図形にします。値を text に入れ、codeKind で "qr"・"code128"・"ean13" のいずれかを選びます。SVG で描かれるので PDF にはベクターとして入ります — 印刷解像度がどれだけ高くても鮮明なままで、バーの端は規格どおりの位置に正確に来ます。この最後の点が大切です: 誤ったタイミングでラスタライズされたバーコードは、読み取り機が誤読するバーコードだからです。

QR は正方形のままにしてください。ボックスを埋めるためにコードを歪めることは決してしません — 潰れたコードは読み取れないからです。ですから長方形のボックスでは、長いほうに余白が残ります。コードの周りの余白(クワイエットゾーン)は規格の一部なので、こちらで確保します。切り詰めようとしないでください。

ean13 は 12 桁を受け取り、チェックディジットはこちらで計算します。13 桁をお渡しいただいた場合は、代わりにそれを検証します。code128 は ASCII の 32〜126 を運びます。値をエンコードできない場合 — 桁数の誤り、対象外の文字、QR に対して多すぎるデータ — は、空のボックスを描かずエラーでレンダリングを失敗させます。空のボックスは、ラベルを印刷したあとになって気づくものだからです。

対応しているのはこの 3 つです。100 種類の記号体系を網羅するライブラリもありますが、いちばん小さいものでもこちらのワーカー全体より重くなります。描けていないものが必要でしたら、どれがなぜ必要かをお知らせください。

表

表には 2 つのモードがあります。bind が空なら手で組む格子、bind に配列のパスを入れるとデータから行が生まれる繰り返し表です。このキー 1 つで表が何であるかが変わり、2 つのモードは違う項目を使います — 格子は rows と cells を、繰り返し表は columns と max を使います。

格子表は行数を rows から、列と幅を columns から取ります。セルの中身は cells 配列に入れ、r と c で位置を指定します — 指定のない位置は単に空のセルなので、すべてを並べる必要はありません。右や下のセルと結合するには span と rowspan を使います。head を有効にすると見出しセルになり、塗りの色はそこにだけ塗られます。

繰り返し表では、columns の 1 つ 1 つが 1 列です。header が見出し、cell が本文のテンプレートで、そのテンプレートの中では item が現在の行を指します — "{{ item.name }}" のように書きます。行数はデータから決まり、max がその上限になります。上限を超えた行は落とされ、warnings でお知らせします。

列の幅の合計は、表の幅と一致させてください。合っていない場合はブラウザが伸ばしたり縮めたりするので、設計したとおりの結果にはなりません。

余白は 3 段階で決まります。まずセル(cells[].padY・padX・pad)、次に表(padY・padX・padding)、どちらも指定がなければ、罫線があるときだけ既定の余白が適用されます。「継承する」の印は -1 です。0 は「余白なし」という実際の値なので、その用途には使えません。

配置も同じ順で決まります。セルの指定が優先され、なければ表の align と valign が適用されます。wrap を "off" にするとそのセルは 1 行のままになり、はみ出した分は切って省略記号を付けます — はみ出した文字が隣のセルに重ね書きされることは決してありません。

受け取った人が記入できる欄

field ノードは、PDF を開いた相手が入力できる欄を作ります — 契約書・申込書・アンケートなど、値があとから届くものに使います。種類は fieldKind で選びます: "text"(既定)・"check"・"select"・"list"・"radio"・"button"・"sign"。

fieldName は PDF の中でその欄が持つ名前であり、記入された内容を読む側が探すキーです。空にすると、ノードの id を使います。使えるのは英数字・アンダースコア・ハイフンだけで、64 文字までです。それ以外が入っていた場合は、直さずにノードの id へ切り替えます。直してしまうと、書いた名前と違う名前になり、それに気づくのは受け取る側になってからだからです。ドットは特に使えません。PDF が名前の区切りとして扱うためです。

"select"・"list"・"radio" には options が必要です。選択肢がなければ選ぶものがないので、その欄だけを飛ばし、応答の warnings でお知らせします。"radio" は、描いたボックスを選択肢ごとの行に分けます。

線はご自身で stroke と thickness を使って描いてください。画面上では PDF ビューアが入力欄を描いてくれますが、印刷した紙にビューアはありません — 線がないと、書く場所がどこにも見えない白紙の用紙が出てきます。特に "sign" は、何も描かれません。

値をあらかじめ入れておくこともできます — label と同じ text プロパティを使います。{{ data.payment }} のように結び付けると、お渡しする PDF の時点ですでに値が入った状態になります(受け取った方があとから変えることもできます)。"check" は "false"・"0"・"no"・空 をオフとして読みます。"select"・"list"・"radio" は options にある値しか受け付けません。誰も選べない値は、空のボックスより厄介だからです。日本語などの非ラテン文字も使えます。

できないこと: "sign" は署名する場所を置くだけで、署名そのものは行いません。required はこちらで強制しない印であり、強制するかどうかは PDF を開くプログラムが決めます。フィールドが計算式やスクリプトを持つことはありません。画像(PNG・JPEG)で受け取ると、フィールドはなくなります。画像には「記入する箱」という概念がないからです。1 つのドキュメントに置けるフィールドは 500 個までで、それを超えると止めてお知らせします。

伸びる表

配列に結び付いた表は、高さが決まっていません — 3 行にも 300 行にもなります。繰り返しグループも同じです。そうしたノードが最初に現れたところから先は、固定の座標ではなくドキュメントの流れの中で配置されるため、後ろにあるものは表が伸びるぶん下へ動きます。

表に与える h は上限ではなく、確保する高さです。表はそれを超えることがあります。表より後ろに置いたノードは、確保した高さの下端から測って、設計したときの間隔を保ちます。

伸びる表には、現実的な h を与えてください。0 のままにすると、下に置いたノードが表の後ろではなく、表の上端から始まってしまいます。

ひとかたまりを繰り返す

表は行を繰り返します。rect は中にあるものすべてを繰り返します: props.repeat に配列のパスを入れると、そのグループが子ごと、要素ごとに 1 組ずつ描かれます。1 件につき 1 リクエストではなく、1 回のリクエストで多くのレコードを網羅したドキュメントを作れるのは、この仕組みのおかげです — 支店ごとの明細書、会社ごとの集計などです。

グループの中では、現在の要素が props.as の名前で参照されます。as が "co" なら {{ co.company }} で現在の要素の company を読みます。既定は "block" です。"item" にしていないのは意図的です: その名前は表の行のものなので、分けておくことで、グループの中の表が両方を読めます — レコードは {{ co.company }}、行は {{ item.name }} です。名前に data・item・index は使えません。

props.break を "page" にすると、繰り返しごとに新しいページから始まります。1 ページより高いグループは自分で複数ページに分かれるので、数えていただく必要はありません。グループの中の表も流れるので、描いたグループを超えて伸び、そのグループの残りを下へ押し下げることがあります。

1 組の中のページ番号には [[bpage]] と [[bpages]] を使います — ドキュメント全体ではなく「この請求書の 2 ページ中 1 ページ目」という意味です。page.footer でのみ使えます。[[page]] は Chromium が印刷しながら埋めますが、Chromium は繰り返しがどこで始まったかを知らないため、そのフッターはあとからこちらで描きます。そうしたフッターが Latin-1 の範囲に収まっている必要があるのも、同じ理由です: こちらが埋め込めるのは標準フォントであって、日本語のフォントではありません。

パスが存在しない、配列でない、空である、のいずれの場合も、グループは単に描かれません — エラーにはなりません。繰り返しは表の行と同じノード予算を分け合うので、大きな配列は際限なく伸びるのではなく切り詰められます。グループを互いに入れ子にすることはできません。

エディタが書き足すキー

上のスキーマは、レンダラーが必要とするすべてです。ただしエディタで保存したテンプレートを API から読み戻すと、さらに 3 つのキーが付いています — name・hidden・lockOwn です。こちらの検証はこれらを受け付けるので、そのままにしておいて構いません。取り除いてもレンダリングは何も変わりません。

name はオブジェクトの一覧に出るラベル、lockOwn はエディタでロックしたかどうかの記録です。どちらもレンダリングにはまったく影響しません。

hidden は別です — true のとき、そのノードと中にあるものすべてが出力からまるごと外れます。エディタの目のアイコンがこの値を書きます。show と混同しやすいのですが、show はお客様のデータに対して評価される条件であり、hidden は「作った人がいまは見たくない」という意味です。API でドキュメントを組み立てる場合は、hidden ではなく show をお使いください。

例
{
  "v": 1,
  "unit": "px",
  "page": {
    "size": "a4",
    "orientation": "portrait",
    "margin": { "t": 48, "r": 48, "b": 56, "l": 48 },
    "header": "",
    "footer": "{{ data.company }} · [[page]]/[[pages]]"
  },
  "fonts": [],
  "styles": [
    {
      "id": "s_title",
      "fontFamily": "Noto Sans CJK KR",
      "fontSize": 26, "fontWeight": 700, "italic": false,
      "color": "#1a1a1a", "bg": "transparent",
      "align": "left", "valign": "top", "lineHeight": 1.5,
      "border": "none", "radius": 0, "opacity": 1
    }
    // ...
  ],
  "nodes": [
    {
      "id": "n_title", "parent": "", "order": 0, "type": "label",
      "x": 0, "y": 0, "w": 400, "h": 36,
      "show": "", "styleId": "s_title",
      "props": {
        "text": "{{ data.title }}",
        "src": "", "fit": "", "stroke": "", "fill": "",
        "thickness": 0, "bind": "",
        "headerHeight": 0, "rowHeight": 0, "max": 0, "columns": []
      }
    },
    {
      "id": "n_items", "parent": "", "order": 1, "type": "table",
      "x": 0, "y": 168, "w": 499, "h": 300,
      "show": "", "styleId": "",
      "props": {
        "text": "", "src": "", "fit": "", "stroke": "", "fill": "",
        "thickness": 0,
        "bind": "data.items",
        "headerHeight": 32, "rowHeight": 28, "max": 200,
        "columns": [
          { "header": "Item",  "cell": "{{ item.name }}",  "width": 289, "align": "left" },
          { "header": "Qty",   "cell": "{{ item.qty }}",   "width": 60,  "align": "center" },
          { "header": "Price", "cell": "{{ item.price }}", "width": 150, "align": "right" }
        ]
      }
    }
  ]
}
リクエスト
{
  "template": { /* the object on the left */ },
  "data": {
    "company": "ACME Inc.",
    "title": "Invoice #1042",
    "items": [
      { "name": "Design",  "qty": 2, "price": 120 },
      { "name": "Hosting", "qty": 1, "price": 30 }
    ]
  }
}