帳票を作る

請求書や納品書のような帳票を、テンプレートとデータから BDF として作れます。テンプレートは、文字のところにタグ({{名前}})を書いた Excel のブック、Word の文書、PowerPoint のプレゼンテーション、入力欄のある PDF、pdfme のテンプレートです。データは JSON・YAML・CSV・Excel のファイルで、bdf fill がタグをデータに置き換え(または入力欄を埋め)、組版して .bdf を書き出します。Office のファイルは書き出しません。できた帳票は bdf pdf で PDF にできます。タグ付き PDF と PDF/A も選べます。

bdf fill -data order.yaml -data items=lines.csv invoice.xlsx invoice.bdf
bdf pdf invoice.bdf invoice.pdf
テンプレート 帳票の形 向いているもの
Excel(.xlsx) ブックが印刷するページ。行が増えればページが増える 表が中心の帳票、数式で計算する帳票、決まった行数の伝票
Word(.docx) 文書のページ。長くなった分は次のページへ流れる 文章と表の帳票、通知書、差し込みの手紙
PowerPoint(.pptx) スライド。図形は置いた場所から動かない 位置が決まった帳票、証書、ラベル、1 件 1 枚の資料
PDF フォーム(.pdf) PDF のページ。入力欄の場所に値が入る すでに PDF として配られている帳票。申請書、公的な様式
pdfme(.json) デザイナーで見えているページ。表はページをまたいで伸びる pdfme のデザイナーを使い、ブラウザでレイアウトした帳票

タグの書き方とデータは、Office の 3 つの形式に共通です。同じデータを、形式の違うテンプレートに流し込めます。PDF フォームと pdfme のテンプレートにはタグの代わりにフィールドがあり、その名前で同じ形のデータを受け取ります。

インストールせずに試すなら、フォームのページにテンプレートをドロップしてください。タグを読んで入力フォームを作り、入力した値で穴埋めした帳票を表示して、PDF で保存できます。処理はすべてブラウザの中で行われ、ファイルはアップロードされません。

テンプレートを書く

普段どおりに帳票を作り、値を入れたいところにタグを書きます。まず Excel のブックで説明します。Word と PowerPoint は、同じタグが何を繰り返すかだけが違います(Word のテンプレート、PowerPoint のテンプレート)。

Excel では、用紙・余白・印刷範囲・印刷タイトル・改ページ・ヘッダーとフッターは、ブックのページ設定がそのまま使われます(Excel の印刷ビュー)。

