从PDF中提取一个页面作为JPEG

187

在Python代码中,如何高效地将PDF的某一页保存为JPEG文件?

使用场景:我有一个Python Flask Web服务器,PDF将上传并存储对应每个页面的JPEG。

这个解决方案很接近,但问题在于它没有将整个页面转换为JPEG。


2
根据图片的情况,最好将其提取为png格式。如果页面主要包含文本,则适用此方法。 - Paul Rooney
尽管通常是正确的,但使用fitz输出PNG格式的代码质量明显低于使用JPG格式的被接受代码。我怀疑图像分辨率会根据PDF纸张大小进行调整。 - Nelson
17个回答

223

可以使用pdf2image库。

您可以简单地安装它,使用以下命令:

pip install pdf2image

安装完成后,您可以使用以下代码获取图像。

from pdf2image import convert_from_path
pages = convert_from_path('pdf_file', 500)

将页面保存为jpeg格式

for count, page in enumerate(pages):
    page.save(f'out{count}.jpg', 'JPEG')

编辑: Github 仓库 pdf2image 也提到了它使用 pdftoppm 并且需要其他安装:

pdftoppm 是实现实际操作的软件。它作为一个名为poppler的大型软件包的一部分进行分发。 Windows 用户需要安装Windows 的 poppler。 Mac 用户需要安装Mac 的 poppler。 如果没有预先安装 pdftoppm,Linux 用户可以通过运行sudo apt install poppler-utils命令来安装(在 Ubuntu 和 Archlinux 上测试过)。

您可以使用 anaconda 在 Windows 上安装最新版本:

conda install -c conda-forge poppler

注意:Windows版本0.67及以下可在http://blog.alivate.com.au/poppler-windows/上获取,但请注意0.68已于2018年8月份发布,因此您将无法获得最新功能或错误修复。


6
嗨,poppler只是一个压缩文件,不会安装任何东西,那么dll文件或bin文件应该怎么处理? - gaurwraith
2
@elPastor,你可以在convert_from_path函数的参数中添加first_page和last_page来仅转换指定页面。 - Keval Dave
3
我用 conda install -c conda-forge poppler 安装了 Poppler,安装成功。 - MNA
3
用于只转换PDF的第一页且不做其他操作的Python代码如下:from pdf2image import convert_from_path pages = convert_from_path('file.pdf', 500) pages = convert_from_path('file.pdf', 500, single_file=True) pages[0].save('file.jpg', 'JPEG')注意:该代码需要先安装pdf2image库。 - helgis
1
Poppler的许可证基于GPL。在商业环境中要小心! - Shmack
显示剩余13条评论

142

我找到了这个简单的解决方案,PyMuPDF,可以将其输出为png文件。请注意,该库被导入为“fitz”,这是它使用的渲染引擎的历史名称。

import fitz

pdffile = "infile.pdf"
doc = fitz.open(pdffile)
page = doc.load_page(0)  # number of page
pix = page.get_pixmap()
output = "outfile.png"
pix.save(output)
doc.close()
注意:该库已从使用“camelCase”更改为“snake_cased”。如果你遇到一个函数不存在的错误,请查看弃用名称。上面示例中的函数已相应地更新。 fitz.Document类支持上下文管理器初始化:
with fitz.open(pdffile) as doc:
   ...

3
一本好的图书馆,它可以在Windows 10上安装,无需任何问题(不需要轮子)。 https://github.com/pymupdf - Comrade Che
32
这是最好的答案。这是唯一一个不需要在我的操作系统上进行额外安装的代码。Python脚本应该专注于在Python系统内工作。我不需要安装poppler、pdftoppm、imageMagick或ghostscript等软件。(Python 3.6) - ZStoneDPM
6
实际上,这需要另一个安装(fitz库,甚至在未被引用时已经导入以及其依赖项),这个回答是不完整的(就像所有这个问题的回答一样)。 - Tommaso Guerrini
8
image = page.getPixmap(matrix=fitz.Matrix(150/72,150/72)) 提取了150 DPI的图像。此主题上的问题。 - Josiah Yoder
8
这个解决方案使用了由Artifix Software商业许可的代码,以及AGPL许可下的开源代码。如果您的项目是商业性质的,请谨慎使用,您可能需要更深入地了解法律影响。 - Milo Persic
显示剩余7条评论

38
使用 pypdfium2(v4):
python3 -m pip install "pypdfium2==4" pillow

import pypdfium2 as pdfium

# Load a document
filepath = "tests/resources/multipage.pdf"
pdf = pdfium.PdfDocument(filepath)

# render a single page (in this case: the first one)
page = pdf[0]
pil_image = page.render(scale=4).to_pil()
pil_image.save("output.jpg")

# render multiple pages concurrently (in this case: all)
page_indices = [i for i in range(len(pdf))]
renderer = pdf.render(pdfium.PdfBitmap.to_pil, page_indices=page_indices)
for index, image in zip(page_indices, renderer):
    image.save("output_%02d.jpg" % index)

