pdf-lib アーキテクチャ:純 JavaScript で PDF の作成・編集・結合
なぜ pdf-lib か
ブラウザ向け PDF ライブラリは厳しい基準で選びます。
| ライブラリ | サイズ | 作成 | 編集 | 結合 | フォント | メンテ |
|---|---|---|---|---|---|---|
| pdf-lib | ~350KB | ✅ | ✅ | ✅ | ✅ | 活発 |
| pdfjs-dist | ~2MB | ❌ | ❌ | ❌ | ❌ | Mozilla |
| jsPDF | ~300KB | ✅ | ❌ | ❌ | 一部 | 活発 |
| PDFKit | ~1MB | ✅ | ❌ | ❌ | ✅ | Node 向け |
pdf-lib はブラウザで 作成と編集の両方 ができる唯一の純 JS ライブラリです。
PDF ファイル形式の基礎
内部構造
%PDF-1.7 ← バージョンヘッダ
1 0 obj ← オブジェクト 1
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj ← オブジェクト 2
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj ← オブジェクト 3
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref ← クロスリファレンス表
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
190
%%EOF
主要概念
| 概念 | 説明 |
|---|---|
| 間接オブジェクト | 1 0 obj ... endobj、番号で参照 |
| 辞書 | << /Key /Value >>、JSON に似た構造 |
| ストリーム | stream ... endstream、バイナリ(多くは FlateDecode) |
| xref | 各オブジェクトのバイトオフセット、O(1) ランダムアクセス |
| ページツリー | ネストした /Pages ノード |
pdf-lib のアーキテクチャ
モジュール階層
PDFDocument (トップ API)
├── PDFPage (ページ操作)
├── PDFFont (フォント)
├── PDFImage (画像)
└── PDFCatalog (文書構造)
└── PDFContext (低レベルオブジェクトモデル)
├── PDFObject
├── PDFDict
├── PDFStream
├── PDFRef
└── PDFCrossRefSection
オブジェクトモデル
pdf-lib の中心は PDFContext で、文書全体のオブジェクトグラフを保持します。
class PDFContext {
// すべての間接オブジェクトのレジストリ
objects: Map<PDFRef, PDFObject>;
// 新しいオブジェクト番号を割り当て
assign(ref: PDFRef, object: PDFObject): void;
// 参照解決
lookup(ref: PDFRef): PDFObject;
// 削除
delete(ref: PDFRef): void;
}
PDF 結合の仕組み
// PDFDocument.copyPages のコア
copyPages(srcDoc: PDFDocument, indices: number[]): PDFPage[] {
const pages: PDFPage[] = [];
for (const index of indices) {
// 1. ソースからページオブジェクト取得
const srcPage = srcDoc.getPage(index);
// 2. ページと参照先をすべて深いコピー
const copiedPage = this.context.copy(srcPage.node);
// 3. ターゲット文書に登録
// ページ辞書、コンテンツストリーム、リソース、フォントなど
// 重要:すべての PDFRef を新しい番号にリマップ
pages.push(PDFPage.of(copiedPage));
}
return pages;
}
難所:深いコピーではすべての間接参照をリマップする必要があります。A が 2 0 R で B を参照している場合、ターゲットでは B の番号が変わるため、マッピング表が必須です。
ストリーム圧縮
PDF のコンテンツストリームは多くの場合 FlateDecode(zlib/deflate)です。
class PDFStream {
dictionary: PDFDict;
contents: Uint8Array;
getContentsString(): string;
getContentsSize(): number;
}
// 圧縮して書き込み
const compressed = pako.deflate(rawBytes);
stream.dictionary.set(PDFName.of('Filter'), PDFName.of('FlateDecode'));
stream.contents = compressed;
pdf-lib は pako(純 JS zlib)で圧縮・展開します。
フォント埋め込み
標準フォントとカスタムフォント
PDF には Helvetica など 14 の標準フォントがあり、埋め込みなしで表示できます。CJK は埋め込みが必要です。
ToolsKu のフォント戦略
// 同梱 CJK フォント
const fonts = {
sourceHanSans: await pdfDoc.embedFont(
await fetch('/fonts/CN/SourceHanSansCN-Regular.otf')
),
sourceHanSansBold: await pdfDoc.embedFont(
await fetch('/fonts/CN/SourceHanSansCN-Bold.otf')
),
};
フォントサブセット:文書で使ったグリフだけ埋め込み、ファイルサイズを大幅削減。
| ケース | フルフォント | サブセット | 削減 |
|---|---|---|---|
| CJK 10 文字 | ~7MB | ~15KB | 99.8% |
| CJK 100 文字 | ~7MB | ~80KB | 98.9% |
ToolsKu の PDF ツールチェーン
20+ ツールと API の対応
| ツール | pdf-lib API | 追加ライブラリ |
|---|---|---|
| 結合 | copyPages() + addPage() |
- |
| 分割 | 新規 doc + copyPages() |
- |
| 回転 | page.setRotation() |
- |
| 透かし | page.drawText() + 透明度 |
- |
| ページ番号 | page.drawText() ループ |
- |
| 暗号化 | - | @pdfsmaller/pdf-encrypt-lite |
| テキスト抽出 | - | pdfjs-dist |
| PDF→画像 | - | pdfjs-dist + canvas |
| 圧縮 | メタデータ削除 + ストリーム最適化 | - |
暗号化:pdf-lib の外
pdf-lib は PDF 暗号化非対応。ToolsKu は @pdfsmaller/pdf-encrypt-lite を使用します。
import { encrypt } from '@pdfsmaller/pdf-encrypt-lite';
const encryptedPdf = await encrypt(pdfBytes, {
userPassword: 'user123',
ownerPassword: 'owner456',
permissions: {
printing: true,
copying: false,
modifying: false,
},
});
性能の実践
大きなファイル
// 100 ページ超はページ単位で処理しメモリピークを抑える
async function processLargePdf(file: File) {
const pdfDoc = await PDFDocument.load(await file.arrayBuffer());
const totalPages = pdfDoc.getPageCount();
for (let i = 0; i < totalPages; i++) {
const page = pdfDoc.getPage(i);
// ページごとの処理...
updateProgress(i / totalPages);
}
}
ストリーミング読み込み
pdf-lib はストリーミング非対応—PDF 全体をメモリに読み込む必要があります。100MB 超はメモリ負荷になります。
対策:大ファイルでは進捗表示し、極大ファイルはデスクトップツールを推奨。
まとめ
約 350KB でブラウザ内 PDF の作成・編集を実現する pdf-lib は驚くべき成果です。PDFContext のオブジェクトグラフ、間接参照のリマップ、ストリーム圧縮は PDF 仕様への深い理解を示しています。
ToolsKu は PDF結合、分割、回転、透かし、ページ番号 など 20 以上のツールをすべてブラウザ内で提供。pdfjs-dist(描画・抽出)と pdf-encrypt-lite(暗号化)と合わせ、完全な PDF ツールチェーンを構成しています。
ブラウザローカルツールを無料で試す →