pdfkit vs pdf-lib
フロントエンドでのPDF生成・操作ライブラリの比較
pdfkitpdf-lib類似パッケージ:

フロントエンドでのPDF生成・操作ライブラリの比較

pdf-libpdfkit はどちらもJavaScriptでPDFファイルを生成・編集できるライブラリですが、アーキテクチャや用途に大きな違いがあります。pdf-lib は主に既存のPDFを読み込んで修正する用途に特化しており、ブラウザ環境でも動作します。一方、pdfkit はゼロからPDFを描画するための高機能な描画エンジンを備えており、Node.js向けに設計されていますが、ブラウザでも使用可能です。両方ともTypeScript対応で、現代的なフロントエンド開発に組み込むことができます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
pdfkit6,049,11510,69710.5 MB3311日前MIT
pdf-lib08,599-3165年前MIT

pdf-lib vs pdfkit: フロントエンド開発者が選ぶべきPDFライブラリ

PDFを扱うJavaScriptライブラリにはいくつかありますが、pdf-libpdfkit はよく比較されます。しかし、この2つは根本的な目的が異なります。pdf-lib は「PDFの編集」に、pdfkit は「PDFの描画」に特化しています。ここでは、実際の開発シナリオに基づいて深く比較します。

📄 基本的な用途:編集 vs 描画

pdf-lib は既存のPDFを読み込んで操作することを前提としています。例えば、ユーザーがアップロードしたPDFにテキストを追加したり、署名欄を埋めたりする用途に最適です。

// pdf-lib: 既存PDFにテキストを追加
import { PDFDocument, rgb } from 'pdf-lib';
import fontBytes from './font.ttf';

const existingPdfBytes = await fetch('/template.pdf').then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(existingPdfBytes);

const helveticaFont = await pdfDoc.embedFont(fontBytes);
const pages = pdfDoc.getPages();
const firstPage = pages[0];

firstPage.drawText('Hello World!', {
  x: 50,
  y: 700,
  size: 20,
  font: helveticaFont,
  color: rgb(0, 0.53, 0.71),
});

const modifiedPdfBytes = await pdfDoc.save();

pdfkit は白紙のキャンバスからPDFを一から構築します。描画コマンドを使ってテキスト、図形、画像などを自由に配置できます。

// pdfkit: ゼロからPDFを生成
import PDFDocument from 'pdfkit';

const doc = new PDFDocument({ size: 'A4' });
let buffers = [];
doc.on('data', buffers.push.bind(buffers));
doc.on('end', () => {
  const pdfData = Buffer.concat(buffers);
  // ブラウザならBlobとしてダウンロード可能
});

doc.fontSize(20).text('Hello World!', 50, 700);
doc.strokeColor('#008000').lineWidth(2).moveTo(50, 680).lineTo(200, 680).stroke();
doc.end();

💡 注:pdfkit はStreamベースのAPIなので、ブラウザで使うにはblob-streamなどの補助ライブラリが必要になることがあります。

🌐 ブラウザ互換性と依存関係

pdf-lib は純粋なESMモジュールとして提供されており、ブラウザ環境でそのまま使えます。外部依存が少なく、バンドルサイズも比較的小さく抑えられています。Web Worker内での使用も可能です。

pdfkit は元々Node.js向けに設計されており、内部でstreamzlibなどのNode固有モジュールを使っています。そのため、ブラウザで使うにはWebpackやViteなどのバンドラでポリフィルを適用する必要があります。これがプロジェクトのビルド設定を複雑にすることがあります。

// Vite + pdfkit の例(vite.config.js)
export default defineConfig({
  resolve: {
    alias: {
      stream: 'stream-browserify',
      zlib: 'browserify-zlib'
    }
  }
});

もしプロジェクトが純粋なフロントエンド(Next.js App RouterやVite単体など)で、Node.js互換レイヤーが使えない場合、pdfkit の導入は困難になる可能性があります。

✍️ テキストとフォントの扱い

pdf-lib は埋め込み済みの標準フォント(Helvetica, Times Romanなど)に加え、TTF/OTFフォントを動的に埋め込むことができます。ただし、描画機能は基本的なものに限られます(太字・斜体の切り替えはできません)。

