請求書や報告書のように見た目が決まっている帳票は、レイアウトをコードで組み立てるより、 Excel で作ったテンプレートを読み込んで値だけ差し込むほうが確実です。 罫線・フォント・列幅・印刷設定はテンプレートが持ったまま往復します。
この方法は画面を必要としません。サーバー上のバッチ処理でもそのまま動きます (ヘッドレスで使う)。
1. テンプレートを Excel で作る
通常どおり Excel で作り、.xlsx で保存します。
差し込む場所は空欄にしておき、書式だけ設定しておきます。金額セルに #,##0、
日付セルに yyyy/mm/dd を設定しておけば、コードからは数値を入れるだけで済みます。
印刷の用紙・余白・印刷範囲も Excel 側で設定しておきます。PrintSettings として
読み込まれ、PDF 出力にそのまま効きます(印刷とページ設定)。
2. 読み込んで差し込む
using unvell.ReoGrid.Core;
using unvell.ReoGrid.IO.Excel;
using unvell.ReoGrid.IO.Pdf;
// テンプレートは書式・罫線・数式・印刷設定を持ったまま読み込まれる
Workbook wb = XlsxReader.Read("invoice-template.xlsx");
Worksheet ws = wb[0];
ws.SetText(1, 4, "2026-08-19"); // E2 発行日
ws.SetText(3, 0, "株式会社サンプル 御中"); // A4 宛名
XlsxWriter.Write(wb, "invoice-0001.xlsx");
PdfExporter.Export(wb, "invoice-0001.pdf");
読み込みは書式を保ったまま行われます。SetText / SetNumber はセルの値だけを
変えるため、テンプレートで設定した書式・罫線・表示形式はそのまま残ります。
3. 位置は定義名で指定する
行番号を直接書くと、テンプレートに 1 行足しただけでコードが壊れます。 Excel 側で範囲に名前を付けておき、名前で引いてください。
Workbook wb = XlsxReader.Read("invoice-template.xlsx");
Worksheet ws = wb[0];
// テンプレート側で定義しておいた名前で位置を指定する。
// レイアウトを変えても、コードを直す必要がない
RangePosition body = ws.ResolveRange("明細");
var lines = new[]
{
("設計費", 400_000d),
("実装費", 850_000d),
("保守費", 120_000d),
};
for (int i = 0; i < lines.Length; i++)
{
ws.SetText(body.Row + i, body.Col, lines[i].Item1);
ws.SetNumber(body.Row + i, body.Col + 1, lines[i].Item2);
}
Excel での定義は「数式」タブ →「名前の定義」です。XLSX の定義名はそのまま読み込まれます (定義名)。
これでテンプレートの行を増やしても、名前が同じ範囲を指していればコードは無変更です。
4. 出力する
XlsxWriter.Write(wb, "invoice-0001.xlsx"); // Excel で開ける
PdfExporter.Export(wb, "invoice-0001.pdf"); // そのまま配布・印刷できる
PDF は日本語フォントを埋め込むため、フォントが入っていない Linux サーバーでも 文字化けしません(PDF 出力)。
テンプレートをアプリに埋め込む
テンプレートファイルを別途配布したくない場合は、プロジェクトの埋め込みリソースにします。
// テンプレートをアセンブリに埋め込んでおけば、配布はアプリ 1 つで済む
Workbook wb = XlsxReader.Read(templateStream);
.csproj 側:
<ItemGroup>
<EmbeddedResource Include="Templates\invoice-template.xlsx" />
</ItemGroup>
using Stream s = typeof(Program).Assembly
.GetManifestResourceStream("MyApp.Templates.invoice-template.xlsx")!;
Workbook wb = XlsxReader.Read(s);
明細の行数が可変のとき
明細が 3 行のときと 30 行のときがある帳票では、テンプレートに十分な行数の書式を 用意しておき、余った行を隠すのが最も簡単です。
for (int r = body.Row + lines.Length; r <= body.EndRow; r++)
ws.Rows.SetHidden(r, true);
非表示行は印刷・PDF でも高さ 0 として扱われ、紙にも出ません (行と列)。
行を挿入して増やすこともできますが、挿入した行は上の行の書式を引き継がないため、 罫線の設定が別途必要になります。隠すほうを勧めます。
テンプレートで使えないもの
XLSX から読み込めないものは、テンプレートに入れても失われます。
- チャート・図形・テキストボックス — V5 は未対応
- ピボットテーブル — V5 は未対応
- スレッド形式のコメント — 従来の「メモ」は往復します
- 共有数式 — 展開に未対応
対応状況は XLSX 対応状況 にまとめてあります。 ロゴなどの画像は読み込めます(画像)。