Создать PDF из списка изображений
Есть ли практический способ создать PDF из списка файлов изображений, используя Python?
В Perl я знаю этот модуль. С его помощью я могу создать PDF всего за 3 строки:
use PDF::FromImage;
...
my $pdf = PDF::FromImage->new;
$pdf->load_images(@allPagesDir);
$pdf->write_file($bookName . '.pdf');
Мне нужно сделать что-то очень похожее на это, но в Python. Я знаю модуль pyPdf, но мне хотелось бы чего-то простого.
@Редактировать
Если вы пришли через Google, вот код:
from fpdf import FPDF
from PIL import Image
def makePdf(pdfFileName, listPages, dir = ''):
if (dir):
dir += "/"
cover = Image.open(dir + str(listPages[0]) + ".jpg")
width, height = cover.size
pdf = FPDF(unit = "pt", format = [width, height])
for page in listPages:
pdf.add_page()
pdf.image(dir + str(page) + ".jpg", 0, 0)
pdf.output(dir + pdfFileName + ".pdf", "F")
22 ответа
Установите FPDF для Python:
pip install fpdf
Теперь вы можете использовать ту же логику:
from fpdf import FPDF
pdf = FPDF()
# imagelist is the list with all image filenames
for image in imagelist:
pdf.add_page()
pdf.image(image,x,y,w,h)
pdf.output("yourfile.pdf", "F")
Вы можете найти больше информации на странице учебника или официальной документации.
Лучший способ конвертировать несколько изображений в PDF, который я пробовал, это использовать PIL
чисто. Это довольно просто, но мощно:
from PIL import Image
im1 = Image.open("/Users/apple/Desktop/bbd.jpg")
im2 = Image.open("/Users/apple/Desktop/bbd1.jpg")
im3 = Image.open("/Users/apple/Desktop/bbd2.jpg")
im_list = [im2,im3]
pdf1_filename = "/Users/apple/Desktop/bbd1.pdf"
im1.save(pdf1_filename, "PDF" ,resolution=100.0, save_all=True, append_images=im_list)
Просто установите save_all
в True
а также append_images
в список изображений, которые вы хотите добавить.
Вы можете столкнуться с AttributeError: 'JpegImageFile' object has no attribute 'encoderinfo'
, Решение здесь - ошибка при сохранении нескольких JPEG-файлов в виде многостраничного PDF
Примечание: установите новейшие PIL
Чтобы убедиться save_all
Аргумент доступен для PDF.
Если вы используете Python 3, вы можете использовать модуль python img2pdf
установить его с помощью pip3 install img2pdf
а затем вы можете использовать его в сценарии с помощью import img2pdf
образец кода
import os
import img2pdf
with open("output.pdf", "wb") as f:
f.write(img2pdf.convert([i for i in os.listdir('path/to/imageDir') if i.endswith(".jpg")]))
Если ваши изображения представляют собой графики, которые вы создали с помощью matplotlib, вы можете использовать matplotlib.backends.backend_pdf.PdfPages
(См. Документацию).
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
# generate a list with dummy plots
figs = []
for i in [-1, 1]:
fig = plt.figure()
plt.plot([1, 2, 3], [i*1, i*2, i*3])
figs.append(fig)
# gerate a multipage pdf:
with PdfPages('multipage_pdf.pdf') as pdf:
for fig in figs:
pdf.savefig(fig)
plt.close()
pgmagick это GraphicsMagick(Magick++)
привязка для Python.
Это оболочка Python для ImageMagick (или GraphicsMagick).
import os
from os import listdir
from os.path import isfile, join
from pgmagick import Image
mypath = "\Images" # path to your Image directory
for each_file in listdir(mypath):
if isfile(join(mypath,each_file)):
image_path = os.path.join(mypath,each_file)
pdf_path = os.path.join(mypath,each_file.rsplit('.', 1)[0]+'.pdf')
img = Image(image_path)
img.write(pdf_path)
Sample input Image:
PDF looks like this:
Инструкция по установке pgmagick для windows:
1) Загрузите предварительно скомпилированные двоичные пакеты из неофициальных двоичных файлов Windows для пакетов расширения Python (как указано на веб-странице pgmagick) и установите их.
Примечание. Попробуйте загрузить правильную версию, соответствующую вашей версии Python, установленной на вашем компьютере, и 32-битной или 64-битной.
Вы можете проверить, есть ли у вас 32-битный или 64-битный питон, просто набрав python на своем терминале и нажав Enter.
D:\>python
ActivePython 2.7.2.5 (ActiveState Software Inc.) based on
Python 2.7.2 (default, Jun 24 2011, 12:21:10) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
Так оно и есть python version 2.7
и его 32 bit (Intel)] on win32
так что вы должны загрузить и установить pgmagick‑0.5.8.win32‑py2.7.exe
,
Это следующие доступные пакеты расширения Python для pgmagick:
- pgmagick-0.5.8.win-amd64-py2.6.exe
- pgmagick-0.5.8.win-amd64-py2.7.exe
- pgmagick-0.5.8.win-amd64-py3.2.exe
- pgmagick-0.5.8.win32-py2.6.exe
- pgmagick-0.5.8.win32-py2.7.exe
- pgmagick-0.5.8.win32-py3.2.exe
2) Затем вы можете следовать инструкциям по установке здесь.
pip install pgmagick
А затем попробуйте импортировать его.
>>> from pgmagick import gminfo
>>> gminfo.version
'1.3.x'
>>> gminfo.library
'GraphicsMagick'
>>>
Первый pip install pillow
в интерфейсе командной строки. Изображения могут быть в формате jpg или png. если у вас есть 2 или более изображений и вы хотите сделать их в 1 pdf-файле.
Код:
from PIL import Image
image1 = Image.open(r'locationOfImage1\\Image1.png')
image2 = Image.open(r'locationOfImage2\\Image2.png')
image3 = Image.open(r'locationOfImage3\\Image3.png')
im1 = image1.convert('RGB')
im2 = image2.convert('RGB')
im3 = image3.convert('RGB')
imagelist = [im2,im3]
im1.save(r'locationWherePDFWillBeSaved\\CombinedPDF.pdf',save_all=True, append_images=imagelist)
**** Convert images files to pdf file.****
from os import listdir
from fpdf import FPDF
path = "/home/bunny/images/" # get the path of images
imagelist = listdir(path) # get list of all images
pdf = FPDF('P','mm','A4') # create an A4-size pdf document
x,y,w,h = 0,0,200,250
for image in imagelist:
pdf.add_page()
pdf.image(path+image,x,y,w,h)
pdf.output("images.pdf","F")
Как насчет этого??
from fpdf import FPDF
from PIL import Image
import glob
import os
# set here
image_directory = '/path/to/imageDir'
extensions = ('*.jpg','*.png','*.gif')
# set 0 if you want to fit pdf to image
# unit : pt
margin = 10
imagelist=[]
for ext in extensions:
imagelist.extend(glob.glob(os.path.join(image_directory,ext)))
for imagePath in imagelist:
cover = Image.open(imagePath)
width, height = cover.size
pdf = FPDF(unit="pt", format=[width + 2*margin, height + 2*margin])
pdf.add_page()
pdf.image(imagePath, margin, margin)
destination = os.path.splitext(imagePath)[0]
pdf.output(destination + ".pdf", "F")
Некоторые изменения, чтобы сделать PDF из каталога, где файлы
Я взял код и внес небольшое изменение, чтобы сделать его пригодным для использования.
from fpdf import FPDF
from PIL import Image
import os # I added this and the code at the end
def makePdf(pdfFileName, listPages, dir=''):
if (dir):
dir += "/"
cover = Image.open(dir + str(listPages[0]))
width, height = cover.size
pdf = FPDF(unit="pt", format=[width, height])
for page in listPages:
pdf.add_page()
pdf.image(dir + str(page), 0, 0)
pdf.output(dir + pdfFileName + ".pdf", "F")
# this is what I added
x = [f for f in os.listdir() if f.endswith(".jpg")]
y = len(x)
makePdf("file", x)
В моем случае нужно было конвертировать более 100 изображений в разных форматах (с альфа-каналом и без, с разными расширениями).
Перепробовал все рецепты из ответов на этот вопрос.
Pil => нельзя комбинировать с альфа-каналом и без него (необходимо конвертировать изображения)
fpdf => стек на большом количестве изображений
печать из html в gotenberg => очень долгая обработка
И моей последней попыткой был reportlab. И работает красиво и быстро. (Но иногда при большом вводе создается поврежденный PDF-файл). Вот мой код
from PyPDF2 import PdfMerger
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.platypus import Image, PageBreak, Paragraph, SimpleDocTemplate
async def save_report_lab_story_to_pdf(file_name, story):
doc = SimpleDocTemplate(
file_name,
pagesize=letter,
rightMargin=32,
leftMargin=32,
topMargin=18,
bottomMargin=18,
)
doc.build(story)
async def reportlab_pdf_builder(data, images):
story = []
width = 7.5 * inch
height = 9 * inch
chunk_size = 5 * 70
pdf_chunks = []
files_to_clean_up = []
for trip in data['trips']:
for invoice in trip['invoices']:
for page in invoice['pages']:
if trip['trip_label']:
story.append(Paragraph(
f"TRIP: {trip['trip_label']} {trip['trip_begin']} - {trip['trip_end']}"
))
else:
story.append(Paragraph("No trip"))
story.append(Paragraph(
f"""Document number: {invoice['invoice_number']}
Document date: {invoice['document_date']}
Amount: {invoice['invoice_trip_value']} {invoice['currency_code']}
"""
))
story.append(Paragraph(" "))
img_name = page['filename']
img_bytes = images[page['path']]
tmp_img_filename = f'/tmp/{uuid.uuid4()}.{img_name}'
with open(tmp_img_filename, "wb") as tmp_img:
tmp_img.write(img_bytes)
im = Image(tmp_img_filename, width, height)
story.append(im)
story.append(PageBreak())
files_to_clean_up.append(tmp_img_filename)
# 5 objects per page in story
if len(story) >= chunk_size:
file_name = f"/tmp/{uuid.uuid4()}_{data['tail_number']}.pdf"
await save_report_lab_story_to_pdf(file_name, story)
story = []
pdf_chunks.append(file_name)
merger = PdfMerger()
for pdf in pdf_chunks:
merger.append(pdf)
res_file_name = f"/tmp/{uuid.uuid4()}_{data['tail_number']}.pdf"
merger.write(res_file_name)
merger.close()
Вот ответ ilovecomputer, упакованный в функцию и непосредственно используемый. Это также позволяет уменьшить размеры изображений и хорошо работает.
Код предполагает наличие папки внутри input_dir, которая содержит изображения, упорядоченные в алфавитном порядке по их имени, и выводит PDF-файл с именем папки и, возможно, строкой префикса для имени.
import os
from PIL import Image
def convert_images_to_pdf(export_dir, input_dir, folder, prefix='', quality=20):
current_dir = os.path.join(input_dir, folder)
image_files = os.listdir(current_dir)
im_list = [Image.open(os.path.join(current_dir, image_file)) for image_file in image_files]
pdf_filename = os.path.join(export_dir, prefix + folder + '.pdf')
im_list[0].save(pdf_filename, "PDF", quality=quality, optimize=True, save_all=True, append_images=im_list[1:])
export_dir = r"D:\pdfs"
input_dir = r"D:\image_folders"
folders = os.listdir(input_dir)
[convert_images_to_pdf(export_dir, input_dir, folder, prefix='') for folder in folders];
Это не совсем новый ответ, но - при использовании img2pdf размер страницы оказался неправильным. Итак, вот что я сделал, чтобы использовать размер изображения, надеюсь, он кого-то найдет:
при условии, 1) все изображения одинакового размера, 2) размещение одного изображения на странице, 3) изображение заполняет всю страницу
from PIL import Image
import img2pdf
with open( 'output.pdf', 'wb' ) as f:
img = Image.open( '1.jpg' )
my_layout_fun = img2pdf.get_layout_fun(
pagesize = ( img2pdf.px_to_pt( img.width, 96 ), img2pdf.px_to_pt( img.height, 96 ) ), # this is where image size is used; 96 is dpi value
fit = img2pdf.FitMode.into # I didn't have to specify this, but just in case...
)
f.write( img2pdf.convert( [ '1.jpg', '2.jpg', '3.jpg' ], layout_fun = my_layout_fun ))
Готовое к использованию решение, которое конвертирует все PNG в текущей папке в PDF, вдохновленное ответом @ilovecomputer:
import glob, PIL.Image
L = [PIL.Image.open(f) for f in glob.glob('*.png')]
L[0].save('out.pdf', "PDF" ,resolution=100.0, save_all=True, append_images=L[1:])
Больше ничего, кроме PIL, не нужно:)
У меня была та же проблема, поэтому я создал функцию Python, чтобы объединить несколько изображений в один PDF-файл. Код (доступен на моей странице GitHub, использует reportlab
, и основан на ответах по следующим ссылкам:
- Создать PDF из списка изображений
- Объединение нескольких PNG в один PDF-файл в Python
- PNG изображения в один PDF в Python
- Как я могу преобразовать все файлы JPG в папке в PDF-файлы и объединить их?
- https://www.blog.pythonlibrary.org/2012/01/07/reportlab-converting-hundreds-of-images-into-pdfs/
Вот пример того, как объединить изображения в PDF:
У нас есть папка "D:\pictures" с изображениями типов png и jpg, и мы хотим создать из них файл pdf_with_pictures.pdf и сохранить его в той же папке.
outputPdfName = "pdf_with_pictures"
pathToSavePdfTo = "D:\\pictures"
pathToPictures = "D:\\pictures"
splitType = "none"
numberOfEntitiesInOnePdf = 1
listWithImagesExtensions = ["png", "jpg"]
picturesAreInRootFolder = True
nameOfPart = "volume"
unite_pictures_into_pdf(outputPdfName, pathToSavePdfTo, pathToPictures, splitType, numberOfEntitiesInOnePdf, listWithImagesExtensions, picturesAreInRootFolder, nameOfPart)
Я знаю, что на вопрос дан ответ, но еще один способ решить эту проблему - использовать библиотеку подушек. Чтобы преобразовать весь каталог изображений:
from PIL import Image
import os
def makePdf(imageDir, SaveToDir):
'''
imageDir: Directory of your images
SaveToDir: Location Directory for your pdfs
'''
os.chdir(imageDir)
try:
for j in os.listdir(os.getcwd()):
os.chdir(imageDir)
fname, fext = os.path.splitext(j)
newfilename = fname + ".pdf"
im = Image.open(fname + fext)
if im.mode == "RGBA":
im = im.convert("RGB")
os.chdir(SaveToDir)
if not os.path.exists(newfilename):
im.save(newfilename, "PDF", resolution=100.0)
except Exception as e:
print(e)
imageDir = r'____' # your imagedirectory path
SaveToDir = r'____' # diretory in which you want to save the pdfs
makePdf(imageDir, SaveToDir)
Для использования его на одном изображении:
From PIL import Image
import os
filename = r"/Desktop/document/dog.png"
im = Image.open(filename)
if im.mode == "RGBA":
im = im.convert("RGB")
new_filename = r"/Desktop/document/dog.pdf"
if not os.path.exists(new_filename):
im.save(new_filename,"PDF",resolution=100.0)
Что сработало для меня в python 3.7 и img2pdf версии 0.4.0, так это использование чего-то похожего на код, данного Сайедом Шамихом Шаббиром, но изменение текущего рабочего каталога с использованием ОС, как предложил Стю в своем комментарии к решению Сайеда.
import os
import img2pdf
path = './path/to/folder'
os.chdir(path)
images = [i for i in os.listdir(os.getcwd()) if i.endswith(".jpg")]
for image in images:
with open(image[:-4] + ".pdf", "wb") as f:
f.write(img2pdf.convert(image))
Стоит упомянуть, что это решение выше сохраняет каждый файл.jpg отдельно в одном единственном PDF-файле. Если вы хотите, чтобы все ваши файлы.jpg были собраны в один.pdf, вы можете сделать:
import os
import img2pdf
path = './path/to/folder'
os.chdir(path)
images = [i for i in os.listdir(os.getcwd()) if i.endswith(".jpg")]
with open("output.pdf", "wb") as f:
f.write(img2pdf.convert(images))
Я знаю, что это старый вопрос. В моем случае я использую Reportlab.
Размеры листа выражаются в точках, а не в пикселях, с точкой, равной 1/72 дюйма. Лист А4 состоит из 595,2 пункта в ширину и 841,8 пункта в высоту. Начало координат положения (0, 0) находится в нижнем левом углу. При создании экземпляра canvas.Canvas вы можете указать размер листов с помощью параметра page size, передав кортеж, первый элемент которого представляет ширину в точках, а второй - высоту. Метод c.showPage () сообщает ReportLab, что он уже закончил работу с текущим листом и переходит к следующему. Хотя второй лист еще не обработан (и не будет отображаться в документе, пока ничего не нарисовано), рекомендуется не забыть сделать это перед вызовом c.save (). Для вставки изображений в PDF-документ ReportLab использует библиотеку Pillow.Метод drawImage () принимает в качестве аргумента путь к изображению (поддерживает несколько форматов, таких как PNG, JPEG и GIF) и позицию (x, y) в том, который вы хотите вставить. Изображение можно уменьшить или увеличить, указав его размеры с помощью аргументов ширины и высоты.
Следующий код предоставляет имя файла pdf, список с файлами png, координаты для вставки изображений, а также размер для размещения на страницах с книжной ориентацией.
def pntopd(file, figs, x, y, wi, he):
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, letter, landscape, portrait
w, h = letter
c = canvas.Canvas(str(file), pagesize=portrait(letter))
for png in figs:
c.drawImage(png, x, h - y, width=wi, height=he)
c.showPage()
c.save()
from datetime import date
from pathlib import Path
ruta = "C:/SQLite"
today = date.today()
dat_dir = Path(ruta)
tit = today.strftime("%y%m%d") + '_ParameterAudit'
pdf_file = tit + ".pdf"
pdf_path = dat_dir / pdf_file
pnglist = ['C0.png', 'C4387.png', 'C9712.png', 'C9685.png', 'C4364.png']
pntopd(pdf_path, pnglist, 50, 550, 500, 500)
Лучший ответ уже существует!!! Я просто немного улучшаю ответ. Вот код:
from fpdf import FPDF
pdf = FPDF()
# imagelist is the list with all image filenames you can create using os module by iterating all the files in a folder or by specifying their name
for image in imagelist:
pdf.add_page()
pdf.image(image,x=0,y=0,w=210,h=297) # for A4 size because some people said that every other page is blank
pdf.output("yourfile.pdf", "F")
Для этого вам необходимо установить FPDF.
pip install FPDF
Вы можете использовать pdfme . Это самая мощная библиотека на Python для создания PDF-документов.
from pdfme import build_pdf
...
pdf_image_list = [{"image": img} for img in images]
with open('images.pdf', 'wb') as f:
build_pdf({"sections": [{"content": pdf_image_list}]})
Проверьте документы здесь
Этот ответ казался законным, но я не мог заставить его работать из-за ошибки «требуется байтовый объект, а не str». После прочтения документации img2pdf у меня сработало следующее:
import img2pdf
import os
dirname = "/path/to/images"
imgs = []
for fname in os.listdir(dirname):
if not fname.endswith(".jpg") and not fname.endswith(".png"):
continue
path = os.path.join(dirname, fname)
if os.path.isdir(path):
continue
imgs.append(path)
with open("name.pdf","wb") as f:
f.write(img2pdf.convert(imgs))
Если ваши изображения находятся в альбомном режиме, вы можете сделать это так.
from fpdf import FPDF
import os, sys, glob
from tqdm import tqdm
pdf = FPDF('L', 'mm', 'A4')
im_width = 1920
im_height = 1080
aspect_ratio = im_height/im_width
page_width = 297
# page_height = aspect_ratio * page_width
page_height = 200
left_margin = 0
right_margin = 0
# imagelist is the list with all image filenames
for image in tqdm(sorted(glob.glob('test_images/*.png'))):
pdf.add_page()
pdf.image(image, left_margin, right_margin, page_width, page_height)
pdf.output("mypdf.pdf", "F")
print('Conversion completed!')
Здесь page_width и page_height - это размер бумаги A4, где в альбомной ориентации ширина будет 297 мм, а высота - 210 мм; но здесь я отрегулировал высоту согласно моему изображению. ИЛИ вы можете использовать либо сохранение соотношения сторон, как я прокомментировал выше, для правильного масштабирования как ширины, так и высоты изображения.
Добавляя к ответу @ilovecomputer, если вы хотите сохранить pdf в памяти, а не на диске, вы можете сделать это:
import io
from pdf2image import convert_from_bytes
pil_images = convert_from_bytes(original_pdf_bytes, dpi=100) # (OPTIONAL) do this if you're converting a normal pdf to images first and then back to only image pdf
pdf_output = io.BytesIO()
pil_images[0].save(pdf_output, "PDF", resolution=100.0, save_all=True, append_images=pil_images[1:])
pdf_bytes = pdf_output.getvalue()