マニュアル一覧PDFで読む

Reports.web WPF/.NET版 プログラマーズガイド/APIリファレンス

製品版のライセンス配置と ReportsProductInfo.LoadLicense の使い方は、製品版を使い始めるをご覧ください。.NET Framework互換版と.NET 10版で同じ手順です。体験版では登録不要です。

Reports.web WPF/.NET版は、C#から帳票定義へ業務データを差し込み、プレビュー、PDF、ページ単位のSVG/PNG、プリンター出力、印刷データの保存まで行うための帳票エンジンです。WindowsではWPF版デザイナーとプレビュアーを利用でき、Webサーバーでは画面を起動せずに帳票を生成できます。

この版はブラウザー内のWASMを呼び出すC#ラッパーではありません。.NETで実装した帳票エンジンをC#から直接呼び出す構成です。共通WASMを使うWebアプリケーションについては、別冊の「C#/.NET WASM言語ラッパー プログラマーズガイド」を参照してください。

この一冊では、次の処理を実装できることを目標にします。

画面の使い方は、WPF版 帳票デザイナー操作ガイドとWPF版 帳票プレビュアー操作ガイドを参照してください。

1. 最初に理解する3つのデータRM001

帳票は「レイアウト」と「今回印刷する値」を分けて扱います。

データ C#の型 内容
帳票定義 ReportDocument 用紙、文字、罫線、画像、バーコード、明細の繰返しなどのレイアウト
作成中の印刷データ PrintDataDocument 帳票定義と、ページごとの値・属性
出力用の完成ページ IReadOnlyList<ResolvedReportPage> 値を帳票定義へ反映した、PDF・プレビュー・印刷に渡せるページ一覧

主なファイル形式は次のとおりです。

拡張子 内容 主な用途
.prepdj JSON形式の帳票定義 デザイナーで作成し、C#から読み込む
.prepd 従来形式の帳票定義 既存帳票の読み込み・移行
.prepej 帳票定義と値を含む自己完結した印刷データ 保存、受け渡し、再表示、再印刷
.prepe 従来形式の印刷データ 既存データの読み込み

通常は次の順序で処理します。

  1. ReportCreator.Create() で帳票処理を開始する
  2. LoadDefFile(...) でPREPDJまたはPREPDを読む
  3. PageStart()、Write(...)、PageEnd() でページを作る
  4. PDF、SVG、PNG、プレビュー、プリンターのいずれかへ出力する
  5. 後から同じ帳票を開く必要があれば SaveJson(...) でPREPEJを保存する

2. 開発環境と参照先RM002

2.1 推奨環境RM003

Windowsアプリへ組み込むDLLは、次の2系統から選びます。.NET 10へ移行しないと帳票エンジンを使えない、ということではありません。

お使いのアプリの環境 使用する配布SDK
.NET Framework 4.7.2以降 SDK/net472:Framework互換版
.NET 5~9/Windows SDK/net472:Framework互換DLLを参照して使用
.NET 10/Windows SDK/net10:.NET 10版

どちらもReports.webの帳票エンジンとWPFプレビュアーです。従来のエンジンへ置き換える方式ではありません。使用する主なクラス・メソッドは共通で、Pao.Reports.Maui.Engine.dll と Pao.Reports.Wpf.Windows.dll および依存DLLを、選んだSDKからまとめて参照します。2系統のDLLを混ぜないでください。

Framework互換版は、net472アプリでの実請求書PDF出力・プレビュー表示と、同じ配布DLLを.NET 8/9アプリから直接参照したPDF出力を確認しています。.NET 8でのプレビュー表示も確認済みです。.NET 5~9のすべての版・依存パッケージの組み合わせを実測した、という意味ではありません。既存システム独自の依存関係で問題がある場合や、.NET Framework 4.7.1以前で使用する場合は、環境とエラー内容を添えて info@pao.ac へご相談ください。

配布する01~04の学習サンプルの標準プロジェクトは.NET 10です。Linuxで動くWeb APIも.NET 10の帳票エンジンを使用し、Windows専用のWPFプレビュアーは使用しません。デザイナーは帳票を作るための別ツールであり、アプリが参照するエンジンDLLの選択とは分けて考えます。

2.2 配布SDKを参照する際の注意RM004

SDK/net472/README.md にFrameworkアプリのbinding redirect設定と、.NET 8から参照する場合の例を記載しています。製品DLLだけでなく、同じSDKの System.Text.Json.dll などの依存DLLも参照し、出力先へコピーしてください。実行環境に入っている同名DLLが、そのまま必要な版と一致するとは限りません。独自のアセンブリ読み込み処理を追加する必要はありません。

SkiaのネイティブDLLはアプリのビット数に合わせて配置します。x64アプリでは runtimes/win-x64/native/libSkiaSharp.dll、x86アプリでは runtimes/win-x86/native/libSkiaSharp.dll を実行ファイルと同じフォルダーへコピーします。具体的な参照例は、選んだSDKのREADMEをご覧ください。

2.3 同梱SDKで最初のプロジェクトを作るRM005

サンプル01をまだ試していない場合は、先にそのREADMEから起動してみてください。ここでは一歩進めて、自分の小さなC#プロジェクトから帳票を作ります。製品の開発リポジトリやソースコードは必要ありません。

Windows x64と.NET 10 SDKを使います。展開したNative/WPF-DotNetの中に、SDK・samplesと並ぶFirstReportフォルダーを作り、次の2ファイルを置きます。既存のサンプルやSDKは移動しません。