优势:

  • PDFium采用自由许可证(BSD 3-Clause或Apache 2.0,根据您的选择)
  • 它速度快,超过了Poppler。在速度方面,pypdfium2几乎可以达到{{link1:PyMuPDF}}
  • 根据您的需求,返回{{link2:PIL.Image.Image}}、{{link3:numpy.ndarray}}或ctypes数组
  • 能够处理加密(密码保护)的PDF文件
  • 没有强制运行时依赖
  • 支持Python >= 3.6
  • 安装基础设施符合PEP 517/518标准

目前已提供以下版本的安装包:

  • Windows amd64, win32, arm64
  • macOS x86_64, arm64
  • Linux (glibc 2.26+) x86_64, i686, aarch64, armv7l
  • Linux (musl 1.2+) x86_64, i686

还有一个脚本可以从源代码构建。

(免责声明:我是作者)


6
这是对我最有效的解决方案,因为它不需要在Python 3.9.13和Windows 10上安装任何其他内容。您应该在回复中添加如何导入PDFium:import pypdfium2 as pdfium。 - Francesco Pettini
1
已添加,谢谢!我相信它最初是帖子的一部分,但可能在编辑过程中丢失了。(由于API更改,我多次更新了此回复。) - mara004
据我所知,pymupdf也不需要任何外部依赖。技术上说,它比pypdfium2要好一点,所以如果您不介意AGPL,您也可以尝试使用它。 - mara004
安装 pymupdf 通过 fitz 需要我安装 frontend,如果我没记错的话,这还需要其他包。 - Francesco Pettini
8
这应该是被接受的答案,感谢您的工作。不需要任何额外的安装,只需执行pip install pypdfium2即可。 - Tim
显示剩余15条评论

30
Python库pdf2image(在其他答案中使用)实际上并没有做更多的事情,只是启动pdttoppmsubprocess.Popen,因此这里有一个直接执行的简短版本:
PDFTOPPMPATH = r"D:\Documents\software\____PORTABLE\poppler-0.51\bin\pdftoppm.exe"
PDFFILE = "SKM_28718052212190.pdf"

import subprocess
subprocess.Popen('"%s" -png "%s" out' % (PDFTOPPMPATH, PDFFILE))

这里是 Windows 安装链接,用于安装pdftoppm(包含在名为 poppler 的软件包中): http://blog.alivate.com.au/poppler-windows/


4
你好,pdftoppm的Windows安装链接只是一堆压缩文件,你需要做什么才能让它们正常工作?谢谢! - gaurwraith

17

不必在您的操作系统上安装Poppler。这样就可以工作:

pip install Wand

from wand.image import Image

f = "somefile.pdf"
with(Image(filename=f, resolution=120)) as source: 
    for i, image in enumerate(source.sequence):
        newfilename = f.removesuffix(".pdf") + str(i + 1) + '.jpeg'
        Image(image).save(filename=newfilename)

19
需要安装ImageMagick库才能在wand上工作。 - Neeraj Gulia
4
我尝试了这个方法,并需要安装Ghostscript(在Windows 10和Python 3.7下)。 安装后,这个方法完美地运行了。 - jcf
1
f[:-4]是干什么用的?它在其他地方没有被引用。 - Ari
1
@Ari f[:-4] 将从文件名中剪切掉“.pdf”(字符串切片),以创建具有其他扩展名的新文件名。 - Fabian

13

@gaurwraith,为Windows安装Poppler并使用pdftoppm.exe,方法如下:

  1. http://blog.alivate.com.au/poppler-windows/下载包含Poppler最新二进制文件/动态链接库的zip文件,并将其解压到程序文件夹中的一个新文件夹中。例如:"C:\Program Files (x86)\Poppler"。

  2. 将"C:\Program Files (x86)\Poppler\poppler-0.68.0\bin"添加到您的系统环境变量的路径中。

  3. 从命令行安装pdf2image模块 -> "pip install pdf2image"。

  4. 或者,直接使用Python的subprocess模块执行pdftoppm.exe,如用户Basj所解释的那样。

@vishvAs vAsuki,此代码应该通过子进程模块为给定文件夹中一个或多个PDF的所有页面生成所需的JPG。

import os, subprocess

pdf_dir = r"C:\yourPDFfolder"
os.chdir(pdf_dir)

pdftoppm_path = r"C:\Program Files (x86)\Poppler\poppler-0.68.0\bin\pdftoppm.exe"

for pdf_file in os.listdir(pdf_dir):

    if pdf_file.endswith(".pdf"):

        subprocess.Popen('"%s" -jpeg %s out' % (pdftoppm_path, pdf_file))

或者使用pdf2image模块:

import os
from pdf2image import convert_from_path

