フロントエンド開発者が40ページのブランドガイドラインPDFを受け取り、3ページだけFigmaボードに貼り付けたい。サポートエンジニアがデータシートの図をSlackスレッドに貼りたい。弁護士が契約書の署名済みページをメールに添付し、ファイル全体は送りたくない。
PDFは固定レイアウトドキュメント向けに作られている。Web、画像エディタ、チャットアプリはピクセル向きだ。PDFページを画像に変換することでその溝を埋められるが、選ぶ方法によって画質、ファイルサイズ、自由度が変わる。
PDFがこの作業に向いている点、面倒な点
PDFはページを描画コマンドのストリームとして保持する:このグリフを配置、ベクターを描画、画像をこのサイズでレンダリング。これにより解像度に依存せず、視覚的に一貫した出力が得られる。同時に、PDFは画像ではない。画像に変換するには、何らかの処理がこれらのコマンドをラスターキャンバス上にレンダリングする必要がある。
利点:
- テキストとベクターは数学的に記述されているため、どの倍率でも鮮明に保たれる。ピクセルとして保存されていないからだ。
- 1つのPDFに数百ページをまとめられる。
- フォント、カラープロファイル、アノテーションが文書と一緒に移動する。
難点:
- PDFリーダー間でレンダリングが異なる。同じページでもAdobe Acrobat、Preview、Chrome、ヘッドレスライブラリで微妙に見え方が変わる。
- スキャンPDFはPDFコンテナに包まれた画像に過ぎないため、「変換」は再エンコードを意味し、ノイズが入ったりファイルが肥大したりする。
- 透過、レイヤー、インタラクティブフォームを含む複雑なPDFは、予測できない形でフラット化されることがある。
- ページサイズはさまざまだ。US Letterページは72 DPIで612×792ピクセル。300 DPIでは2550×3300だ。解像度を指定しなければ、使えない出力になることがある。
実際にPDFが変換される先
多くの変換作業は3つの出力形式のいずれかに該当する。用途はそれぞれ異なる。
| Format | Best for | Trade-off |
|---|---|---|
| JPG | 写真、プレビュー、メール添付、Webギャラリー | ロス圧縮だがファイルサイズが小さい |
| PNG | スクリーンショット、図解、透過が必要なもの | 非可逆圧縮、写真ではJPGより大きい |
| WebP | モダンなWeb、アプリ、帯域が気になる場所 | JPG/PNGより小さいが、普及率はやや低い |
次に実務上のパラメータがある:
- 1ページか一括か。 1ページなら簡単。請求書のフォルダなら自動化が必要だ。
- DPI。 150 DPIはサムネイルに十分。300 DPIは印刷やOCRの標準。600 DPIは細部を拡大して確認しない限りオーバースペックだ。
- カラースペース。 画面向けにはRGBが無難だ。CMYKのPDFをRGBに単純に変換すると、色がずれることがある。
適切なアプローチの選択
最適なツールは、何を最適化したいかに依存する。
ブラウザベースの高速変換
JPG、PNG、WebPとしてページが必要で、サーバー必須ほど機密性が高くないなら、ブラウザベースの変換ツールが最も手軽だ。当社のPDFをJPGへ、PDFをPNGへ、PDFをWebPへツールはPDFをローカルでレンダリングする。ファイルが端末から外に出ないため、契約書、身分証明書、医療記録に有効だ。
コマンドラインでの一括作業
PDFのフォルダやCIパイプラインでは、コマンドラインツールが勝る。再現性のある出力、DPIの制御、スクリプト化が可能だ。
アプリケーション内での変換
変換がプロダクトの一部となる場合、ライブラリを呼び出す方がCLIツールを呼び出すよりすっきりすることが多い。外部依存を減らし、コードベース全体に合わせたエラー処理を実現できる。
Windowsでの変換
Adobe Acrobat
- PDFを開き、必要なページに移動する。
- ファイル > 書き出し > 画像 > JPEG/PNG/TIFF を選択する。
- 書き出しダイアログで出力解像度を設定する。
- 保存する。
Acrobatの出力は信頼できるが、無料ではなく、有料SDKがなければスクリプト化できない。
PDF-XChange Editor
無料プランがある軽量な代替品。ファイル > エクスポート > ページを画像としてエクスポートで形式、DPI、ページ範囲を選択できる。
PowerShellでpdftoppmを使う
Poppler for Windowsをインストールし、PowerShellからpdftoppmを使う:
pdftoppm -jpeg -r 300 input.pdf output
これによりoutput-1.jpg、output-2.jpgのように、各ページごとに1枚、300 DPIで出力される。
透過背景のPNGが必要な場合:
pdftoppm -png -r 300 input.pdf output
1ページだけの場合:
pdftoppm -jpeg -r 300 -f 1 -l 1 input.pdf output
-fと-lフラグはそれぞれ開始ページと終了ページを指定する。
PowerShellでImageMagickを使う
ImageMagickはPDFをレンダリングできるが、Windowsでは通常、内部でGhostscriptを使っている:
magick -density 300 input.pdf[0] output.jpg
[0]は最初のページを意味する。これがないと、ImageMagickはマルチフレーム画像を生成しようとすることがある。
macOSでの変換
Preview
- PreviewでPDFを開く。
- 必要なページのサムネイルを選択する。
- ファイル > 書き出しを選び、形式と解像度を設定する。
Previewは高速でプライバシー性も高いが、一度に1ページしか処理できない。
ターミナルでsipsを使う
macOSにはsipsが含まれているが、PDFのテキストをうまくレンダリングしない。すでにビットマップのPDFにのみ使う:
sips -s format jpeg input.pdf --out output.jpg
本格的なPDFレンダリングには、HomebrewでPopplerをインストールする:
brew install poppler
pdftoppm -jpeg -r 300 input.pdf output
Automatorクイックアクション
Automatorで、選択したPDFに対してpdftoppmを実行する右クリックサービスを作成できる。定期的にページを変換する場合に便利で、フラグを覚える必要がない。
Linuxでの変換
Linuxでは通常、Popplerが最適な選択だ。
sudo apt install poppler-utils
pdftoppm -jpeg -r 300 input.pdf output
pdftoppm -png -r 300 input.pdf output
WebP出力が必要な場合は、まずPNGに変換してからcwebpを使う:
pdftoppm -png -r 300 input.pdf temp
cwebp temp-1.png -o output.webp
一括変換
PDFのフォルダがあり、各PDFの最初のページを1枚ずつ画像にしたい場合:
for f in *.pdf; do
pdftoppm -jpeg -r 300 -f 1 -l 1 "$f" "${f%.pdf}"
done
ImageMagick
ImageMagickはLinuxでも動作するが、Ghostscript経由でラスタライズするため、PDFの処理は通常遅い:
magick -density 300 input.pdf[0] -quality 90 output.jpg
-densityはPDFを読み込む前に指定する。後に置いても効果がない。
コードによる変換
TypeScript / Node.js
pdfjs-distを使ってページをキャンバスにレンダリングし、画像データとして書き出す。
import * as pdfjsLib from "pdfjs-dist"
import { createCanvas } from "canvas"
import fs from "fs"
async function pdfPageToPng(
pdfPath: string,
pageNumber: number,
outputPath: string,
scale: number = 2
) {
const data = new Uint8Array(fs.readFileSync(pdfPath))
const pdf = await pdfjsLib.getDocument({ data }).promise
const page = await pdf.getPage(pageNumber)
const viewport = page.getViewport({ scale })
const canvas = createCanvas(viewport.width, viewport.height)
const context = canvas.getContext("2d")
await page.render({ canvasContext: context, viewport }).promise
const buffer = canvas.toBuffer("image/png")
fs.writeFileSync(outputPath, buffer)
await pdf.destroy()
}
await pdfPageToPng("input.pdf", 1, "output.png", 2)
scale値はおおまかにDPIに対応する:72 DPIで2なら144 DPIの出力になる。300 DPIが必要なら、約4.17のスケールを使う。
ブラウザでは、当社のPDFをPNGへ変換ツールが同じキャンバスへのレンダリングを、ファイルをどこにも送信せずに行う。
PHP
PHPはpdftoppmを直接呼び出すか、ImageMagickをラップするspatie/pdf-to-imageのようなライブラリを使える:
<?php
use Spatie\PdfToImage\Pdf;
$pdf = new Pdf('input.pdf');
$pdf->setPage(1)
->setOutputFormat('png')
->saveImage('output.png');
依存を増やしたくない場合は、Popplerを直接呼び出す:
<?php
function pdfPageToPng(string $input, int $page, string $output, int $dpi = 300): void {
$cmd = sprintf(
'pdftoppm -png -r %d -f %d -l %d %s %s',
$dpi,
$page,
$page,
escapeshellarg($input),
escapeshellarg($output)
);
exec($cmd);
}
pdfPageToPng('input.pdf', 1, 'output', 300);
これはoutput-1.pngを書き出す。本番で使う前に、独自のエラーチェックを追加すること。
Go
Goに組み込みのPDFレンダラはないが、github.com/gen2brain/go-fitzはMuPDFをラップしている:
package main
import (
"image/jpeg"
"os"
"github.com/gen2brain/go-fitz"
)
func main() {
doc, err := fitz.New("input.pdf")
if err != nil {
panic(err)
}
defer doc.Close()
img, err := doc.Image(0)
if err != nil {
panic(err)
}
out, err := os.Create("output.jpg")
if err != nil {
panic(err)
}
defer out.Close()
if err := jpeg.Encode(out, img, &jpeg.Options{Quality: 90}); err != nil {
panic(err)
}
}
go-fitzはPDFの既定のレンダリング解像度で画像を返す。DPIを制御する必要がある場合はライブラリのドキュメントを確認する。
Java
JavaでのPDF作業にはApache PDFBoxが標準的な選択だ。
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class PdfToImage {
public static void main(String[] args) throws IOException {
try (PDDocument document = PDDocument.load(new File("input.pdf"))) {
PDFRenderer renderer = new PDFRenderer(document);
BufferedImage image = renderer.renderImageWithDPI(0, 300);
ImageIO.write(image, "png", new File("output.png"));
}
}
}
Maven dependency:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>3.0.2</version>
</dependency>
renderImageWithDPIは0始まりのページインデックスとDPI値を受け取る。
Python
Pythonではpymupdfまたはpdf2imageで簡単に変換できる。
pymupdfを使う場合:
import fitz
doc = fitz.open("input.pdf")
page = doc[0]
mat = fitz.Matrix(2, 2)
pix = page.get_pixmap(matrix=mat)
pix.save("output.png")
Matrixはスケールを制御する。72 DPIのPDFからおおまかに300 DPIを得るには、fitz.Matrix(300/72, 300/72)を使う。
pdf2imageを使う場合(Popplerをラップしている):
from pdf2image import convert_from_path
images = convert_from_path("input.pdf", dpi=300, first_page=1, last_page=1)
images[0].save("output.jpg", "JPEG", quality=90)
pdf2imageは便利だが、システムにPopplerがインストールされている必要がある。
よくある落とし穴
- DPIの指定を忘れる。 既定のPDFレンダリングは多くの場合72または96 DPIだ。その解像度ではテキストがぼやける。画質が重要なら、必ず出力DPIを指定する。
- カラースペースを無視する。 プロファイルなしにCMYKのPDFをRGBに変換すると、色が褪せたり過剰に鮮やかになったりすることがある。
- スキャンPDFを再エンコードする。 PDFがすでにJPEGスキャンの場合、PNGに変換しても失われた細部は復元されない。ファイルサイズが大きくなるだけだ。
- フォントの置換。 ヘッドレスサーバーにはPDFに埋め込まれたフォントが欠けていることがある。レンダラが代替フォントを使い、レイアウトが崩れる。PDF作成時にフォントを埋め込んでおけば防げる。
- ページ番号の1つずれ。 コードのAPIは通常0始まりのページインデックスを、コマンドラインツールは通常1始まりのページ番号を使う。
場面に応じた選択
- 1回限りの個人利用: macOSのPreview、WindowsのPDFリーダー、またはブラウザベースの変換ツール。
- サーバーでの一括処理:
pdftoppmまたはpdf2image。 - プロダクト内: JavaならApache PDFBox、Pythonなら
pymupdf、TypeScriptならpdfjs-dist。 - 機密性の高いファイル: ブラウザベースのツールを使い、PDFが端末から外に出ないようにする。
何もインストールせずに最速で変換したい場合、当社のPDFをJPGへ、PDFをPNGへ、PDFをWebPへ変換ツールは完全にブラウザ内で動作する。