WPF-DotNet/
├─ SDK/net10/          ← 同梱ライブラリ
├─ samples/01.…/       ← 同梱の帳票定義を使う
└─ FirstReport/        ← 自分で作るフォルダー
   ├─ FirstReport.csproj
   └─ Program.cs

FirstReport.csprojの全文です。依存DLLとWindows x64用の描画DLLも出力先へコピーします。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
    <UseWPF>true</UseWPF>
    <PlatformTarget>x64</PlatformTarget>
    <ImplicitUsings>enable</ImplicitUsings>
    <ReportsSdkDir>$(MSBuildProjectDirectory)\..\SDK\net10</ReportsSdkDir>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Pao.Reports.Maui.Engine" HintPath="$(ReportsSdkDir)\Pao.Reports.Maui.Engine.dll" Private="true" />
    <Reference Include="Pao.Reports.Wpf.Windows" HintPath="$(ReportsSdkDir)\Pao.Reports.Wpf.Windows.dll" Private="true" />
    <Content Include="$(ReportsSdkDir)\**\*" Link="%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" Visible="false" />
    <Content Include="$(ReportsSdkDir)\runtimes\win-x64\native\libSkiaSharp.dll" Link="libSkiaSharp.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
    <Content Include="..\samples\01.あっという間に帳票出力\quick-start.prepdj" Link="quick-start.prepdj" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>
</Project>

Private="true" は両方の参照に指定してください。SkiaSharpなどの依存DLLも実行時の依存関係へ含めるために必要です。Contentでファイルをコピーするだけでは、実行時に依存DLLを読み込めないことがあります。

同梱DLLの役割は次のとおりです。

Framework互換版を使う場合は、01のCompat-net472/QuickStart.Compat.csprojを参照例にしてください。対象フレームワークと参照SDK、binding redirectが異なります。上のプロジェクトのDLLだけを混ぜて差し替えないでください。

3. 最初の帳票プログラムRM006

前章で作ったFirstReportに、次の内容をProgram.csとして保存します。同梱のquick-start.prepdjを読み、実在する4つのオブジェクトへ値を入れます。

using System;
using System.IO;
using System.Linq;
using Pao.Reports.Maui.Engine.Rendering;
using Pao.Reports.Maui.Engine.Reports;
using Pao.Reports.Wpf.Windows;

// WPFでWindows互換PDFとSwapPdfImageを使う正規の作成入口
var report = WindowsReportFactory.Create();
report.LoadDefFile(Path.Combine(AppContext.BaseDirectory, "quick-start.prepdj"));

report.PageStart();
report.Write("Title", "C#から初めての帳票");
report.Write("Customer", "株式会社サンプル 御中");
report.Write("Message", "同じ帳票をPDFと印刷データへ保存します。");
report.Write("Amount", "120,000 円");
report.PageEnd();

Directory.CreateDirectory("output");

// 帳票定義と値を含む、再表示可能な印刷データ
report.SaveJson("output/first-report.prepej");

// Windows互換PDF。解決済みの全ページを1つのPDFにする
var pages = report.ResolvePages().Select(page => page.Document).ToArray();
var pdf = new WindowsPdfExporter().Render(
    pages, new PdfRenderOptions { Title = "初めての帳票" });
File.WriteAllBytes("output/first-report.pdf", pdf);
Console.WriteLine(Path.GetFullPath("output/first-report.pdf"));

FirstReportをエディターで開き、そのフォルダーのターミナルで実行します。

dotnet run --project FirstReport.csproj

output/first-report.pdfを開き、タイトル・会社名・金額を確認します。Program.csの文字列を一つ変えて再実行すると、同じファイルに変更が反映されます。output/first-report.prepejはWPFプレビュアーの[帳票データを開く]から再表示できます。DBやWebサーバーは不要です。

Visual Studio 2026ではこのFirstReport.csprojを開いてF5でも実行できます。作業ディレクトリが違う場合にも保存先が分かるよう、プログラムはPDFの絶対パスをコンソールに表示します。

Write(...) の名前には、デザイナーで設定したオブジェクト名を指定します。存在しない名前を渡すと例外になります。PageStart() と PageEnd() の間だけ値を書き込めます。

WPFアプリでは WindowsReportFactory.Create() を使用してください。この入口で作成した Report は、通常時と SwapPdfImage=true のどちらも同じWindows GDI+描画、DPI、ページ寸法、フォント経路を使用します。汎用の ReportCreator.Create() または new Report(...) は移植可能なSkia PDF経路であり、SwapPdfImage=true を指定すると PlatformNotSupportedException になります。設定だけを黙って無視したり、別の描画方式へ切り替えたりはしません。

4. 値と明細を設定するRM007

ここからは業務帳票へ応用するための部分例です。「顧客名」「商品名」などは説明用のオブジェクト名で、01の定義にそのまま追加で書ける名前ではありません。ご自身の帳票定義にある名前へ合わせてください。実際の明細・DB・複数ページ処理は02サンプルのソースで確認できます。

4.1 1ページに1つの値RM008

文字の枠からのあふれと、明細行がページからあふれる処理は別です。通常文字は枠の寸法・横書き/縦書き・折返しの設定で配置します。長い文字だけで自動改ページはしません。伸縮文字は文字数・バイト数の条件を満たすと枠に合わせて変形し、高さ固定なら横方向を伸縮します。PDF 専用の自動折返し・高さによる切取りは、その設定を使う出力経路に適用されます。書体で字幅・行送りが変わるため、最長の値で画面と PDF を確認してください。