pdf_dir = r"C:\yourPDFfolder"
os.chdir(pdf_dir)

    for pdf_file in os.listdir(pdf_dir):

        if pdf_file.endswith(".pdf"):

            pages = convert_from_path(pdf_file, 300)
            pdf_file = pdf_file[:-4]

            for page in pages:

               page.save("%s-page%d.jpg" % (pdf_file,pages.index(page)), "JPEG")

这非常有帮助。谢谢! - Sreekiran A R
2
这实际上应该是被采纳的答案。它展示了如何处理已安装的Poppler二进制文件。 - Kunj Mehta

8

GhostScript在Linux系统上的性能比Poppler快得多。

下面是PDF转图像的代码。

def get_image_page(pdf_file, out_file, page_num):
    page = str(page_num + 1)
    command = ["gs", "-q", "-dNOPAUSE", "-dBATCH", "-sDEVICE=png16m", "-r" + str(RESOLUTION), "-dPDFFitPage",
               "-sOutputFile=" + out_file, "-dFirstPage=" + page, "-dLastPage=" + page,
               pdf_file]
    f_null = open(os.devnull, 'w')
    subprocess.call(command, stdout=f_null, stderr=subprocess.STDOUT)

在 macOS 上,可以使用 brew install ghostscript 命令安装 GhostScript。

其他平台的安装信息可以在这里找到。如果系统中没有预先安装 GhostScript。


2
只是想让大家知道,Ghostscript 基于 AGPL 许可证,如果在商业项目中使用可能需要获得许可。更多参考请阅读 https://www.ghostscript.com/license.html。 - Abhishek Jain
你是如何得出Ghostscript比Poppler“快得多”的结论的?我在个人基准测试中无法复现这一观察结果。事实上,我发现Ghostscript稍微慢一些。 - mara004

5

有一个名为pdftojpg的实用工具,可以将pdf转换为img。

你可以在这里找到代码 https://github.com/pankajr141/pdf2jpg

from pdf2jpg import pdf2jpg
inputpath = r"D:\inputdir\pdf1.pdf"
outputpath = r"D:\outputdir"
# To convert single page
result = pdf2jpg.convert_pdf2jpg(inputpath, outputpath, pages="1")
print(result)

# To convert multiple pages
result = pdf2jpg.convert_pdf2jpg(inputpath, outputpath, pages="1,0,3")
print(result)

# to convert all pages
result = pdf2jpg.convert_pdf2jpg(inputpath, outputpath, pages="ALL")
print(result)

4
这个Java程序刚才是不是删除了我整个文件夹里用来操作PDF的Python脚本了...? - Ulf Gjerdingen
一个替代 Apache PDFBox 的绑定是 https://github.com/lebedov/python-pdfbox - mara004

4

每个人都会遇到一个问题,即安装Poppler 。我提供的方法比较繁琐,但效率高。

首先,从这里下载Poppler

然后将其解压缩,在代码部分添加如下内容poppler_path=r'C:\Program Files\poppler-0.68.0\bin' (例如),如下所示:

from pdf2image import convert_from_path
images = convert_from_path("mypdf.pdf", 500,poppler_path=r'C:\Program Files\poppler-0.68.0\bin')
for i, image in enumerate(images):
    fname = 'image'+str(i)+'.png'
    image.save(fname, "PNG")

这将使用i参数为每个页面生成一张图片。它非常有效。谢谢! - Harry

3

下面是一个函数,可以将包含单页或多页PDF文件转换为单个合并的JPEG图像

import os
import tempfile
from pdf2image import convert_from_path
from PIL import Image

def convert_pdf_to_image(file_path, output_path):
    # save temp image files in temp dir, delete them after we are finished
    with tempfile.TemporaryDirectory() as temp_dir:
        # convert pdf to multiple image
        images = convert_from_path(file_path, output_folder=temp_dir)
        # save images to temporary directory
        temp_images = []
        for i in range(len(images)):
            image_path = f'{temp_dir}/{i}.jpg'
            images[i].save(image_path, 'JPEG')
            temp_images.append(image_path)
        # read images into pillow.Image
        imgs = list(map(Image.open, temp_images))
    # find minimum width of images
    min_img_width = min(i.width for i in imgs)
    # find total height of all images
    total_height = 0
    for i, img in enumerate(imgs):
        total_height += imgs[i].height
    # create new image object with width and total height
    merged_image = Image.new(imgs[0].mode, (min_img_width, total_height))
    # paste images together one by one
    y = 0
    for img in imgs:
        merged_image.paste(img, (0, y))
        y += img.height
    # save merged image
    merged_image.save(output_path)
    return output_path

示例用法: -

convert_pdf_to_image("PDF文件路径/1.pdf", "输出路径/output.jpeg")


只是好奇,为什么使用for i, img in enumerate(imgs): total_height += imgs[i].height而不是简单地使用for img in imgs: total_height += img.height - Vladimir Prudnikov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接