// pdf-lib: カスタムフォント埋め込み
const fontBytes = await fetch('/NotoSansJP-Regular.ttf').then(res => res.arrayBuffer());
const customFont = await pdfDoc.embedFont(fontBytes);
page.drawText('こんにちは', { font: customFont, size: 16 });

pdfkit は非常に高度なテキスト描画をサポートしています。フォントのスタイル(bold, italic)、行間、段落、ワードラップ、縦書きなど、出版レベルの制御が可能です。

// pdfkit: 複雑なテキストレイアウト
doc.font('Helvetica-Bold')
   .fontSize(18)
   .text('タイトル', { align: 'center' });

doc.moveDown()
   .font('Helvetica')
   .fontSize(12)
   .text('長い本文...', {
     width: 400,
     align: 'justify',
     indent: 20
   });

日本語などの非Latin文字を使う場合、pdfkit の方がより柔軟に扱えますが、その分初期設定が複雑になります。

🖼️ 画像の埋め込み

pdf-lib はJPEGとPNGの埋め込みをサポートしています。画像データをUint8Arrayとして渡す必要があります。

// pdf-lib: 画像埋め込み
const jpgUrl = '/photo.jpg';
const jpgImageBytes = await fetch(jpgUrl).then(res => res.arrayBuffer());
const jpgImage = await pdfDoc.embedJpg(jpgImageBytes);
page.drawImage(jpgImage, { x: 50, y: 500, width: 200, height: 150 });

pdfkit も同様にJPEG、PNG、GIFをサポートしており、さらにSVGの描画も可能です(ただしSVGはパスに変換されるため、完全互換ではありません)。

// pdfkit: 画像埋め込み
doc.image('/photo.jpg', 50, 500, { width: 200 });

🧩 フォームとインタラクション

pdf-lib はPDFフォーム(AcroForm)の操作に強いです。既存のフォームフィールドを取得して値を設定したり、新しいフィールドを追加したりできます。

// pdf-lib: フォーム入力
const form = pdfDoc.getForm();
const nameField = form.getTextField('name');
nameField.setText('山田太郎');
form.flatten(); // フォームを固定(編集不可に)

pdfkit はフォーム機能をサポートしていません。すべての内容は静的な描画として出力されます。つまり、後からユーザーが入力できるようなインタラクティブなPDFは作れません。

⚙️ 非同期処理モデル

pdf-lib はすべてのI/O操作(フォント埋め込み、画像埋め込み、PDF読み込み)が非同期(Promiseベース)です。これは現代的なフロントエンド開発との相性が良く、ReactのuseEffectやasync/awaitと自然に統合できます。

pdfkit はStreamベースの同期的な描画APIを持ちますが、最終的な出力(BufferやBlob)を得るにはイベント待ちが必要です。これはコールバック地獄になりやすく、async/awaitと組み合わせるにはラッパー関数が必要です。

// pdfkit を Promise 化するユーティリティ
function generatePdf() {
  return new Promise((resolve) => {
    const doc = new PDFDocument();
    let buffers = [];
    doc.on('data', buffers.push.bind(buffers));
    doc.on('end', () => resolve(Buffer.concat(buffers)));
    
    doc.text('Hello');
    doc.end();
  });
}

🔒 セキュリティと信頼性

両ライブラリとも現在もアクティブにメンテナンスされており、公式npmページにdeprecation警告はありません。ただし、pdfkit は長年にわたり安定しているものの、最近の更新頻度はやや低めです。一方、pdf-lib は比較的新しく、積極的に機能追加が行われています。

📊 まとめ:どちらを選ぶべきか?

要件pdf-libpdfkit
既存PDFの編集✅ 強力❌ 不可
ゼロからの描画△ 基本的✅ 高度
ブラウザ互換性✅ 素直△ ビルド設定が必要
フォーム操作✅ 対応❌ 非対応
非同期API✅ Promiseベース△ Stream + イベント
日本語対応✅(カスタムフォント)✅(詳細制御可能)

最終的な判断基準

  • 「テンプレートPDFを読み込んで、そこに情報を埋めたい」pdf-lib
  • 「完全にオリジナルの帳票やレポートを一から作りたい」pdfkit