Write(name, value, index) の番号は繰返しでない物体も1からです。同名の2個目は2です。0も最初の物体として受け付けます。繰返し回数を超えた行も同じ間隔で表の下へ描きます。自動改ページはしないので、用紙を越える場合は利用側でページを追加します。

report.PageStart();
report.Write("伝票番号", "INV-2026-0012");
report.Write("顧客名", "株式会社サンプル");
report.Write("発行日", DateTime.Today.ToString("yyyy年M月d日"));
report.Write("合計金額", 120000.ToString("N0") + " 円");
report.PageEnd();

表示形式は業務プログラム側で文字列へ整形してから渡すと明確です。日付や金額の書式を帳票ごとに変える場合も、値の作成処理を一か所へまとめると保守しやすくなります。

4.2 繰返し明細RM009

繰返し設定されたオブジェクトへは、1から始まる明細番号を指定します。

report.PageStart();

var lines = new[]
{
    new { Name = "商品A", Quantity = 2, Price = 50000 },
    new { Name = "商品B", Quantity = 1, Price = 20000 }
};

for (var index = 0; index < lines.Length; index++)
{
    var row = index + 1;
    report.Write("商品名", lines[index].Name, row);
    report.Write("数量", lines[index].Quantity.ToString("N0"), row);
    report.Write("単価", lines[index].Price.ToString("N0"), row);
}

report.PageEnd();

縦横2方向の繰返しは、互換形式の1始まりインデックスを使う Write(name, value, indexX, indexY)、または0始まりの WriteAt(name, value, indexX, indexY) を使用します。2つを同じ処理で混在させないでください。

4.3 DBの検索結果を流し込むRM010

report.PageStart();

await using var command = connection.CreateCommand();
command.CommandText = "select product_name, quantity, unit_price from invoice_line where invoice_id = @id";
var parameter = command.CreateParameter();
parameter.ParameterName = "@id";
parameter.Value = invoiceId;
command.Parameters.Add(parameter);

await using var reader = await command.ExecuteReaderAsync();
var row = 1;
while (await reader.ReadAsync())
{
    report.Write("商品名", reader.GetString(0), row);
    report.Write("数量", reader.GetInt32(1).ToString("N0"), row);
    report.Write("単価", reader.GetDecimal(2).ToString("N0"), row);
    row++;
}

report.PageEnd();

DB列名と帳票オブジェクト名の対応は、このようにC#コード上で明示すると、DBまたは帳票の変更箇所を見つけやすくなります。

4.4 複数ページを作るRM011

PageEnd 前に PageStart を呼ぶと、編集中のページを捨て、新しいページを作ります。例外にはなりません。Write などの例外後に PageEnd せず SavePdf・SaveData・SaveXml・SaveJson・SVG 保存を行うと、完成済みページだけを保存します。印刷・プレビュー・LoadDefFile は編集中のページがあると例外になります。

PageStart() から PageEnd() までが1ページです。同じ帳票定義でページを増やす場合は、この組合せを繰り返します。途中で LoadDefFile(...) を呼び、表紙と明細などで帳票定義を切り替えることもできます。

ファイル名を持たない入力には LoadDefFile(Stream stream, string name) を使用できます。ストリームは呼出側の所有のままで、読込み後も破棄されません。定義名は GetDefFileName(page) で取得できます。ページ生成後の GetDefFileName は、ページ別名ではなく最初に読み込んだ定義名を返します。

作り直す場合は、ページ作成中でないことを確認して ClearData() を呼びます。帳票定義は保持され、完成済みページだけが消去されます。

4.5 空文字で罫線・図形を消すRM012

罫線・四角・円(Line、DynamicLine、Square/Rectangle、Circle/Ellipse)は、値で表示を切り替えられます。

書く値 罫線・図形
""(空文字) 描かない(消える)
null、または何も書かない 定義どおり描く
""以外の値(例 "1") 描く。値は表示には使わない

繰返しの行でも、1つだけの図形でも同じです。最初に書いたときも、別の値のあとに書いたときも同じです。表の行を途中で終える、合計行の上だけ線を引かない、条件で枠を消す、などに使います。次の明細線は、定義にある罫線のオブジェクト名の例です。

report.Write("明細線", "", row); // この行の罫線を消す

PREPEJへ保存した印刷データでも同じです。図形の値が""なら、Drawingがtrueでも描きません。

文字の欄(Text)は別の規則です。値が""またはnullなら、文字も枠線も背景色も描きません。SVG・印刷・PDFで同じです。

4.6 何も書かない繰返しは1行目だけ描くRM013

繰返し(RepeatCountが2以上)のある物体に、そのページで何も書かないと、定義どおり1つだけ、1行目の位置に描きます。行の数だけ並べては描きません。

一部の行にだけ書くと、書いた行だけ描きます。書かなかった行は描きません。1行目に書かなければ、1行目も描きません。

表の罫線や枠を全部の行に描くには、描きたい行それぞれにnull(値なし)を書きます。罫線・図形はnullなら描き、""なら消えます(4.5)。明細のループに次の1行を足します。値を省くWrite(name, index)は、値なし(null)を書きます。

report.Write("明細線", row); // この行の罫線を描く(値なし)

文字の欄は、nullを書くとその行の文字が空になります。定義の文字を出したい行には、その文字を書きます。PREPEJへ保存した印刷データでも同じです。

5. 帳票全体をPDFへ出力するRM014

PDFは、解決済みの全ページを1つのPDFファイルへまとめます。SVG/PNGのページ単位出力とは扱いが異なります。

5.1 Windows互換PDFRM015