タグ 意味
{{name}} 値。{{customer.name}} は値の中の値、{{.}} は項目そのもの
{{total | fmt:"#,##0"}} 表示形式を付けた値。書式は Excel の表示形式コード(yyyy年m月d日、ggge年、0.0% …)
{{#items}} … {{/items}} 区間。リストなら項目ごとに繰り返し、それ以外なら値が真のときだけ出す
{{^items}} … {{/items}} 逆の区間。リストが空、または値が偽のときだけ出す
{{%photo}} 画像。Excel ではセル(結合セルならその範囲)に、Word と PowerPoint では置いてある画像の枠に収める。{{%photo | fit:cover}} で枠を覆う、fit:stretch で引き伸ばす
{{%url | qr}}、{{%jan | barcode:jan}} 値の QR コード、バーコード(QR コードとバーコード)

行を繰り返す

区間の開始と終了のタグを別々のセルに書くと、行の繰り返しになります。繰り返す行のセルからは、その項目の値が見えます。

行を増やす(開始と終了が同じ行): 項目ごとに行が 1 つ増え、下の行は押し下げられ、用紙に入らなければページが増えます。印刷タイトルの行は各ページで繰り返されます。

A B C D E
{{#items}}{{_index}} {{name}} {{qty}} {{price}}{{/items}} =C4*D4
合計 =SUM(E4:E4)

決まった行数に入れる(開始と終了が別の行): 開始のタグの行から終了のタグの行までが、項目を 1 つずつ入れる枠になります。行は増えず、項目のない行は罫線だけの空行として残ります。1 行目のタグと数式が、自分のタグを持たない残りの行にも使われます。項目が枠より多いとエラーになります。

A B C
{{#rows}}{{no}} {{name}} {{amount}}
(10 行目){{/rows}}

ページとして繰り返す(| pages): 開始から終了までの行のまとまりを項目ごとに複製し、それぞれを新しいページにします。10 行の伝票を 25 行分のデータで 3 枚にする、という帳票はこの形です。データは、ページごとの行と繰越をあらかじめ分けて渡します。

{ "pages": [
    { "rows": [ … 10 行 … ], "carry_in": 0,     "carry_out": 55000 },
    { "rows": [ … 10 行 … ], "carry_in": 55000, "carry_out": 210000 },
    { "rows": [ … 4 行 … ],  "carry_in": 210000, "carry_out": 300000 } ],
  "total": 300000 }

{{#pages | pages}} … {{/pages}} の中に {{#rows}} … {{/rows}} の枠を置きます。ページ番号は {{_page}} / {{_pages}}、最後のページだけに出す合計は {{#_last}}合計 {{total}}{{/_last}} と書けます。

{{#groups | rows}} と書けば、別の行にまたがるまとまりを、行を増やしながら繰り返します(グループの見出し・明細・小計)。区間は入れ子にできます。

開始と終了のタグを同じセルに書いた区間は、セルの中の文字を繰り返します({{#tags}}{{.}} {{/tags}})。同じセルでも | rows を付ければ行の繰り返しになるので、1 列だけの表でも行を増やせます({{#items | rows}}{{name}}{{/items}})。繰り返し方を書いた区間は、文字ではなく行(Word では段落、PowerPoint ではスライドなど)を繰り返す、という規則は 3 つの形式に共通です。逆の区間にも書けます({{^items | rows}}明細なし{{/items}} は、項目がないときだけその行を出します)。

区間の中で使える変数

変数 意味
_index、_count 何番目の項目か(1 から)、項目の数
_first、_last 最初・最後の項目か
_page、_pages `
_blank、_blank_first 枠の行で、項目のない行か、その最初の行か({{#_blank_first}}以下余白{{/_blank_first}})
_slide、_slides PowerPoint で、そのスライドの番号と、できあがったスライドの数

行に追随するもの

行が増えたり複製されたりすると、Excel で行を挿入したときと同じように、行を指しているものが付いてきます。結合セル、条件付き書式、リンク、印刷範囲と印刷タイトル、改ページ、図形、そして数式の参照です。複製された行の中の数式は自分の行(自分のページ)を指し、繰り返す行を含む範囲はすべての複製を含みます(=SUM(E4:E4) は全明細の合計になります)。

数式は穴埋めのあとで計算し直します。条件付き書式の評価器が知っている関数(四則、SUM、AVERAGE、COUNT、COUNTIF、IF、ROUND、文字列、日付 など)が対象で、それ以外の数式のセルは空にして警告を出します。

Excel の表見出しとセクションの改ページ

請求書では、Excel の「印刷タイトル」の「タイトル行」を表の見出しだけ (例: $15:$15)にします。帳票タイトルや宛名はこの範囲に含めません。 通常の印刷タイトルは後続の全ページに繰り返されますが、「数式 → 名前の管理」で 次の名前をシートのスコープに定義すると、BDF の改ページを調整できます。

名前 参照範囲の例 動作
_bdf.Print_TitleScope 'Invoice'!$A$15:$H$16 この表の明細が続くページだけに印刷タイトルを繰り返す。見出しと明細タグの行を含めると、項目数に合わせて範囲が伸びる。合計や備考だけのページには表見出しを付けない。
_bdf.Print_KeepTogether 'Invoice'!$F$18:$H$20,'Invoice'!$A$22:$H$25,'Invoice'!$A$27:$H$31 各範囲の行を同じページにまとめ、残りの高さに入らなければ次のページへ送る。合計・振込先・備考などを別々の範囲で指定する。

範囲はテンプレートの行の増減に追従します。繰り返す項目の中に置いた範囲は 各項目に適用されます。重なった範囲は一つのまとまりになり、隣接するだけの 範囲は別々に扱います。1 ページより高いまとまりは分割でき、手動の改ページは 優先されます。この名前は BDF の出力を制御するもので、Excel 自体の印刷プレビューは Excel の規則に従います。公開している Excel の請求書サンプルには両方を設定済みです。

Word のテンプレート

Word の文書では、本文・表のセル・テキストボックス・ヘッダーとフッターの文字にタグを書きます。穴埋めのあと文書を組み直すので、値が長くなれば行が増え、表の行が増えれば次のページへ流れます。表の見出し行(「タイトル行の繰り返し」)は、流れた先のページでも繰り返されます。帳票はページのビューだけになります。

区間が繰り返すものは、開始と終了のタグを両方含むいちばん小さいまとまりで決まります。

タグの位置 繰り返すもの
同じ段落 段落の中の文字
本文(または同じセル、同じテキストボックス)の別々の段落 開始の段落から終了の段落までの、段落と表
表の同じ行の別々のセル その行(項目ごとに行が増える)
表の別々の行 開始の行から終了の行までが、項目を 1 つずつ入れる枠(| rows を付ければ、行を増やしながら繰り返す)

PowerPoint のテンプレート

PowerPoint では、図形・テキストボックス・表のセルの文字にタグを書きます。スライドは固定のページで、図形は置いた場所から動きません。

タグの位置 繰り返すもの
同じ段落 段落の中の文字
同じ図形(または同じセル)の別々の段落 開始の段落から終了の段落まで
表の同じ行の別々のセル その行(項目ごとに行が増える)
表の別々の行 開始の行から終了の行までが、項目を 1 つずつ入れる枠(| rows で行を増やす繰り返し)
同じスライドの別々の図形 そのスライド(項目ごとに 1 枚)
別々のスライド 開始のスライドから終了のスライドまで

PDF フォーム

入力欄のある PDF(AcroForm。Acrobat や LibreOffice、多くのフォーム作成ツールが作るもの)は、そのままテンプレートになります。入力欄の名前がデータの名前です。bdf fill は入力欄の場所に値を描き、PDF のページを BDF の文書として書き出します。できた帳票に入力欄は残りません。

bdf template schema -json-schema application.pdf   # 入力欄を、データの JSON Schema として
bdf fill -data applicant.json application.pdf application.bdf
bdf pdf application.bdf filled.pdf
入力欄 データ
テキスト 文字列か数値。複数行の欄は欄の幅で折り返し、マス目の欄(comb)は 1 文字ずつマスに入れ、大きさが自動の欄は収まる大きさにする
チェックボックス true か false
ラジオボタン 選ぶボタンの値
リスト、ドロップダウン 選択肢の値。欄には、その選択肢の表示名が出る

pdfme のテンプレート

pdfme は、ブラウザで動くデザイナーを持つオープンソースの PDF 生成ライブラリです。そのデザイナーが保存するテンプレート(JSON のファイル。土台の PDF か白紙のページと、ミリ単位で場所を決めたフィールド)も、そのままテンプレートになります。pdfme の生成器と同じ規則で埋めます。

bdf fill -data invoice.json template.json invoice.bdf
bdf template compile template.json invoice.bdft   # デザイナーで入れてあった内容のページになる
pdfme のフィールド データ
text、select 文字列か数値(select は選択肢の 1 つ)
multiVariableText 変数のオブジェクト。"customer": {"company": "…", "person": "…"}
date、time、dateTime 日付(2026-10-14、2026/10/14)、時刻(09:30)、またはその両方。フィールドの書式で描く(月や曜日の名前は英語と日本語)
checkbox、radioGroup、circleMark true か false
image、signature、svg ファイルのパスか data: URL(svg は SVG そのものでもよい)
qrcode、ean13、ean8、code39、code128、nw7、itf14、upca コードの内容
table 行の配列。行は、列の名前をキーにしたオブジェクトか、セルの配列
list 文字列の配列(先頭にタブを置くと 1 段深くなる)

QR コードとバーコード

画像のタグに qr か barcode:種類 を付けると、値を符号化したコードを画像の代わりに描きます。置く場所は画像と同じです。Excel ではタグを書いたセル(結合セルならその範囲)、Word と PowerPoint では代替テキストにタグを書いた画像の枠です。

タグ 描くもの
{{%url | qr}} QR コード(モデル 2、バージョン 1〜40)。誤り訂正レベルは M。qr:L、qr:Q、qr:H で変える
{{%jan | barcode:jan}} JAN / EAN-13。12 桁の値にはチェックデジットを付け、13 桁の値は確かめる。barcode:ean13 も同じ
barcode:ean8、barcode:upca JAN / EAN-8(7 桁か 8 桁)、UPC-A(11 桁か 12 桁)
barcode:code39 Code 39。数字、英大文字、空白、- . $ / + %。前後の * は自動で付く
barcode:code128 Code 128。ASCII の文字。コードセット A・B・C は自動で選ぶ
barcode:gs1-128 GS1-128。値は (01)04901234567894(10)ABC のように、AI を括弧で書く
barcode:itf、barcode:itf14 ITF(数字。奇数桁なら先頭に 0 を足す)、ITF-14(13 桁ならチェックデジットを付ける)
barcode:nw7 NW-7(Codabar)。数字と - $ : / . +。スタートとストップ(A〜D)を値に書かなければ A で挟む

フォームを使わず、同じ描画を直接生成することもできます。

symbol, err := barcode.Encode("jan", "490123456789", nil) // チェックデジットを補い 4901234567894 に
// err を確認してから、バーと数字を一緒に描きます。
drawing := symbol.Object(113, 36)

バーだけ描く場合は &barcode.Options{NoText: true} を渡します。Symbol.At と Symbol.Rects は、数字の描画とは独立して符号化されたモジュールだけを返します。

PDF にする

bdf pdf は、BDF の文書を PDF に書き出します。帳票に限らず、どの文書でも使えます。

bdf pdf invoice.bdf invoice.pdf
bdf pdf -skip background -skip master slip.bdf slip.pdf   # 印刷済みの用紙に重ねて刷る
bdf pdf -tagged report.bdf report.pdf                      # タグ付き PDF
bdf pdf -pdfa 2a invoice.bdf invoice.pdf                   # 保存用の PDF/A

タグ付き PDF と PDF/A

タグ付き PDF(-tagged)は、ページの見た目とは別に、文書の構造を持つ PDF です。どの文字が見出しで、どれが段落、箇条書きの項目、表のセルなのか、どの順に読むのか、図は何を表しているのかが入ります。スクリーンリーダーはこの構造をたどって読み上げ、PDF から文字を取り出すツールは段落や表を崩さずに取り出せます。

PDF/A(-pdfa)は、長期保存のための PDF です。10 年後に開いても同じに見えるように、開くマシンに頼るものを一切持ちません。

レベル 約束すること
2b どこで開いても同じに見える。すべてのフォントを埋め込み、色は PDF に入れた出力インテント(sRGB)で決まり、メタデータが規格を名乗る
2u 加えて、文字が Unicode に対応する。検索とコピーで正しい文字が得られる
2a 加えて、タグ付きである(-tagged を兼ねる)
3b、3u、3a 規格の第 3 部で、同じ内容

データ

データは JSON の値でできた 1 つのオブジェクトです。-data を繰り返して組み立てます。

書き方 読み方
-data order.json、-data order.yaml オブジェクト。後のファイルが同じ名前の値を置き換える
-data items=lines.csv CSV・TSV を、1 行目を名前にしたオブジェクトのリストとして、その名前に置く。文字コードと区切りは自動で判定(Shift_JIS も読む)
-data book.xlsx、-data items=book.xlsx ブックのシートごとのリスト(シート名が名前)。名前を付けた 1 シートのブックはそのリスト
-data customer.address=addr.json JSON・YAML の値を、その名前に置く

CSV の値は、JSON の数値と同じ書き方のものだけが数値になります(007 や 1,234 は文字列のまま)。true と false は真偽値、空は値なしです。画像はファイルのパス(-files のディレクトリ、既定ではテンプレートのディレクトリから)か data: URL で渡します。パスはそのディレクトリの中にとどまります。.. や外へ出るリンクはたどりません。

データにない名前のタグは空にして警告を出します。-strict を付けるとエラーにします。

レコードごとに 1 つずつ帳票を作るには -each を使い、出力のパスにタグを書きます。

bdf fill -each orders.csv -data items=lines.csv invoice.xlsx 'out/{{customer}}-{{no}}.bdf'

1 つの文書に全員分をまとめるなら、テンプレートの側で {{#orders | pages}} を使います。

コンパイル済みテンプレート(.bdft)

bdf template compile はテンプレートを検査し(タグの閉じ忘れや、行をまたぐ区間の書き間違いを、セルの番地や段落・図形の場所つきで報告します)、コンパイル済みテンプレートを書き出します。中身は BDF の文書で、サンプルデータで穴埋めしたページ(サンプルがなければタグのままのページ)を見せ、テンプレートそのものと、タグが引く名前の一覧を持ちます。ビューアやサムネイルでそのまま開け、bdf fill にテンプレートの代わりに渡せます。

bdf template compile -sample sample.yaml invoice.xlsx invoice.bdft
bdf template schema invoice.bdft        # タグが引く名前を JSON で
bdf fill -data order.yaml invoice.bdft out.bdf

データの JSON Schema と入力フォーム

テンプレートが求めるデータの形は、JSON Schema として取り出せます。入力フォームを自動で作るライブラリや、データの検証にそのまま渡せます。フォームのページの入力欄も、このスキーマから作っています。

bdf template schema -json-schema invoice.xlsx invoice.schema.json
bdf template schema -json-schema -sample sample.yaml invoice.xlsx   # サンプルで型と場所を確かめる

タグは値の型を書かないので、スキーマはタグから推し量ったものです。

テンプレートの書き方 スキーマ
{{name}} 文字列か数値("type": ["string", "number"])
{{total | fmt:"#,##0"}} 数値。日付の表示形式(yyyy/mm/dd)なら "format": "date" の文字列
{{customer.name}} オブジェクト customer のプロパティ name
中に名前か {{.}} がある区間、| rows・| pages の区間 配列。項目はオブジェクトか値。枠の区間(slots)は maxItems が枠の数
それ以外の区間({{#paid}}■{{/paid}}) 真偽値
{{%photo}} 文字列(ファイルのパスか data: URL)
{{%url | qr}}、{{%jan | barcode:jan}} 文字列(コードの内容)

Go から使う

data, err := form.LoadData(
	form.Source{FileName: "order.yaml", Data: order},
	form.Source{Name: "items", FileName: "lines.csv", Data: lines})
res, err := form.Fill(template, size, &form.Options{Data: data})
err = res.Doc.WriteSingle(out)

form.Fill は、テンプレートの形式を中身から判定します(Office のファイル、PDF フォーム、pdfme のテンプレート、コンパイル済みテンプレート)。form.Fields はタグが引く名前を、form.SchemaOf はデータの JSON Schema を、form.Compile はコンパイル済みテンプレートを返します。形式が決まっていれば xlsx.Fill、docx.Fill、pptx.Fill を直接呼べます。PDF は pdfbdf.Write で書き出します。

res, err := pdfbdf.Write(out, doc, &pdfbdf.Options{})                          // PDF
res, err = pdfbdf.Write(out, doc, &pdfbdf.Options{Tagged: true, PDFA: "2a"}) // タグ付きの PDF/A-2a

知っておくこと