多くのフロントエンドプロジェクトでは、pdf-lib の方が導入しやすく、メンテナンスコストも低いです。一方、印刷物に近い高品質なPDFが必要な場合は、pdfkit の描画能力が活きます。ただし、ブラウザ環境での制約をよく検討してください。

選び方: pdfkit vs pdf-lib

  • pdfkit:

    pdfkit を選ぶべきケースは、完全にゼロから複雑なレイアウトやグラフィックを含むPDFを生成する必要がある場合です。帳票、請求書、レポートなど、細かい描画制御(フォント、パス、画像配置など)が求められるサーバーサイドまたはハイブリッドなフロントエンドアプリケーションに向いています。ただし、ブラウザでの使用には追加のビルド設定が必要になることがあります。

  • pdf-lib:

    pdf-lib を選ぶべきケースは、既存のPDFテンプレートを読み込んで署名、テキスト追加、フォーム入力などの軽量な編集を行う必要がある場合です。特にブラウザ上で直接PDFを操作したいフロントエンドアプリケーション(例:契約書への署名、証明書の自動生成)に最適です。APIが直感的で、非同期処理をうまく扱える点もメリットです。

pdfkit のREADME

PDFKit

A JavaScript PDF generation library for Node and the browser.

Description

PDFKit is a PDF document generation library for Node and the browser that makes creating complex, multi-page, printable documents easy. The API embraces chainability, and includes both low level functions as well as abstractions for higher level functionality. The PDFKit API is designed to be simple, so generating complex documents is often as simple as a few function calls.

Check out some of the documentation and examples to see for yourself! You can also read the guide as a self-generated PDF with example output displayed inline. If you'd like to see how it was generated, check out the README in the docs folder.

You can also try out an interactive in-browser demo of PDFKit here.

Installation

Use npm or yarn package manager. Just type the following command:

# with npm
npm install pdfkit

# with yarn
yarn add pdfkit

Features

  • Vector graphics
    • HTML5 canvas-like API
    • Path operations
    • SVG path parser for easy path creation
    • Transformations
    • Linear and radial gradients
  • Text
    • Line wrapping (with soft hyphen recognition)
    • Text alignments
    • Bulleted lists
  • Font embedding
    • Supports TrueType (.ttf), OpenType (.otf), WOFF, WOFF2, TrueType Collections (.ttc), and Datafork TrueType (.dfont) fonts
    • Font subsetting
    • See fontkit for more details on advanced glyph layout support.
  • Image embedding
    • Supports JPEG and PNG files (including indexed PNGs, and PNGs with transparency)
  • Tables
  • Annotations
    • Links
    • Notes
    • Highlights
    • Underlines
    • etc.
  • AcroForms
  • Outlines
  • PDF security
    • Encryption
    • Access privileges (printing, copying, modifying, annotating, form filling, content accessibility, document assembly)
  • Accessibility support (marked content, logical structure, Tagged PDF, PDF/UA)

Coming soon!

  • Patterns fills
  • Higher level APIs for laying out content
  • More performance optimizations
  • Even more awesomeness, perhaps written by you! Please fork this repository and send me pull requests.

Example

Both const PDFDocument = require('pdfkit') and import PDFDocument from 'pdfkit' remain supported for backward compatibility. New code should prefer the named PDFDocument export, which will make a future migration to an ESM-only package more straightforward.

const { PDFDocument } = require('pdfkit');
const fs = require('fs');

// Create a document
const doc = new PDFDocument();

// Pipe its output somewhere, like to a file or HTTP response
// See below for browser usage
doc.pipe(fs.createWriteStream('output.pdf'));

// Embed a font, set the font size, and render some text
doc
  .font('fonts/PalatinoBold.ttf')
  .fontSize(25)
  .text('Some text with an embedded font!', 100, 100);

// Add an image, constrain it to a given size, and center it vertically and horizontally
doc.image('path/to/image.png', {
  fit: [250, 300],
  align: 'center',
  valign: 'center'
});

// Add another page
doc
  .addPage()
  .fontSize(25)
  .text('Here is some vector graphics...', 100, 100);

// Draw a triangle
doc
  .save()
  .moveTo(100, 150)
  .lineTo(100, 250)
  .lineTo(200, 250)
  .fill('#FF3300');