半角・全角空白も検索・コピーできます。ただし Arial など欧文 PDF 書体の欄の全角空白は検索で取得できません。縦書きと伸縮文字は輪郭に検索用の文字を重ねるため検索・コピーできます。縦書きは列ごとの文字列です。JAN などバーコードの下の数字は文字として取得できません。配布フォントで補った字も使った字だけを埋め込みます。

Windows上でGDI+の描画とPDFライターを利用する経路です。

using Pao.Reports.Maui.Engine.Rendering;
using Pao.Reports.Wpf.Windows;

var documents = report.ResolvePages()
    .Select(page => page.Document)
    .ToArray();

var exporter = new WindowsPdfExporter();
var bytes = exporter.Render(
    documents,
    new PdfRenderOptions
    {
        Title = "請求書",
        Progress = (current, total) =>
            Console.WriteLine($"{current}/{total}")
    },
    cancellationToken);

await File.WriteAllBytesAsync("output/invoice.pdf", bytes, cancellationToken);

各ページを画像化してからPDFへ格納する場合は RenderImagePdf(...) を使用します。通常PDFとImagePDFは別の出力方式です。

5.2 ポータブルなSkiaSharp PDFRM016

静的 TrueType は使った文字と参照先の複合字形をサブセット化します。CFF・可変フォント・埋込ビットマップを持つ字体は元の face を保持します。

帳票エンジンの SavePdf(...) はSkiaSharp経路を使用し、帳票全体を出力します。

report.SavePdf(
    "output/invoice-skia.pdf",
    new PdfRenderOptions { Title = "請求書", RasterDpi = 144 },
    cancellationToken);

状態を持つ Report APIから直接PDFを保存する場合は、WindowsReportFactory.Create() で作成し、必要に応じて report.SwapPdfImage = true を設定してから SavePdf(...) を呼びます。false(既定値)と true は同じWindows描画結果を使い、true はPDF画像ストリームを出力ごとに固有の一時領域へ退避して保持メモリーを抑えます。一時ファイルは成功、取消、失敗の各経路で削除されます。最終PDFの戻り値や1画像分の処理領域、GDI+の一時使用量までなくす機能ではありません。

SwapPdfImage は、大きな画像を多数のページへ配置する場合のメモリー負荷を減らすためのbool型プロパティです。画像のサイズや品質を下げる設定ではありません。一時領域への書込み権限と空き容量が必要で、ディスクI/Oは増加します。通常の小さな帳票では既定の false を使用できます。強制終了や電源断では後処理が実行されないため、一時ファイルの削除は保証されません。

バイト配列、Stream、非同期APIも用意されています。

byte[] bytes = await report.SavePdfAsync(
    new PdfRenderOptions { Title = "請求書" },
    cancellationToken);

Windowsで画面・印刷と同じGDI+の見た目を優先するときは WindowsPdfExporter、Windowsに依存しないサーバー処理には Report.SavePdf(...) または SkiaPdfRenderer を選びます。

6. SVG/PNGをページ単位で出力するRM017

6.1 SVGRM018

GetSvgPages() は、1ページにつき1つのSVG文字列を返します。

var svgPages = report.GetSvgPages();

for (var index = 0; index < svgPages.Count; index++)
{
    var path = $"output/invoice-{index + 1}.svg";
    await File.WriteAllTextAsync(path, svgPages[index], cancellationToken);
}

解決済みページから直接作る場合は SvgReportRenderer.Render(...) を使います。

var svgRenderer = new SvgReportRenderer();
var pages = report.ResolvePages();
var svg = svgRenderer.Render(pages[0].Document); // 先頭ページ

SaveSVGFile(...) は全ページを閲覧・印刷できるHTMLとして保存します。ページごとの純粋な .svg が必要な場合は、上記の GetSvgPages() または SvgReportRenderer を使用してください。

6.2 PNGRM019

Windows版では、解決済みページを GdiPageRenderer で1ページずつ画像化します。

using System.Drawing.Imaging;
using Pao.Reports.Wpf.Windows;

var renderer = new GdiPageRenderer();
var pages = report.ResolvePages();

for (var index = 0; index < pages.Count; index++)
{
    using var bitmap = renderer.RenderBitmap(pages[index].Document, dpi: 144);
    bitmap.Save($"output/invoice-{index + 1}.png", ImageFormat.Png);
}

PDFは全ページ、SVG/PNGはページ単位、という違いを意識すると、Web APIのURLや保存先を設計しやすくなります。

7. PREPEJを保存・再利用するRM020

PREPEJ の共通属性は横揃えを Left・Center・Right、縦揃えを Top・Middle・Bottom と書きます。大文字の旧表記も読み込みます。WPF/.NET は大小だけの違いを補正して知らせ、未知の値は適用せずに知らせます。

PREPEJは、帳票定義とページごとの値をまとめた自己完結形式です。元のPREPDJやDBへ再接続しなくても、ファイルだけで再表示・PDF作成・再印刷できます。

7.1 保存するRM021

report.SaveJson("output/invoice.prepej");

XML形式が必要な場合は SaveXml(...) を使用します。通常の新規処理にはJSON形式のPREPEJを推奨します。

7.2 読み直すRM022

var reopened = ReportCreator.Create();
reopened.LoadData("output/invoice.prepej");

Console.WriteLine($"{reopened.PageCount}ページ");
reopened.SavePdf("output/invoice-reprint.pdf");

データ層だけで扱う場合は PrintDataStore を使用します。

var data = PrintDataStore.LoadJson("output/invoice.prepej");
PrintDataStore.Validate(data);
var resolved = PrintDataStore.Resolve(data);

