PDFエクスポート
v1.4.0 から、ReoGrid インスタンスはアクティブなワークシートを本物のベクター PDF(テキスト・罫線・塗り・セル結合・セルアンカー画像)に出力できます。外部ライブラリもサーバー往復も不要で、ワークシートのデータからブラウザ内で生成します。v1.7.0 からは、複数のシートやブック全体を 1 つの PDF にまとめることもできます。
Note: PDFエクスポートは Pro 版で利用可能です。
フォントは必須です。 PDF レンダラーはグリフ輪郭を埋め込むため、エクスポートには実際のフォントのバイト列が必要です。
locale('ja'/'zh-CN'/'zh-TW'/'ko')を指定すれば ReoGrid がフォントを解決します。自前のバイト列をfontに渡すこともできます。どちらも指定しない場合はエクスポートが例外を投げます。これにより日本語・中国語・韓国語が正しく描画されます。
クイックスタート
import { createReogrid, preloadPdfFont } from '@reogrid/pro'
const grid = createReogrid({ workspace: '#app' })
// ロケールのフォントを一度だけ、自分で制御できる非同期地点で取得する
await preloadPdfFont('ja')
// レンダリングとダウンロードを 1 回で
grid.saveAsPdf({ locale: 'ja', filename: 'report.pdf' })
saveAsPdf() はレンダリングしてブラウザのダウンロードを起動します。生のバイト列が欲しい場合(アップロードやプレビューなど)は exportPdf() を使います。
const bytes: Uint8Array = grid.exportPdf({ locale: 'ja' })
// 例:プレビュー URL 用に Blob へラップ
const url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }))
なぜ
preloadPdfFontを分けているのか。 エクスポートは意図的に同期です。saveAsPdfは生成したリンクのクリックで終わりますが、その前にawaitが入るとユーザージェスチャーのコンテキストが失われ、ポップアップブロッカーに阻まれます。そこでダウンロードは呼び出し側が選んだ時点で一度だけ行い、以降のエクスポートは即座に完了します。数 MB のフォント取得がsaveAsPdf()の内部に隠れないという利点もあります。
ロケール
locale は、ReoGrid が取得方法を知っているフォントを指す名前です。標準で 4 つが登録されています。
| ロケール | フォント | 用途 |
|---|---|---|
'ja' | Noto Sans JP | 日本語 |
'zh-CN' | Noto Sans SC | 簡体字中国語 |
'zh-TW' | Noto Sans TC | 繁体字中国語 |
'ko' | Noto Sans KR | 韓国語 |
import { preloadPdfFont, registerPdfFont } from '@reogrid/pro'
await preloadPdfFont('zh-CN') // アプリ起動時に一度
grid.saveAsPdf({ locale: 'zh-CN' }) // 以降はいつでも
ロケールの選択は重要です。日本語フォントは簡体字中国語のビジネス頻出語の約 3 分の 1 しかカバーしないため、中国語のテキストを 'ja' でエクスポートすると残りが豆腐(□□□)になります。
独自のロケールも登録できます(ローダー・URL・手元のバイト列のいずれでも可)。
registerPdfFont('th', () => loadFont('/fonts/NotoSansThai-Subset.ttf'))
await preloadPdfFont('th')
grid.saveAsPdf({ locale: 'th' })
バイト列で登録した場合は即座にロード済みとなり、preload は不要です。ネットワークを完全に回避したい場合(アプリに同梱したフォントやパッケージから読み込んだフォント)はこの方法を使ってください。
関連ヘルパー(いずれも @reogrid/pro からエクスポート): isPdfFontRegistered、isPdfFontLoaded、getPdfFont、getPdfFontFaces、getPdfFontWeights、getRegisteredPdfFontTags、removePdfFontWeight、clearPdfFontCache。
フォントのウェイト
PDF はグリフの輪郭を埋め込むため、ウェイトは別のフォントファイルです — 描画時に補間する余地はありません。そこで v1.6.0 から、1 つのタグが thin・normal・bold のウェイトごとに face を持つようになりました。
// 1 つのソースは `normal` として登録されます
registerPdfFont('brand', regularBytes)
// ウェイトを指定する形 — @reogrid/font-* パッケージが export しているのはこの形です
registerPdfFont('brand', { normal: regularBytes, bold: boldBytes })
// 既存のタグにウェイトを 1 つ足す
registerPdfFont('brand', { bold: '/fonts/Brand-Bold.ttf' })
preloadPdfFont(tag) は既定で normal と bold を取得します。ワークシートの見出しはたいてい太字だからです。太字が無いと分かっている文書では絞り込めます。
await preloadPdfFont('ja', { weights: ['normal'] }) // 転送量が半分になります
返り値は従来どおり normal のバイト列なので、v1.6.0 より前に書いた呼び出しはそのままで構いません。
太字のセルは bold face があればそれで描画されます。無い場合は擬似ボールド(regular の輪郭を 4% ストロークしたもの)にフォールバックします — 単一ウェイトの文書にできるのはそこまでです。
これが極細 PDF の原因を解消した変更です。 v1.6.0 より前、組み込みのタグは Google の可変フォントを指していました。可変フォントは既定座標の字形を
glyfに、差分をgvarに持ちますが、PDF 書き出しはglyfを埋め込むだけでgvarを適用しません。そのため出力はfvarの既定 wght=100 —— つまり Thin で焼き付き(/BaseFont /…+NotoSansJP-Thin)、擬似ボールドも極細の輪郭を太らせることしかできませんでした。組み込みの URL は静的な face を指すようになっています。
組み込みのタグに単一のソースを登録した場合、そのタグの他の組み込みウェイトはそのまま残ります。意図しない場合は { normal: … } の形で登録し、removePdfFontWeight で不要なものを外してください。
フォントパッケージ(CDN 不要・オフライン対応)
組み込みロケールは公開 CDN からフルの上流フォントを取得します。Noto Sans SC だけで約 17 MB あり、その CDN は中国本土からは低速で、しばしば到達できません。本番環境では対応するサブセットパッケージをインストールして登録してください。バイト列は npm から来るため実行時のネットワークが一切不要になり、企業導入で一般的なオフラインのイントラネットでも動作します。
npm install @reogrid/font-sc # zh-CN — 他に font-tc / font-jp / font-kr
import { registerPdfFont, preloadPdfFont } from '@reogrid/pro'
import { notoSansSC } from '@reogrid/font-sc'
registerPdfFont('zh-CN', notoSansSC) // パッケージが持つ全ウェイトを登録
await preloadPdfFont('zh-CN') // アプリ起動時に一度 — normal と bold
grid.saveAsPdf({ locale: 'zh-CN', filename: 'report.pdf' })
フォントパッケージ v2。 ReoGrid v1.6.0 に合わせて、各パッケージは Thin / Regular / Bold の静的な face を同梱し、
registerPdfFontがそのまま受け取れる形のオブジェクトを export するようになりました。ローダー 1 本ではなくnotoSansSCを渡してください。registerPdfFont('zh-CN', loadNotoSansSC)も動きますが、regular だけが登録されるため、太字は擬似ボールドにフォールバックします。
| パッケージ | ロケール | export | 収録文字数 | 1 face あたり |
|---|---|---|---|---|
@reogrid/font-sc | 'zh-CN' | notoSansSC | 7,709(GB 2312) | 約 2,378 KB |
@reogrid/font-tc | 'zh-TW' | notoSansTC | 13,682(Big5) | 約 4,756 KB |
@reogrid/font-jp | 'ja' | notoSansJP | 6,974(JIS X 0208) | 約 2,557 KB |
@reogrid/font-kr | 'ko' | notoSansKR | 3,196(KS X 1001) | 約 567 KB |
既定の preload は、このうち 2 つの face(regular と bold)を取得します。単一の face が欲しい場合の loadNotoSansSC(weight?) なども引き続き export されています。
各サブセットは、その言語の伝統的な国家標準を第 2 水準まで収録しています。人名・地名は第 2 水準にあり、顧客名が豆腐になる方がダウンロードが少し大きくなるより問題が大きいためです。バイト列は動的インポートの背後に置かれるため、バンドラーは専用チャンクに分離します。PDF を出力しないアプリはダウンロードしません。
自前のフォントバイト列を渡す
ReoGrid が知らない書体を使う場合は font を指定します。生の TrueType glyf バイト列(ArrayBuffer | Uint8Array)を渡し、font と locale の両方を指定した場合は font が優先されます。入手方法は 3 通りです。
import { loadDefaultJapaneseFont, loadFont, DEFAULT_JAPANESE_FONT_URL } from '@reogrid/pro'
// 1. 同梱の既定フォント — Noto Sans JP(約 9.6 MB、公開 CDN から取得・キャッシュ)
const a = await loadDefaultJapaneseFont()
// 2. 自前でホストするサブセット(本番推奨 — はるかに軽量)
const b = await loadFont('https://cdn.example.com/fonts/NotoSansJP-Subset.ttf')
// 3. すでに手元にあるバイト列(<input type="file"> や fetch など)
const c = new Uint8Array(await file.arrayBuffer())
どちらのヘルパーも、ダウンロードしたバイト列をメモリおよび Cache Storage にキャッシュするため、約 9.6 MB の既定フォントの取得はブラウザごとに最大 1 回です。本番環境では実際に必要なグリフだけをサブセット化してホストし、その URL を loadFont に渡してください(DEFAULT_JAPANESE_FONT_URL は同じソースからサブセットを作りたい場合に公開されています)。
fontはフォント名ではありません。font: 'Arial'は動作しません — このオプションはフォントファイルのバイト列を受け取ります。グリフのアウトライン自体を PDF に埋め込むことで、閲覧側の環境にフォントがインストールされていなくても同じ表示になるためです。上記の方法でファイルを読み込んでから渡してください。名前を渡した場合は次のエラーになります。exportWorksheetPdf: `font` must be TrueType (glyf) font bytes, not a font-family name (got "Arial"). Either name a locale — …ロケールを誤って
fontに渡した場合はそれを認識し、{ locale: 'zh-CN' }として渡すよう案内します。フォントは
glyfアウトラインを持つ TrueType である必要があります。OpenType/CFF(OTTO— 多くの.otfファイル)は未対応で、専用のエラーになります。
ExportPdfOptions
| オプション | 型 | 既定値 | 説明 |
|---|---|---|---|
locale | 'ja' | 'zh-CN' | 'zh-TW' | 'ko' または登録済みのタグ | — | 埋め込むフォント。レジストリ経由で解決されます。事前に preload が必要です。font を指定しない場合は必須。 |
font | ArrayBuffer | Uint8Array | — | 埋め込み可能な TrueType(glyf)フォントのバイト列。locale より優先されます。locale を指定しない場合は必須。 |
fonts | { normal?, bold?, thin? } | — | v1.6.0 — font と併せて埋め込む追加ウェイト(ファイルのバイト列)。locale を使う場合は自動的に用意されます。 |
pageSize | 'A3'|'A4'|'A5'|'B4'|'B5'|'Letter'|'Legal'|'Tabloid'|'Executive' または { widthMm, heightMm } | 'A4' | 用紙サイズ。 |
orientation | 'portrait' | 'landscape' | 'portrait' | ページの向き。 |
marginMm | number | { top, right, bottom, left } | 12 | 余白(mm、数値は全辺共通)。 |
range | PdfExportRange | 印刷されるすべて | 出力するセル範囲。既定では A1 から、紙に何かが印刷される最後のセル(値・罫線・白以外の塗り・結合セル・画像)までです(ページレイアウト参照)。 |
showGridLines | boolean | ワークシートの設定 | グリッド線を描画。既定では worksheet.setShowGridLines()(画面表示)に従うため、明示的に指定しない限り PDF は画面と一致します。 |
scale | number | 1 | 一様な内容スケール(0.1〜4)。fitToWidth 時は無視。 |
fitToWidth | boolean | false | 全列が 1 ページ幅に収まるよう縮小。 |
usePageBreaks | boolean | false | 画面上の改ページプレビューとまったく同じように改ページ(後述)。 |
images | WorksheetImageInfo[] | シートの画像 | セルにアンカーする画像。 |
showImages | boolean | true | セルアンカー画像を描画。 |
title | string | — | ドキュメントタイトル(PDF メタデータ)。 |
sheets | 'active' | 'all' | (number | string)[] | 'active' | v1.7.0 — インスタンスの exportPdf / saveAsPdf のみ。PDF に含めるシート(後述)。 |
saveAsPdf はさらに filename?: string を受け取ります。省略時は読み込み済みドキュメントのベース名(getDocumentName() / setDocumentName() 参照)、ファイル未読込のときは worksheet.pdf になります。
usePageBreaks による複数ページ出力
既定では exportPdf は使用範囲を pageSize / orientation / marginMm / scale オプションでページに割り付けます。usePageBreaks: true を指定すると、代わりにワークシート独自のページングモデルで改ページします — PDF は ページレイアウト の改ページプレビューとまったく同じ位置(手動改ページ・印刷範囲・フィットスケールを含む)で改ページされます。
// 用紙・向き・余白・スケール・ページ帯はすべて
// worksheet.getPrintSettings() と計算済みの改ページから取得されます。
grid.worksheet.setPrintSettings({ paperSize: 'A4', orientation: 'landscape' })
grid.worksheet.insertRowPageBreak(40) // 40 行目から新しいページ
await preloadPdfFont('ja')
grid.saveAsPdf({ locale: 'ja', usePageBreaks: true, filename: 'ledger.pdf' })
usePageBreaks が有効なとき、オプション側の pageSize / orientation / marginMm / scale / fitToWidth / range は無視されます — 代わりに ページレイアウト で設定してください。改ページ対象が何もないシートの場合はオプション側にフォールバックします。
これには fitToPages(v1.6.0)も含まれます。ワークシート側に設定すれば、書き出される PDF は画面のプレビューと同じ導出倍率を使います。
複数のシートを 1 つの PDF にする
v1.7.0 から、exportPdf() と saveAsPdf() は sheets オプションを受け取ります。Excel の「ブック全体を印刷」に相当します。
await preloadPdfFont('ja')
// 表示されているすべてのシートを、タブの順に
grid.saveAsPdf({ locale: 'ja', usePageBreaks: true, sheets: 'all', filename: 'workbook.pdf' })
// 選んだシートを指定した順に — シート名でもインデックスでも
const bytes = grid.exportPdf({ locale: 'ja', usePageBreaks: true, sheets: ['見積書', 2] })
sheets | PDF に含まれるもの |
|---|---|
'active'(既定) | 画面に表示中のシート。v1.7.0 より前と同じです。 |
'all' | 表示されているすべてのシートをタブの順に。Excel と同じく、非表示のシートは含みません。 |
[…] | 指定したシート(インデックスと名前の混在も可)を、その順に 1 回ずつ。非表示のシートも名前を指定すれば含まれます。存在しない名前やインデックスを指定すると、ブックのシート名一覧を添えて例外を投げます。 |
- シートごとに自分のページ設定を使います。
usePageBreaksを指定すると、各シートは自分の用紙サイズ・向き・余白・「N ページに収める」・改ページでページ分割されるため、A4 縦と A3 横のページを 1 つの文書に混在させられます。指定しない場合は、オプション側の設定(とrange)がすべてのシートに同じように適用されます。 - ページ番号はシートをまたいで通しになります。 ヘッダー・フッターの
&Pは文書の最初のページから数え、&Nは文書全体のページ数です。Excel でブック全体を印刷したときと同じ番号の振り方です。&Aはそれぞれのページのシート名になります。1 シートだけを出力する場合の番号は従来と変わりません。 - 空のシートは飛ばします。 Excel も空のシートは印刷しません。明示した
range、印刷範囲(usePageBreaks時)、または印刷される内容のいずれかがあるシートが対象です。すべてのシートが空の場合は、PDF には最低 1 ページが必要なため、先頭のシートが空白の 1 ページになります。
インスタンスを使わずに出力する
exportWorksheetsPdf(worksheets, options) はヘッドレス版です。渡したワークシートを、その順に同じ規則で 1 つの文書に出力します。exportWorksheetPdf(worksheet, options) は、その 1 シート版です。
import { exportWorksheetsPdf } from '@reogrid/pro'
const sheets = grid.workbook.getWorksheets()
const bytes = exportWorksheetsPdf([sheets[0], sheets[2]], { locale: 'ja', usePageBreaks: true })
表示形式の色
表示形式のブラケット色(#,##0;[赤]"▲"#,##0 など)は、v1.6.0 から PDF 出力にも反映されます。それ以前はセルスタイルの文字色しか見ていなかったため、画面では赤い負数が紙では黒くなり、会計帳票の赤字が消えていました。現在は画面と同じ優先順位(書式色 → セルスタイル色 → 既定色)です。
リッチテキストのセルは実行ごとに色を持ち、かつ文字列セルなので表示形式の色は付きません(画面と同じ挙動です)。ブラウザ印刷(HTML 経由)は対象外で、引き続きセルスタイルの色を使います。
React の例:PDF 出力ボタン
import { useEffect, useRef } from 'react'
import { Reogrid } from '@reogrid/pro/react'
import { preloadPdfFont } from '@reogrid/pro'
import type { ReogridInstance } from '@reogrid/pro/react'
function App() {
const gridRef = useRef<ReogridInstance>(null)
// フォントの取得はマウント時に一度だけ。クリックハンドラ内で await すると、
// ダウンロードリンクに必要なユーザージェスチャーが失われます。
useEffect(() => { void preloadPdfFont('ja') }, [])
function handleExport() {
gridRef.current?.saveAsPdf({ locale: 'ja', usePageBreaks: true, filename: 'report.pdf' })
}
return (
<>
<button onClick={handleExport}>PDF 出力</button>
<Reogrid ref={gridRef} style={{ flex: 1 }} />
</>
)
}
Vue の例:PDF 出力ボタン
Vue コンポーネントは、テンプレート ref の instance プロパティ(gridRef.value?.instance)としてグリッドを公開します。出力の呼び出し自体は他のフレームワークと同じです。
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { Reogrid, type ReogridInstance } from '@reogrid/pro/vue'
import { preloadPdfFont } from '@reogrid/pro'
const gridRef = ref<{ instance: ReogridInstance | null } | null>(null)
// フォントの取得はマウント時に一度だけ。クリックハンドラ内で await すると、
// ダウンロードリンクに必要なユーザージェスチャーが失われます。
onMounted(() => { void preloadPdfFont('ja') })
function exportPdf() {
gridRef.value?.instance?.saveAsPdf({ locale: 'ja', usePageBreaks: true, filename: 'report.pdf' })
}
</script>
<template>
<button @click="exportPdf">PDF 出力</button>
<Reogrid ref="gridRef" style="width: 100%; height: 400px" />
</template>
関連項目
- ページレイアウト — 用紙サイズ・余白と、
usePageBreaksが従う改ページプレビュー。 - マルチシートワークブック —
sheets: 'all'で 1 つの文書にまとめるシート。 - 印刷 — 印刷経路の全体像と選び方。
- HTML で印刷 — ブラウザの標準ダイアログによる簡易印刷。
- 画像 — エクスポートした PDF に表示されるセルアンカー画像。
弊社サイトの品質向上のため、コメントをご記入ください。