Статьи

Reportlab: смешивание фиксированного содержимого и потоковых таблиц

Недавно мне понадобилась возможность использовать текучие элементы Reportlab, но размещать их в фиксированных местах. Некоторые из вас, вероятно, задаются вопросом, почему я хотел бы сделать это. Хорошая вещь о текучих средах, таких как Параграф, состоит в том, что они легко стилизованы. Если бы я мог выделить что-то жирным шрифтом или центрировать и поместить это в фиксированное место, то это было бы здорово! Потребовалось много Googling, проб и ошибок, но я наконец-то собрал приличный шаблон, который я мог использовать для рассылок. В этой статье я покажу вам, как это сделать тоже.

Начиная

 

Вам нужно убедиться, что у вас есть Reportlab, иначе у вас ничего не получится. Вы можете пойти сюда, чтобы взять его. Пока вы ждете его загрузки, вы можете продолжить чтение этой статьи или заняться чем-нибудь еще продуктивным. Вы готовы сейчас? Тогда давайте запустим это шоу в дорогу!

Теперь нам просто нужно привести пример. К счастью, я работал над чем-то на своей работе, и мне удалось вписаться в следующее глупое и неполное письмо. Внимательно изучите код, потому что вы никогда не знаете, когда будет тест

 

from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm, inch
from reportlab.pdfgen import canvas
from reportlab.platypus import Image, Paragraph, Table
 
 
########################################################################
class LetterMaker(object):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, pdf_file, org, seconds):
        self.c = canvas.Canvas(pdf_file, pagesize=letter)
        self.styles = getSampleStyleSheet()
        self.width, self.height = letter
        self.organization = org
        self.seconds  = seconds
 
 
    #----------------------------------------------------------------------
    def createDocument(self):
        """"""
        voffset = 65
 
        # create return address
        address = """<font size="9">
        Jack Spratt<br/>
        222 Ioway Blvd, Suite 100<br/>
        Galls, TX 75081-4016</font>
        """
        p = Paragraph(address, self.styles["Normal"])        
 
        # add a logo and size it
        logo = Image("snakehead.jpg")
        logo.drawHeight = 2*inch
        logo.drawWidth = 2*inch
##        logo.wrapOn(self.c, self.width, self.height)
##        logo.drawOn(self.c, *self.coord(140, 60, mm))
##        
 
        data = [[p, logo]]
        table = Table(data, colWidths=4*inch)
        table.setStyle([("VALIGN", (0,0), (0,0), "TOP")])
        table.wrapOn(self.c, self.width, self.height)
        table.drawOn(self.c, *self.coord(18, 60, mm))
 
        # insert body of letter
        ptext = "Dear Sir or Madam:"
        self.createParagraph(ptext, 20, voffset+35)
 
        ptext = """
        The document you are holding is a set of requirements for your next mission, should you
        choose to accept it. In any event, this document will self-destruct <b>%s</b> seconds after you
        read it. Yes, <b>%s</b> can tell when you're done...usually.
        """ % (self.seconds, self.organization)
        p = Paragraph(ptext, self.styles["Normal"])
        p.wrapOn(self.c, self.width-70, self.height)
        p.drawOn(self.c, *self.coord(20, voffset+48, mm))
 
    #----------------------------------------------------------------------
    def coord(self, x, y, unit=1):
        """
        # http://stackoverflow.com/questions/4726011/wrap-text-in-a-table-reportlab
        Helper class to help position flowables in Canvas objects
        """
        x, y = x * unit, self.height -  y * unit
        return x, y    
 
    #----------------------------------------------------------------------
    def createParagraph(self, ptext, x, y, style=None):
        """"""
        if not style:
            style = self.styles["Normal"]
        p = Paragraph(ptext, style=style)
        p.wrapOn(self.c, self.width, self.height)
        p.drawOn(self.c, *self.coord(x, y, mm))
 
    #----------------------------------------------------------------------
    def savePDF(self):
        """"""
        self.c.save()   
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    doc = LetterMaker("example.pdf", "The MVP", 10)
    doc.createDocument()
    doc.savePDF()

Теперь вы видели код, поэтому мы потратим немного времени на то, чтобы понять, как он работает. Сначала мы создаем объект Canvas, который мы можем использовать без нашего класса LetterMaker. Мы также создаем стиль и устанавливаем несколько других переменных класса. В методе createDocument мы создаем Абзац (адрес), используя некоторые HTML-подобные теги для управления поведением шрифта и разрывом строки. Затем мы создаем логотип и изменяем его размеры, прежде чем помещать оба элемента в объект таблицы отчета . Вы заметите, что я оставил пару закомментированных строк, которые показывают, как разместить логотип без таблицы. Мы используем метод координат, чтобы помочь позиционировать поток. Я нашел его в StackOverflow и подумал, что это очень удобно.

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

Завершение

 

Я надеюсь, что этот код поможет вам в ваших усилиях по созданию PDF. Я должен признать, что я публикую это здесь столько же для моей собственной будущей выгоды, сколько и для вас самого. Мне немного грустно, что мне пришлось так сильно от него отказаться, но моей организации не очень понравилось бы, если бы я опубликовал оригинал. Несмотря на это, теперь у вас есть инструменты для создания довольно красивых PDF-документов с помощью Python. Теперь вам просто нужно выйти и сделать это!