LoadData(...) と PrintDataStore.Load(...) はXML/JSONを内容から判定できます。公開Web APIで受け取る場合は、形式判定だけに頼らずファイルサイズ、ページ数、画像、診断結果も検証してください。

従来形式(.prepe)とのデータ交換には report.SaveData() を使用します。戻り値はPrintData XMLをRFC 1950 zlibで圧縮したバイト列で、ZIPファイルではありません。LoadData(byte[]/Stream) はこの圧縮形式に加えて従来のXML/JSONも読み込めます。入力と展開後データの既定上限は256 MiBで、zlibヘッダー、終端、Adler-32 checksumも検証します。より小さい業務上限、または正当な大容量帳票の上限を明示する場合は LoadData(input, maxExpandedBytes) を使用してください。値は正のバイト数で、失敗時に既存の帳票状態は置換されず、呼出側のStreamも破棄されません。SaveJson(...) のPREPEJは編集・移行に適した現行形式、SaveData() は従来形式(.prepe)でのデータ交換用として使い分けてください。公開ZIP圧縮APIは提供していません。

8. WPFプレビュアーをC#から開くRM023

ReportPreviewWindow は、業務アプリケーションから所有して表示できる公開WPF Windowです。PREPEJの保存・再読込を有効にする場合は PrintDataDocument を渡します。

using Pao.Reports.Maui.Engine.Data;
using Pao.Reports.Wpf.Windows;

var data = PrintDataStore.LoadJson("output/invoice.prepej");
var preview = new ReportPreviewWindow(data, new ReportPreviewOptions
{
    Title = "請求書プレビュー",
    InitialPage = 1,
    InitialZoomMode = PreviewZoomMode.FitPage,
    InitialZoomPercent = 100,
    InitialDataPath = "output/invoice.prepej",
    InitialPdfPath = "output/invoice.pdf",
    ShowOpenDataCommand = true,
    ShowSaveDataCommand = true,
    ShowPdfCommand = true,
    ShowPrintCommand = true,
    ShowPageNavigation = true,
    AllowDataDrop = true
});

preview.Owner = this;
preview.ShowDialog();

主な公開操作は次のとおりです。

操作 API
ページ移動・倍率 GoToPage(...)、SetZoom(...)、PreloadPages(...)
印刷データ LoadData(...)、SaveData(...)
検索 Search(...)、ClearSearch()
帳票全体のPDF ExportPdfAsync(...)、ExportImagePdfAsync(...)
現在ページの画像 ExportCurrentPngAsync(...)、ExportCurrentSvgAsync(...)
表示領域 SetChromeVisibility(...)
操作UIの一括表示・非表示 SetPreviewChromeVisible(bool)

8.1 プレビューだけを表示し、アプリ側から操作するRM024

preview.SetPreviewChromeVisible(false); // 設定を含むツールバーとステータスを隠す
await preview.ExportPdfAsync("output/invoice.pdf"); // 非表示中もPDFを保存できる
preview.SetPreviewChromeVisible(true);  // 全操作UIを再表示する

preview は上で作成した ReportPreviewWindow、出力フォルダーは作成済みとします。表示切り替えはWPFのUIスレッドから呼び出します。通常の初期表示は変えず、false を指定したときだけ帳票表示に絞ります。設定ボタンも隠れるので、アプリ側に「ツールバーを表示」などの操作を用意すると分かりやすくなります。プレビュアーにフォーカスがある状態では Ctrl+Shift+T でも操作UIと帳票表示を再表示できます。OSのウィンドウ枠は残ります。このAPIはFramework互換版と.NET 10版の両方で利用できます。

ReportPreviewWindow は再利用可能なWindowです。別WindowのVisual Treeへ直接挿入する公開UserControlではありません。より詳しい制約と表示設定はプレビュアー操作ガイドを参照してください。

9. Windowsプリンターへ出力するRM025

using Pao.Reports.Wpf.Windows;

var documents = report.ResolvePages()
    .Select(page => page.Document)
    .ToArray();

var options = new WindowsPrintOptions
{
    PrinterName = "",
    Copies = 1,
    LeftHundredthsInch = 0,
    RightHundredthsInch = 0,
    TopHundredthsInch = 0,
    BottomHundredthsInch = 0,
    ScaleToFitMargins = false
};

using var printDocument = new GdiPageRenderer()
    .CreatePrintDocument(documents, "請求書", options);
printDocument.Print();

空の PrinterName は既定プリンターを使用します。実際の印刷前には、WPFプレビュアーの印刷設定画面でプリンター、部数、余白、余白内への縮小を利用者に確認してもらう方法もあります。

サーバー処理ではプリンター名を外部入力から無制限に指定させず、利用可能なプリンターをアプリケーション側で限定してください。

CutByPageでページ単位の印刷ジョブに分けるRM026

CutByPage は、レシート・ラベルプリンターの用紙カットに用いる bool型のプロパティ です。メソッドではありません。

帳票の改ページをなくしたり、ページをつなげたりする設定ではありません。ページごとに印刷ジョブを分ける方式であり、実際のカット動作はプリンターとドライバーの設定にも依存します。

Reports Web WPF/.NET版では、report.CutByPage および WindowsPrintOptions.CutByPage を利用できます。WindowsPrintOptions.FromPage/ToPage で1始まり・両端を含む印刷範囲を選択した後、false は選択範囲を1ジョブ、true は選択した各ページを1ジョブとして送ります。プリンター名、部数、照合、余白、縮小、各ページの用紙設定は維持されます。