// Apply some transforms and render an SVG path with the 'even-odd' fill rule
doc
  .scale(0.6)
  .translate(470, -380)
  .path('M 250,75 L 323,301 131,161 369,161 177,301 z')
  .fill('red', 'even-odd')
  .restore();

// Add some text with annotations
doc
  .addPage()
  .fillColor('blue')
  .text('Here is a link!', 100, 100)
  .underline(100, 100, 160, 27, { color: '#0000FF' })
  .link(100, 100, 160, 27, 'http://google.com/');

// Finalize PDF file
doc.end();

The PDF output from this example (with a few additions) shows the power of PDFKit — producing complex documents with a very small amount of code. For more, see the demo folder and the PDFKit programming guide.

Browser Usage

There are three ways to use PDFKit in the browser:

In addition to PDFKit, you'll need to collect its output. Browsers provide a Blob object for storing binary data and creating URLs that can be displayed in an iframe, downloaded or uploaded.

Experimental output helpers

PDFKit provides experimental toBlob and toBytes helpers from pdfkit/output. These functions may change before they are stabilized. Call the selected helper before ending the document so it receives the complete output.

Use toBlob when displaying, downloading or uploading the PDF in a browser:

import { PDFDocument, registerStdFonts } from 'pdfkit';
import Helvetica from 'pdfkit/standard-fonts/Helvetica';
import HelveticaBold from 'pdfkit/standard-fonts/HelveticaBold';
import { toBlob } from 'pdfkit/output';

registerStdFonts(Helvetica, HelveticaBold);
const doc = new PDFDocument();
const output = toBlob(doc);

// Add your content to the document here, as usual.

doc.end();
const blob = await output;
const url = URL.createObjectURL(blob);
iframe.src = url;

// Revoke the URL when the iframe no longer needs the PDF.
// URL.revokeObjectURL(url);

Use toBytes instead when a binary API, worker or parser needs one contiguous Uint8Array:

import { toBytes } from 'pdfkit/output';

const output = toBytes(doc);
doc.end();
const bytes = await output;

The stable, dependency-free alternative is to collect the document's Uint8Array chunks using its events and construct the Blob directly:

const chunks = [];

doc.on('data', chunk => chunks.push(chunk));
doc.on('end', () => {
  const blob = new Blob(chunks, { type: 'application/pdf' });
  const url = URL.createObjectURL(blob);
  iframe.src = url;
});

// Add your content to the document here, as usual.
doc.end();

You can see an interactive in-browser demo of PDFKit here.

The browser build has no access to the file system. You can register a Uint8Array under a path before passing that exact path to registerFont, image or file:

import { PDFDocument, registerFile } from 'pdfkit';

const response = await fetch('/fonts/Roboto-Regular.ttf');
const fontData = new Uint8Array(await response.arrayBuffer());

registerFile('fonts/Roboto-Regular.ttf', fontData);

const doc = new PDFDocument();

// register an alias for the font path
doc.registerFont('Roboto', 'fonts/Roboto-Regular.ttf');
// or use the path directly
doc.font('fonts/Roboto-Regular.ttf');

// Optionally unregister the path when it is no longer needed.
registerFile('fonts/Roboto-Regular.ttf', undefined);

Registration is global to the loaded PDFKit module. Registering the same path again replaces its data. In Node, registered data takes precedence over a file at the same path; unregistering it restores normal file system lookup. Both APIs are available as named exports from the CommonJS entry point:

const { PDFDocument, registerFile } = require('pdfkit');

registerFile('files/example.txt', new Uint8Array([1, 2, 3]), {
  birthtime: new Date('2020-01-02T03:04:05Z'),
  ctime: new Date('2021-02-03T04:05:06Z'),
});
registerFile('files/example.txt', undefined);

registerFile accepts only a Uint8Array or undefined. You can still pass a Uint8Array or ArrayBuffer directly to registerFont, image and file, and you can pass a data URL directly to image and file. An unregistered file path throws in the browser. The optional birthtime and ctime values must be valid Date objects; either omitted value defaults to the time of registration.

Documentation

For complete API documentation and more examples, see the PDFKit website.

License

PDFKit is available under the MIT license.