pdf-lib アーキテクチャ:純 JavaScript で PDF の作成・編集・結合

源码分析(更新: 2026年5月16日)

なぜ 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 ツールチェーンを構成しています。

ブラウザローカルツールを無料で試す →

#PDF#pdf-lib#源码分析#浏览器端#架构