この機能が行うのは印刷ジョブの分割だけで、カッター制御コマンドそのものは生成しません。実機でのカット可否とタイミングはプリンターおよびドライバーに依存します。自動テストではジョブ計画と設定継承を確認しており、レシートプリンター実機のカットは未検証です。

10. ASP.NET Coreで使うRM027

WPF/.NET版には、WebAssemblyを使わず、サーバー側の.NETエンジンがPDF・SVG・PREPEJを返すサンプルがあります。

10.1 PDFを返すRM028

app.MapGet("/api/invoices/{id}/pdf", async (
    long id,
    CancellationToken cancellationToken) =>
{
    var report = await invoiceService.CreateReportAsync(id, cancellationToken);
    var documents = report.ResolvePages()
        .Select(page => page.Document)
        .ToArray();

    var pdf = new Pao.Reports.WindowsServer.WindowsServerPdfExporter().Render(
        documents,
        new PdfRenderOptions { Title = $"請求書 {id}" },
        cancellationToken);

    return Results.File(pdf, "application/pdf", $"invoice-{id}.pdf");
});

この例の invoiceService はアプリケーション側のサービスです。Linuxでは同じ ReportDocument 一覧を SkiaPdfRenderer へ渡します。

10.2 ページ単位のSVGを返すRM029

app.MapGet("/api/invoices/{id}/svg", async (
    long id,
    int? page,
    CancellationToken cancellationToken) =>
{
    var report = await invoiceService.CreateReportAsync(id, cancellationToken);
    var pages = report.ResolvePages();
    var index = page ?? 0;

    if (index < 0 || index >= pages.Count)
        return Results.BadRequest(new { error = "ページ番号が範囲外です。" });

    cancellationToken.ThrowIfCancellationRequested();
    var svg = new SvgReportRenderer().Render(pages[index].Document);
    return Results.Content(svg, "image/svg+xml; charset=utf-8");
});

SVGのページ番号は0から始める設計にしています。PDFは帳票全体、SVGは指定した1ページを返します。

10.3 PREPEJを受け取るRM030

公開APIでアップロードを受け付ける場合は、リクエストを無制限にメモリーへ読み込まないでください。製品サンプルは次の境界を実装しています。

サンプルの実在APIは次のとおりです。

API 内容
GET /api/samples サンプル帳票一覧
GET /api/reports/{id}/data 自己完結PREPEJ
GET /api/reports/{id}/pdf 帳票全体のPDF
GET /api/reports/{id}/svg?page=0 指定ページのSVG
POST /api/print-data/pdf アップロードしたPREPEJから帳票全体のPDFを生成
POST /api/print-data/svg?page=0 アップロードしたPREPEJから指定ページのSVGを生成

10.4 アプリ側からプレビュアーを操作するRM031

帳票だけを業務画面に置き、PDFや検索は自分のアプリのボタンから操作したい場合に使うインターフェースです。ツールバーを隠しても、プレビュアーの機能は停止しません。 設定ボタンまで隠せるため、再表示用のボタンはプレビュアーの外側に置きます。

03「全帳票をWebでプレビュー」と公開デモは、この使い方の実例です。最初はプレビューだけを表示し、帳票選択の横の「ツールバーを表示」を押すとPDF・検索・設定などを表示します。もう一度押すと、設定ボタンとステータスも含めて非表示になります。

表示・非表示を切り替えるコード

以下は03サンプルのプレビュアーを読み込んだ後に置く、アプリ側のコードです。サンプルの native-previewer.js、native-previewer.css、画面の初期化部分を一緒に利用してください。03を編集する場合は、既存の同じボタン・切り替え処理を置き換え、二重に追加しないでください。

<!-- プレビュアーの外側に置くアプリ側のボタン -->
<button type="button" id="togglePreviewToolbar"
        aria-controls="nativePreviewer" aria-expanded="false">
  ツールバーを表示
</button>
// native-previewer.js の読み込み後に実行します。
const previewer = window.ReportsWebNative;
const button = document.getElementById("togglePreviewToolbar");

function updateButton() {
  const visible = previewer.getState().chrome.toolbar;
  button.textContent = visible
    ? "ツールバーを非表示" : "ツールバーを表示";
  button.setAttribute("aria-expanded", String(visible));
}

button.addEventListener("click", () => {
  if (previewer.getState().chrome.toolbar) previewer.hideAll();
  else previewer.showAll();
});
const unsubscribe = previewer.subscribe("chrome-changed", updateButton);

previewer.hideAll(); // 初期画面は帳票だけ
updateButton();
// アプリ側の画面を破棄する場合は unsubscribe() で購読を解除します。

03サンプルでは、プレビュアーを初期化する前の ReportsNativeConfig に externalChromeControl: true を指定しています。この設定では、内部設定から全非表示にした場合も、設定を戻すための小さなボタンをプレビュアー内に残しません。必ず上のような外部ボタンを用意してください。chrome-changed を購読すると、内部設定から切り替えた場合も外部ボタンの文字が追従します。

ツールバーがなくても、PDF・検索・ページ移動を使える

await previewer.showPdfPreview(); // サーバーへPDFを要求し、同じ表示領域へ表示
await previewer.showSvgPreview(); // 通常の印刷プレビューへ戻る
previewer.goToPage(2);            // ページ番号は1から
previewer.setZoom("auto");       // 表示領域に合わせた倍率
previewer.search("請求");         // 通常の印刷プレビュー内を検索

