深度解析

如何將 PDF 頁面轉換為圖片:實用指南

koboshiCo-founder
·5 分鐘閱讀
如何將 PDF 頁面轉換為圖片:實用指南
概述

PDF 頁面不一定適用於所有場景。本指南涵蓋何時該將 PDF 轉為圖片、各種方法的取捨、各作業系統的原生做法,以及五種語言的程式碼範例。

前端開發者收到一份 40 頁的品牌規範 PDF,需要把其中三頁放進 Figma。支援工程師想從規格書貼一張圖到 Slack。律師需要把簽好的合約某一頁附加在郵件裡,但不想寄出整份檔案。

PDF 是為固定版式文件設計的,而網頁、圖片編輯器與聊天軟體則是以像素為基礎。把 PDF 頁面轉成圖片可以彌補這段落差,但選擇的方法會影響輸出品質、檔案大小以及你保留多少控制權。

PDF 在這件事上既好用又麻煩的原因

PDF 把頁面儲存為一連串繪圖指令:在這裡放這個字形、畫這個向量、在這個尺寸渲染這張圖片。這讓它具備解析度獨立性與視覺一致性,也代表 PDF 本身不是圖片。要轉成圖片,就必須有東西把這些指令渲染到點陣畫布上。

優點:

  • 文字與向量在任何縮放比例下都保持銳利,因為它們是以數學方式描述,而非以像素儲存。
  • 單一 PDF 可以容納數百頁。
  • 字型、色彩設定檔與註解會隨文件一起攜帶。

缺點:

  • 不同 PDF 閱讀器的渲染結果不一致。同一頁在 Adobe Acrobat、Preview、Chrome 或無頭函式庫中可能看起來略有差異。
  • 掃描件 PDF 只是包在 PDF 容器裡的圖片,所以「轉換」它們其實是重新編碼,可能產生壓縮瑕疵或讓檔案變大。
  • 帶有透明、圖層或互動式表單的複雜 PDF,在壓平時可能出現預期外的結果。
  • 頁面尺寸不固定。US Letter 頁面在 72 DPI 下是 612 × 792 像素,在 300 DPI 下則是 2550 × 3300。如果沒有指定解析度,輸出可能無法使用。

大家實際上把 PDF 轉成什麼

大多數轉換任務可歸類為三種輸出格式之一,各自有不同的用途。

格式最適合取捨
JPG照片、預覽圖、郵件附件、網路圖庫有損壓縮,但檔案小
PNG截圖、圖表、需要透明的情境無損壓縮,照片比 JPG 大
WebP現代網頁、應用程式、任何頻寬敏感的情境比 JPG/PNG 更小,支援度略低

還有幾個實務上的維度:

  • 單頁或批次。 單頁很簡單;一整批發票則需要自動化。
  • DPI。 150 DPI 對縮圖來說足夠;300 DPI 是列印與 OCR 的標準;600 DPI 除非要放大檢視細節,否則過剩。
  • 色彩空間。 RGB 對螢幕來說安全;CMYK PDF 若用簡陋方式轉成 RGB,顏色可能會偏移。

選擇合適的做法

正確的工具取決於你想優化什麼。

快速的瀏覽器端轉換

如果你只需要把一頁轉成 JPG、PNG 或 WebP,而且檔案不至於敏感到必須留在伺服器上,那麼瀏覽器端轉換是最快的路徑。我們的 PDF 轉 JPGPDF 轉 PNGPDF 轉 WebP 工具會在本地渲染 PDF。檔案不會離開你的裝置,這對合約、證件與病歷來說很重要。

命令列批次處理

面對一整批 PDF 或 CI 流程時,命令列工具勝出。你可以得到可重複的輸出、控制 DPI,並能編寫腳本。

應用程式內轉換

當轉換是產品的一部分時,直接呼叫函式庫通常比呼叫外部 CLI 更乾淨。這能減少外部依賴,並讓錯誤處理與程式碼庫的其他部分保持一致。

在 Windows 上轉換

Adobe Acrobat

  1. 開啟 PDF 並前往所需頁面。
  2. 選擇 檔案 > 匯出為 > 影像 > JPEG/PNG/TIFF
  3. 在匯出對話方塊中設定輸出解析度。
  4. 儲存。

Acrobat 的輸出穩定,但它不是免費軟體,而且沒有付費 SDK 就無法編寫腳本。

PDF-XChange Editor

較輕量的替代方案,有免費版。檔案 > 匯出 > 匯出頁面為影像 可讓你選擇格式、DPI 與頁面範圍。

在 PowerShell 中使用 pdftoppm

在 Windows 上安裝 Poppler 後,即可從 PowerShell 使用 pdftoppm

pdftoppm -jpeg -r 300 input.pdf output

這會產生 output-1.jpgoutput-2.jpg 等檔案,每頁一張,解析度為 300 DPI。

若要輸出帶透明背景的 PNG:

pdftoppm -png -r 300 input.pdf output

若只要單一頁面:

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

  1. 在 Preview 中開啟 PDF。
  2. 選取你要的頁面縮圖。
  3. 選擇 檔案 > 匯出,挑選格式並設定解析度。

Preview 快速且注重隱私,但一次只能處理一頁。

在終端機中使用 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 上轉換

Poppler 通常是 Linux 上的最佳選擇。

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,想要每份檔案各輸出第一頁圖片:

for f in *.pdf; do
    pdftoppm -jpeg -r 300 -f 1 -l 1 "$f" "${f%.pdf}"
done

ImageMagick

ImageMagick 在 Linux 上也能用,但對 PDF 通常較慢,因為它會透過 Ghostscript 進行點陣化:

magick -density 300 input.pdf[0] -quality 90 output.jpg

-density 必須放在讀取 PDF 之前,放在後面沒有效果。

使用程式碼轉換

TypeScript / Node.js

使用 pdfjs-dist 把頁面渲染到 canvas,再匯出為圖片資料。

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 下,scale2 會得到 144 DPI 的輸出。若要 300 DPI,scale 約為 4.17

在瀏覽器中,我們的 PDF 轉 PNG 轉換器會執行相同的 render-to-canvas 步驟,且不會把檔案傳送到任何地方。

PHP

PHP 可以直接呼叫 pdftoppm,或使用 spatie/pdf-to-image 這類包裝 ImageMagick 的函式庫:

<?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

Apache PDFBox 是 Java PDF 處理的標準選擇。

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>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>3.0.2</version>
</dependency>

renderImageWithDPI 接受以零為底的頁面索引與 DPI 值。

Python

Python 透過 pymupdfpdf2image 可以輕鬆完成。

使用 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)

使用包裝 Poppler 的 pdf2image

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 時內嵌字型可避免這個問題。
  • 頁面編號差一。 程式 API 通常使用以零為底的頁面索引;命令列工具通常使用以一為底。

不同情境該用什麼

  • 一次性個人使用: macOS 的 Preview、Windows 上的 PDF 閱讀器,或瀏覽器轉換工具。
  • 伺服器上的批次處理: pdftoppmpdf2image
  • 產品內部整合: Java 用 Apache PDFBox、Python 用 pymupdf、TypeScript 用 pdfjs-dist
  • 隱私敏感檔案: 使用瀏覽器端工具,讓 PDF 不會離開裝置。

如果你想完全不安裝任何東西就完成轉換,我們的 PDF 轉 JPGPDF 轉 PNGPDF 轉 WebP 轉換器全部在瀏覽器中執行。

更多推薦閱讀