VisualforceのPDFで改ページを制御する書き方
取引先の概要を1ページ目、ぶら下がる取引先責任者を2ページ目以降に出力するPDFを作ります。@pageのCSSとpage-break-beforeでページを分け、ページ番号を数える方法もあわせて示します。
なぜページを区切る必要があるのか
Visualforceのページを丸ごとPDFにすると、内容が長いレコードほど1ページに詰め込まれ、読みにくくなります。取引先の概要と、ぶら下がる取引先責任者の一覧を別々のページに分けて出力できれば、印刷しても見やすいPDFになります。ページを分ける仕組みはCSSのpage-break-beforeと、用紙サイズを決める@pageのふたつです。公式ドキュメントは、PDFレンダリングサービスが解釈できるCSSの範囲を「CSS 2.1相当まで」と明記しています。page-break-beforeはCSS 2.1に含まれる標準的なプロパティなので、そのまま使えます。
取引先と取引先責任者のレコードを用意する
まず、PDFに出す元データを作ります。
取引先レコードを作成する
アプリケーションランチャーから「取引先」を開き、新規作成します。取引先名は任意の値(例:00001)で構いません。
取引先責任者レコードを複数作成する
作成した取引先の関連リストから、取引先責任者を3件作成します。姓は00001-0001、00001-0002、00001-0003のように連番にしておくと、後の動作確認で区別しやすくなります。
Apexクラスで取引先責任者を1回のSOQLで取得する
標準コントローラの拡張クラスを作り、取引先責任者を1回のSOQLでまとめて取得します。
public with sharing class StandardControllerPDFSample { public Account acc { get; set; } public List<Contact> conList { get; set; } public Integer totalPage { get; set; } public StandardControllerPDFSample(ApexPages.StandardController stdCtrl) { this.acc = (Account) stdCtrl.getRecord(); Id accId = this.acc.Id; this.conList = [SELECT Id, LastName FROM Contact WHERE AccountId = :accId LIMIT 3]; this.totalPage = this.conList.size() + 1; } }
標準コントローラ拡張。取引先責任者を1回のSOQLで取得します。読み取りだけの処理です。
SOQLは文字列連結で組み立ててDatabase.query()で実行することもできますが、ここではインラインSOQLとバインド変数で書いています。取引先Idはユーザー入力ではないので危険度は高くありませんが、バインド変数で書くほうが短く、意図も伝わります。件数をLIMIT 3で固定しているのはサンプルとしての制約です。実運用では、関連レコードが多い取引先だと、PDFに変換する前のレスポンスサイズが15MB未満という上限に近づくことがあるため、必要な件数だけを絞り込む設計にしてください。
Visualforceページでページを区切る
renderAs="pdf"でPDFに変換し、@pageで用紙サイズを、page-break-beforeでページの区切りを指定します。
<apex:page standardController="Account" extensions="StandardControllerPDFSample" renderAs="pdf" applyHtmlTag="false" applyBodyTag="false" showHeader="false"> <head> <style> @page { size: A4; } </style> </head> <body> <apex:variable value="{!1}" var="pageNumber"/> <div> <h1>取引先</h1> <table> <tr><td>Account Id : {!acc.Id}</td></tr> <tr><td>page ( {!pageNumber} / {!totalPage} )</td></tr> </table> </div> <apex:variable value="{!pageNumber+1}" var="pageNumber"/> <apex:repeat value="{!conList}" var="con"> <div style="page-break-before:always;"></div> <table> <tr><td>Contact Id : {!con.Id}</td></tr> <tr><td>Contact LastName : {!con.LastName}</td></tr> <tr><td>page ( {!pageNumber} / {!totalPage} )</td></tr> </table> <apex:variable value="{!pageNumber+1}" var="pageNumber"/> </apex:repeat> </body> </apex:page>
apex:pageのrenderAs="pdf"でPDF出力にします。@pageとpage-break-beforeでページを分けます。
applyHtmlTagとapplyBodyTagをfalseにしているのは、<head>と<body>を自分で書くためです。showHeaderはLightning Experienceとモバイルアプリでは常にfalseに上書きされると公式ドキュメントに明記されていますが、他の呼び出し経路でも確実にヘッダーを消すため、明示的に指定しておきます。ページ番号はapex:variableを使って、出力するたびに1つずつ増やしています。区切り用のdivはapex:outputLabelで包まず、素のdivのまま置きます。apex:outputLabelは本来フォーム項目のラベル用のコンポーネントです。ページ番号の行も、<tr>の直下に文字列を置くと構造が崩れるため、<td>で囲んでいます。
Apexから直接PDFを生成する場合
アクションからページを開く方法のほかに、ApexからPageReference.getContentAsPDF()を呼んでPDFのバイナリを直接取得する方法もあります。メール添付やContentVersionへの保存に使う場合はこちらが向いています。getContentAsPDF()は、呼び出すapex:page側のrenderAsの値に関係なく、常にPDFとして内容を返す点がgetContent()との違いです。ただし、外部サーバーのリソースを参照しているVisualforceページに対しては、Apexトリガーの中からgetContentAsPDF()を呼ぶと例外になります。このページは外部リソースを使っていないため影響しませんが、画像を外部URLで読み込むページでPDFを作る場合は注意してください。
PDFを開くアクションを配置する
取引先の詳細画面からPDFを開けるように、カスタムアクションを用意します。
クイックアクションを新規作成する
オブジェクトマネージャの「取引先」からボタン、リンク、アクションを開き、新規アクションを作成します。Visualforceページを呼び出す設定を選びます。
呼び出すVisualforceページを選択する
作成したVisualforceページを指定し、ラベルをわかりやすい名前(例:PDF改ページサンプル)にして保存します。
ページレイアウトのアクション欄に配置する
取引先のページレイアウトを編集し、作成したアクションをアクション欄にドラッグして配置します。
動作確認で見るポイント
取引先レコードで作成したアクションを押すと、PDFが新しいタブで開きます。今回の設定なら合計4ページになります。
| ページ | 内容 |
|---|---|
| 1ページ目 | 取引先のAccount Idとページ番号1/4 |
| 2ページ目 | 1件目の取引先責任者のContact IdとLastName、ページ番号2/4 |
| 3ページ目 | 2件目の取引先責任者の情報とページ番号3/4 |
| 4ページ目 | 3件目の取引先責任者の情報とページ番号4/4 |
各ページの先頭で改ページされていること、ページ番号が1つずつ増えていることを確認します。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
@pageのプロパティ名を打ち間違える | 用紙サイズの指定が効かず、既定のサイズで出力される |
ページ番号用のapex:variableを更新し忘れる | 全ページで同じ番号が表示される |
| 関連レコードを絞り込まずにサブクエリを組む | PDFに変換する前のレスポンスサイズが15MB未満という上限に近づく |
showHeaderをtrueのままにする | Lightning Experience以外の呼び出し経路でタブヘッダーが出てしまうことがある |
確認した環境
- 2026年9月 / Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、renderAs="pdf"の仕様とapex:pageの属性を確認しています
まとめ
- Visualforceのページを1ページ目と2ページ目以降に分けるには、
@pageで用紙サイズを、page-break-beforeでページの区切りを指定します - page-breakはtableやtrに直接指定せず、区切り用の空のdivに指定してください
- ページ番号は
apex:variableを出力のたびに更新して数えます - SOQLは文字列連結ではなくバインド変数で書くほうが安全で短くなります
PageReference.getContentAsPDF()はapex:pageのrenderAsの値に関係なく常にPDFを返しますが、外部リソースを参照するページをApexトリガーの中から呼ぶと例外になります- showHeaderはLightning Experienceとモバイルアプリでは常にfalseに上書きされます
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。