ここでの showSvgPreview() はAPI名であり、利用者に見せるボタン名は「印刷プレビュー」で構いません。PDF表示中の検索などはブラウザー内蔵PDFビューアーの機能と区別します。非表示のままPDF表示へ切り替えることもできます。プレビューを最初に開いただけではPDFを要求しません。

API 用途・注意
hideAll() 設定・ステータスを含む操作UIを一括非表示にする。開いている設定ダイアログも閉じる
showAll() 全コマンドを表示する。以前の個別表示設定を復元する操作ではない
getState() 現在のページ・倍率・表示設定などの状態を取得する
subscribe("chrome-changed", handler) 表示設定の変更を受け取る。戻り値は購読解除用関数
setChrome({commands: {print: false}}) 印刷など特定のコマンドだけを隠す
showPdfPreview() / showSvgPreview() PDF表示/通常プレビューへ切り替える。切り替え処理は await で待てるが、ブラウザー内蔵PDFビューアーの読込完了を保証するものではない
print() / savePrintData() / openPrintData() 印刷・PREPEJ保存・PREPEJを開く。自分のボタンのクリックから呼び出す

非表示の対象はReports.webの操作UIです。PDF表示中にブラウザー内蔵PDFビューアーが表示する独自のボタンは、このAPIの制御対象ではありません。

このインターフェースは、3つのNative Webサンプルで使うブラウザー側の ReportsWebNative です。サーバーの帳票エンジンのクラスや、デスクトップ用Windowとは別です。WASM版にも一括表示・非表示の機能がありますが、初期化方法やAPIの所属はWASM版のガイドを参照してください。

11. 中止と進捗通知RM032

PDF出力は CancellationToken と PdfRenderOptions.Progress に対応しています。

using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(60));

var options = new PdfRenderOptions
{
    Title = "月次帳票",
    Progress = (current, total) =>
        Console.WriteLine($"PDF {current}/{total}")
};

var documents = report.ResolvePages()
    .Select(page => page.Document)
    .ToArray();

var pdf = new WindowsPdfExporter().Render(
    documents, options, timeout.Token);

Web APIでは、HTTP切断の RequestAborted とアプリケーションのタイムアウトを連結し、同時生成数にも上限を設けてください。

12. 主要APIリファレンスRM033

12.1 ReportCreator/IReportRM034

SetObject(name) と SetObject(name, 0) は基本物体、正の番号は1始まりの行を選びます。負の番号や存在しない名前は false です。行指定には PageStart 後の編集中ページが必要です。ページ番号を渡す入口ではなく、現在のページを対象にします。

IReport.z_Objects の SetObject で選び、z_Text・z_Line・z_Square・z_Circle・z_Image・z_Barcode・z_ArtText の型別属性を変更できます。選択失敗後や古い参照では変更できません。API の座標は mm、既存 PREPEJ の共通動的座標は px です。値なしの文字・四角に属性が当たるのは、同じページで前に値を書いた物体の最後の行と属性の行が同じ場合だけです。値なしの罫線・楕円・画像・バーコードと、旧形式の SetObject による楕円・画像・バーコードの属性は適用しません。

API 説明
ReportCreator.Create() 新しい帳票処理を作る
LoadDefFile(path/stream/bytes) PREPD/PREPDJ帳票定義を読む
LoadData(path/stream/bytes) PREPE/PREPEJ印刷データを読む
PageStart()/PageEnd() 1ページの開始/確定
Write(name, value) 名前が一致するオブジェクトへ値を設定
Write(name, value, index) 繰返しまたは同名オブジェクトへ値を設定
WriteAt(name, value, indexX, indexY) 0始まりの縦横位置へ値を設定
ClearData() 帳票定義を残し、完成ページを消去
ResolvePages() 出力用の解決済みページ一覧を返す
SaveJson(...)/SaveXml(...) 完成した印刷データをPREPEJ/PREPEとして保存
SavePdf(...)/SavePdfAsync(...) SkiaSharpで帳票全体をPDF出力
GetSvgPages() ページごとのSVG文字列一覧を返す

State、PageCount、IsPageOpen で現在の処理状態も確認できます。

12.2 PrintDataStoreRM035

API 説明
Load(...) XML/JSONを内容から判定して読み込む
LoadJson(...)/LoadXml(...) 形式を指定して読み込む
SaveJson(...)/SaveXml(...) PrintDataDocument を保存する
Validate(...) 構造と値を検証する
Resolve(...) 値を反映した ResolvedReportPage 一覧を作る

12.3 Windows出力RM036

型/API 単位 説明
WindowsPdfExporter.Render(...) 帳票全体 Windows互換PDFを返す
WindowsPdfExporter.RenderImagePdf(...) 帳票全体 全ページを画像化したPDFを返す
GdiPageRenderer.RenderBitmap(...) 1ページ Bitmap を返す
GdiPageRenderer.CreatePrintDocument(...) 帳票全体 PrintDocument を作る
ReportPreviewWindow.ExportPdfAsync(...) 帳票全体 プレビュアーからPDF保存
ReportPreviewWindow.ExportCurrentSvgAsync(...) 現在ページ SVG保存
ReportPreviewWindow.ExportCurrentPngAsync(...) 現在ページ PNG保存

12.4 ポータブル出力RM037

型/API 単位 説明
SkiaPdfRenderer.Render(...) 渡した全ページ Windowsに依存しないPDF出力
SvgReportRenderer.Render(...) 1ページ SVG文字列を返す
SvgReportRenderer.Save(...) 1ページ SVGファイルを保存
SvgReportRenderer.RenderHtml(...) 渡した全ページ 複数SVGを含むHTMLを返す

