前端开发者收到一份 40 页的品牌规范 PDF,需要把其中三页放进 Figma 画板。技术支持工程师想把数据手册里的某张图表贴到 Slack 讨论串里。律师需要把已签合同的某一页作为附件发送邮件,而不是发送整份文件。
PDF 是为固定版式文档设计的,而网页、图片编辑器和聊天应用都是基于像素的。将 PDF 页面转换为图片正好弥合这个鸿沟,但选择的转换方法会直接影响输出质量、文件大小以及可控程度。
为什么 PDF 在这方面既好用又麻烦
PDF 以绘图指令流的形式存储页面:放置这个字形、绘制这个矢量对象、按这个尺寸渲染这张图片。这使它具备分辨率无关性和视觉一致性,同时也意味着 PDF 不是图片。要把它变成图片,必须有东西把这些指令渲染到光栅画布上。
优点:
- 文字和矢量图形在任何缩放级别下都保持锐利,因为它们是数学描述,而非像素存储。
- 单个 PDF 可以在一个文件中容纳数百页。
- 字体、色彩配置文件和批注都会随文档一起携带。
难点:
- 不同 PDF 阅读器的渲染结果存在差异。同一页面在 Adobe Acrobat、Preview、Chrome 或某个无头库中看起来可能略有不同。
- 扫描版 PDF 只是被 PDF 容器包裹的图片,因此"转换"它们意味着重新编码,可能引入伪影或体积膨胀。
- 带有透明度、图层或交互表单的复杂 PDF 可能在扁平化时产生不可预期的结果。
- 页面尺寸不固定。US Letter 页面在 72 DPI 下是 612 x 792 像素,在 300 DPI 下则是 2550 x 3300。如果不指定分辨率,可能得到无法使用的输出。
人们通常把 PDF 转换成什么
大多数转换任务会落在以下三种输出格式之一。它们的用途各不相同。
| 格式 | 最适合 | 取舍 |
|---|---|---|
| JPG | 照片、预览图、邮件附件、网页图库 | 有损压缩,但文件体积小 |
| PNG | 截图、示意图、需要透明度的场景 | 无损压缩,照片体积通常大于 JPG |
| WebP | 现代网页、应用、任何需要考虑带宽的地方 | 比 JPG/PNG 更小,兼容性稍逊 |
还有一些实际维度需要考虑:
- 单页还是批量。 单页很容易;整文件夹的发票则需要自动化。
- 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
安装 Windows 版 Poppler,然后在 PowerShell 中使用 pdftoppm:
pdftoppm -jpeg -r 300 input.pdf output
这会生成 output-1.jpg、output-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
- 用 Preview 打开 PDF。
- 选中需要的页面缩略图。
- 选择 文件 > 导出,选择格式并设置分辨率。
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,并且每个 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 下 2 等于 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 接收从 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)。
使用封装了 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 通常使用从 0 开始的页面索引,命令行工具通常使用从 1 开始。
不同场景的选择
- 一次性个人使用: macOS 上的 Preview、Windows 上的 PDF 阅读器,或浏览器端转换器。
- 服务器批量处理:
pdftoppm或pdf2image。 - 产品内部集成: Java 用 Apache PDFBox,Python 用
pymupdf,TypeScript 用pdfjs-dist。 - 隐私敏感文件: 使用浏览器端工具,让 PDF 不离开设备。
如果你想在不安装任何东西的情况下最快完成转换,我们的 PDF 转 JPG、PDF 转 PNG 和 PDF 转 WebP 转换器完全在浏览器中运行。



