Объедините существующий PDF-файл с новым PDF-файлом ReportLab с помощью потоковых файлов

У меня есть reportlab SimpleDocTemplate, и я возвращаю его как динамический PDF-файл. Я создаю его контент на основе некоторых метаданных модели Django. Вот моя настройка шаблона:

buff = StringIO()
doc = SimpleDocTemplate(buff, pagesize=letter,
                        rightMargin=72,leftMargin=72,
                        topMargin=72,bottomMargin=18)
Story = []

Я могу легко добавить текстовые метаданные из модели Entry в список Story, который будет построен позже:

    ptext = '<font size=20>%s</font>' % entry.title.title()
    paragraph = Paragraph(ptext, custom_styles["Custom"])
    Story.append(paragraph)

А затем сгенерируйте PDF-файл, который будет возвращен в ответе, вызвав build на SimpleDocTemplate:

doc.build(Story, onFirstPage=entry_page_template, onLaterPages=entry_page_template)

pdf = buff.getvalue()
resp = HttpResponse(mimetype='application/x-download')    
resp['Content-Disposition'] = 'attachment;filename=logbook.pdf'
resp.write(pdf)
return resp

Одно из полей метаданных в модели - это прикрепленный файл. Когда эти прикрепленные файлы представляют собой PDF-файлы, я хотел бы объединить их в историю, которую я создаю; IE означает PDF-файл "текучего" типа reportlab.

Я пытаюсь сделать это с помощью pdfrw, но безуспешно. В идеале я бы хотел просто позвонить:

from pdfrw import PdfReader
pdf = pPdfReader(entry.document.file.path)
Story.append(pdf)

и добавьте PDF-файл в существующий список историй, чтобы включить его в создание окончательного документа, как указано выше.

У кого-нибудь есть идеи? Я пробовал что-то подобное, используя pagexobj для создания pdf, пытаясь следовать этому примеру:

http://code.google.com/p/pdfrw/source/browse/trunk/examples/rl1/subset.py

from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl

pdf = pagexobj(PdfReader(entry.document.file.path))

Но и не повезло. Может ли кто-нибудь объяснить мне, как лучше всего объединить существующий файл PDF в текучую отчетную лабораторию? Я плохо разбираюсь в этом материале и уже несколько дней бью головой о создании PDF-файлов. :) Любое направление приветствуется!


person kyleturner    schedule 13.11.2012    source источник
comment
Думаю, это можно сделать с помощью платной версии ReportLab.   -  person G Gordon Worley III    schedule 14.11.2012
comment
Ух, я, к сожалению, не думаю, что платная версия ReportLab мне подходит. :( У кого-нибудь есть альтернативы?   -  person kyleturner    schedule 16.11.2012


Ответы (4)


У меня как раз была аналогичная задача в проекте. Я использовал reportlab (версия с открытым исходным кодом) для создания файлов pdf и pyPDF для облегчения слияния. Мои требования были немного разными в том смысле, что мне требовалась только одна страница из каждого приложения, но я уверен, что этого, вероятно, достаточно, чтобы вы могли получить общее представление.

from pyPdf import PdfFileReader, PdfFileWriter

def create_merged_pdf(user):
    basepath = settings.MEDIA_ROOT + "/"
    # following block calls the function that uses reportlab to generate a pdf
    coversheet_path = basepath + "%s_%s_cover_%s.pdf" %(user.first_name, user.last_name, datetime.now().strftime("%f"))
    create_cover_sheet(coversheet_path, user, user.performancereview_set.all())

    # now user the cover sheet and all of the performance reviews to create a merged pdf
    merged_path = basepath + "%s_%s_merged_%s.pdf" %(user.first_name, user.last_name, datetime.now().strftime("%f"))

    # for merged file result
    output = PdfFileWriter()

    # for each pdf file to add, open in a PdfFileReader object and add page to output
    cover_pdf = PdfFileReader(file( coversheet_path, "rb"))
    output.addPage(cover_pdf.getPage(0))

    # iterate through attached files and merge.  I only needed the first page, YMMV
    for review in user.performancereview_set.all():
        review_pdf = PdfFileReader(file(review.pdf_file.file.name, "rb"))
        output.addPage(review_pdf.getPage(0)) # only first page of attachment

    # write out the merged file
    outputStream = file(merged_path, "wb")
    output.write(outputStream)
    outputStream.close()
person RyanBrady    schedule 06.02.2013

Я использовал следующий класс для решения своей проблемы. Он вставляет PDF-файлы как векторные изображения PDF. Он отлично работает, потому что мне нужно было оглавление. Текучий объект позволил встроенным функциям оглавления работать как шарм.

Есть ли текучая библиотека matplotlib для ReportLab?

Примечание. Если у вас есть несколько страниц в файле, вам нужно немного изменить класс. Образец класса предназначен только для чтения первой страницы PDF-файла.

person Greg Svitak    schedule 27.01.2014

Я знаю, что вопрос немного устарел, но я хотел бы предложить новое решение с использованием последней версии PyPDF2.

Теперь у вас есть доступ к PdfFileMerger, который может делать именно то, что вы хотите, добавлять PDF-файлы в существующий файл. Вы даже можете объединить их в разных позициях и выбрать подмножество или все страницы!

Официальная документация находится здесь: https://pythonhosted.org/PyPDF2/PdfFileMerger.html

Пример из кода в вашем вопросе:

import tempfile
import PyPDF2
from django.core.files import File

# Using a temporary file rather than a buffer in memory is probably better
temp_base = tempfile.TemporaryFile()
temp_final = tempfile.TemporaryFile()

# Create document, add what you want to the story, then build
doc = SimpleDocTemplate(temp_base, pagesize=letter, ...)
...
doc.build(...)

# Now, this is the fancy part. Create merger, add extra pages and save
merger = PyPDF2.PdfFileMerger()
merger.append(temp_base)
# Add any extra document, you can choose a subset of pages and add bookmarks
merger.append(entry.document.file, bookmark='Attachment')
merger.write(temp_final)

# Write the final file in the HTTP response
django_file = File(temp_final)
resp = HttpResponse(django_file, content_type='application/pdf')
resp['Content-Disposition'] = 'attachment;filename=logbook.pdf'
if django_file.size is not None:
    resp['Content-Length'] = django_file.size
return resp
person Uyric    schedule 27.08.2020

Используйте этот нестандартный текучий:

class PDF_Flowable(Flowable):
#----------------------------------------------------------------------
def __init__(self,P,page_no):
    Flowable.__init__(self)
    self.P = P
    self.page_no = page_no
#----------------------------------------------------------------------
def draw(self):
    """
    draw the line
    """
    canv = self.canv
    pages = self.P
    page_no = self.page_no
    canv.translate(x, y)  
    canv.doForm(makerl(canv, pages[page_no]))
    canv.restoreState()

а затем после открытия существующего pdf, т.е.

    pages = PdfReader(BASE_DIR + "/out3.pdf").pages
    pages = [pagexobj(x) for x in pages]
    for i in range(0, len(pages)):
        F = PDF_Flowable(pages,i)
        elements.append(F)
        elements.append(PageBreak())

используйте этот код, чтобы добавить этот настраиваемый поток в elements [].

person Paritosh Mishra    schedule 22.10.2019