13. 帳票定義をC#で扱うRM038

装飾文字(ArtText)は WPF デザイナーで作成・編集し、Windows のプレビュー・印刷・PDF で描けます。他の版は描かず、内容を保持します。

通常はデザイナーでPREPDJを作り、C#では値だけを書き込みます。帳票定義を直接読む場合は JsonDesignStore を利用できます。

using Pao.Reports.Maui.Engine.Services;

var definition = await JsonDesignStore.LoadAsync(
    "reports/invoice.prepdj", cancellationToken);

definition.Title = "請求書(社内控え)";

await JsonDesignStore.SaveAsync(
    definition,
    "reports/invoice-copy.prepdj",
    cancellationToken);

主なモデルは次のとおりです。

型 主な内容
ReportDocument Title、Paper、用紙寸法、グリッド、既定フォント、背景、Objects
ReportObject Name、Kind、X/Y、幅/高さ、回転、表示、文字、画像、線、塗り、繰返し、バーコード
PrintDataDocument 共通帳票定義と Pages
PrintDataPage ページ番号、ページ固有の帳票定義、値、動的属性
ResolvedReportPage 出力用ページ番号、解決済み帳票定義、診断一覧

位置と大きさはmmを基本にします。座標の単位がpxのPREPDJは、読込時にmmへ換算し、SaveAsync ではpxへ戻して保存します。読み込んだファイルの単位は変わりません。モデルを直接変更するときは、元の定義を変更したくない場合に Clone() を使用してください。

14. フォント、性能、安全性RM039

フォントRM040

帳票で指定したフォントが実行環境にない場合は代替フォントが使われ、文字幅や改行位置が変わることがあります。開発PCだけでなく、本番サーバーにも必要なフォントを配置し、利用条件を確認してください。

Windows互換PDFでは、フォントに字形のない文字(絵文字など)があるとPDF出力はエラーになります。

性能RM041

安全性RM042

15. エラー処理の基本RM043

壊れた .prepdj(途中で切れた JSON・Objects など必要な項目がない定義)は日本語の InvalidDataException、存在しないファイルは日本語の FileNotFoundException になります。JSON の元の例外は InnerException に残ります。

try
{
    var report = ReportCreator.Create();
    report.LoadDefFile(definitionPath);
    report.PageStart();
    report.Write("顧客名", customerName);
    report.PageEnd();
    report.SavePdf(outputPath, cancellationToken: cancellationToken);
}
catch (OperationCanceledException)
{
    // 利用者操作またはタイムアウトで中止
}
catch (FileNotFoundException)
{
    // 帳票定義または画像が見つからない
}
catch (InvalidDataException)
{
    // PREPDJ/PREPEJの形式または内容が不正
}
catch (ArgumentException)
{
    // オブジェクト名、ページ番号などの指定が不正
}
catch (IOException)
{
    // 読み書きに失敗
}

利用者向けメッセージと調査用ログは分けてください。失敗時に、作成途中のファイルを完成ファイルとして残さない設計も重要です。

16. 目的別の早見表RM044

やりたいこと 最初に見る章 主なAPI
PREPDJへ値を入れる 3、4 ReportCreator、LoadDefFile、Write
Windows互換PDFを作る 5.1 WindowsPdfExporter
Linuxでも使えるPDFを作る 5.2 Report.SavePdf、SkiaPdfRenderer
SVG/PNGを作る 6 GetSvgPages、SvgReportRenderer、GdiPageRenderer
完成帳票を保存・再表示する 7 SaveJson、LoadData、PrintDataStore
WPFプレビューを開く 8 ReportPreviewWindow
プリンターへ出力する 9 CreatePrintDocument
Web APIから出力する 10 ASP.NET Coreサンプル
API名を調べる 12 APIリファレンス
デザイナーを操作する 操作ガイド デザイナー操作ガイド
プレビュアーを操作する 操作ガイド プレビュアー操作ガイド

17. 付属サンプルを実行するRM045

ZIPを全体ごと展開し、Native/WPF-DotNet/samples/README.html を開いてください。WindowsでVisual Studioからプロジェクトを開き、ソースを変更して実行するためのサンプルです。Dockerは使いません。

番号フォルダー 学ぶこと
01.あっという間に帳票出力 短いC#コードで帳票を作る。Compat-net472 には.NET Framework互換版の例もある
02.全帳票をパソコンでプレビュー SQLiteのデータから全帳票を作り、WPFでプレビューする
03.全帳票をWebでプレビュー .NETエンジンを使うWebアプリで表示する
04.Web API・デスクトップ連携 RESTサーバーとWPFクライアントをつなぐ

番号フォルダーのREADMEに、Visual Studioで開くプロジェクト、必要環境、起動順、読むソース、停止方法を記載しています。04はサーバーとクライアントを別々に起動します。

参照するSDKは、.NET Framework 4.7.2以上/.NET 5~9向けの SDK/net472 と、.NET 10以降向けの SDK/net10 に分かれます。共通のクラスAPIを使いますが、参照DLLを混在させないでください。選択と確認範囲は本書の動作環境・参照設定の説明を確認してください。

最初は01のタイトルや会社名を変更し、PDFやプレビューに反映されることを確認します。次に02、03、04へ進むと、同じ帳票の作り方をデスクトップとWebの両方で学べます。

18. ライセンスRM046

本製品は商用ライセンスです。評価、開発用PC、サーバー配置、再配布、組み込み利用の条件は、製品に付属する使用許諾契約を確認してください。