Cree un impresionante folleto en PDF en Python con borb

En esta guía, crearemos un hermoso volante en PDF estilizado usando nada más que Python y borb, una biblioteca de Python puro para trabajar con documentos PDF.

El Formato de documento portátil (PDF) no es un formato WYSIWYG (Lo que ves es lo que obtienes). Fue desarrollado para ser independiente de la plataforma, independiente del sistema operativo subyacente y los motores de renderizado.

Para lograr esto, PDF se construyó para poder interactuar a través de algo más parecido a un lenguaje de programación, y se basa en una serie de instrucciones y operaciones para lograr un resultado. De hecho, PDF está basado en un lenguaje de secuencias de comandos: Posdata, que fue el primer lenguaje de descripción de página independiente del dispositivo.

En esta guía, usaremos borracho, una biblioteca de Python dedicada a leer, manipular y generar documentos PDF. Ofrece un modelo de bajo nivel (que le permite acceder a las coordenadas y el diseño exactos si elige usarlos) y un modelo de alto nivel (donde puede delegar los cálculos precisos de márgenes, posiciones, etc. a un administrador de diseño) .

En esta guía, veremos cómo generar un folleto que contenga gráficos personalizados (representados por operadores de PDF).

Instalación de borb {#instalación de borb}

borb puede descargarse desde la fuente en GitHub, o instalarse a través de pip:

1
$ pip install borb

Lo que haremos

A menudo es más fácil hacer un boceto y trabajar para lograrlo, en lugar de construir a ciegas, así que siéntete libre de dibujar un volante en una hoja de papel que tengas por ahí y deja que la creatividad fluya hacia el lienzo.

Estaremos haciendo un flyer como este, para promocionar un supuesto producto perteneciente a una supuesta empresa:

Example Flyer Image

Crear un documento PDF con borb

La creación de un documento PDF en borb normalmente sigue el mismo par de pasos:

  • Crear un Documento vacío
  • Crear una Página vacía y adjuntarla al Documento
  • Establecer un PageLayout en la Página
  • Agregar contenido a PageLayout
  • Persistencia del Documento

Veamos cómo se ve eso en el código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from borb.pdf.document import Document
from borb.pdf.page.page import Page
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout
from borb.pdf.pdf import PDF

def main():
  # Create empty Document
  pdf = Document()

  # Create empty Page
  page = Page()

  # Add Page to Document
  pdf.append_page(page)

  # Create PageLayout
  layout: PageLayout = SingleColumnLayout(page)

  # Future content-rendering-code to be inserted here
  
  # Attempt to store PDF
  with open("output.pdf", "wb") as pdf_file_handle:
      PDF.dumps(pdf_file_handle, pdf)
  
if __name__ == '__main__':
  main()

Crear un folleto en PDF con borb

Ahora que tenemos un lienzo vacío para trabajar, agreguemos el contenido básico. Comenzaremos agregando el título, como "Su empresa":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# New imports
from borb.pdf.canvas.layout.text.paragraph import Paragraph  
from borb.pdf.canvas.color.color import HexColor  
from decimal import Decimal  
  
# Contact information
layout.add(
  Paragraph("Your Company", 
            font_color=HexColor("#6d64e8"), 
            font_size=Decimal(20)
    )
)

El siguiente paso es agregar el código QR y la información de contacto. Para presentar fácilmente este contenido uno al lado del otro, vamos a usar una Tabla.

También necesitamos las coordenadas del código QR (le agregaremos algo especial más adelante). Entonces, comencemos declarando eso primero:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# New imports
from borb.pdf.canvas.layout.image.barcode import Barcode, BarcodeType  
from borb.pdf.canvas.layout.layout_element import LayoutElement
  
# Code to generate a QR code LayoutElement
qr_code: LayoutElement = Barcode(
    data="https://www.borbpdf.com",
    width=Decimal(64),
    height=Decimal(64),
    type=BarcodeType.QR,
)

Ahora podemos construir y agregar nuestra Tabla:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 # New imports
from borb.pdf.canvas.layout.table.flexible_column_width_table import FlexibleColumnWidthTable
  
layout.add(
    FlexibleColumnWidthTable(number_of_columns=2, number_of_rows=1)
    .add(qr_code)
    .add(
        Paragraph(
            """
            500 South Buena Vista Street
            Burbank CA
            91521-0991 USA
            """,
            padding_top=Decimal(12),
            respect_newlines_in_text=True,
            font_color=HexColor("#666666"),
            font_size=Decimal(10),
        )
    )
    .no_borders()
)

Ejecutemos ese código y veamos cómo se ve el PDF generado. Creo que es la mejor manera de ajustar los pequeños detalles de UI/UX.

creando un documento pdf con borb

¡Luciendo bien! El código QR se encuentra justo debajo del nombre de la empresa, contiene la información de contacto correcta y, de hecho, codifica los datos de contacto que proporcionamos.

A continuación, agregaremos una anotación de acceso remota. Eso es solo PDF-talk para "un enlace en el que se puede hacer clic que lo lleva fuera del PDF".

Nos aseguraremos de que todo el código QR sea en realidad un enlace que lleve al lector a nuestro sitio web. De esa forma, si tienen la versión impresa de este PDF, simplemente pueden escanear el código QR. Si tienen la versión digital, pueden hacer clic en el código QR.

Esta es una adición simple, pero hace que la navegación por parte del usuario sea una experiencia más placentera:

1
2
3
page.append_remote_go_to_annotation(
  qr_code.get_bounding_box(), uri="https://www.borbpdf.com"
)

Adición de información del producto

Ahora podemos agregar el siguiente título y subtítulo(s), pertenecientes a un producto para el que estamos creando un folleto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Title
layout.add(
    Paragraph(
        "Productbrochure", font_color=HexColor("#283592"), font_size=Decimal(34)
    )
)

# Subtitle
layout.add(
    Paragraph(
        "September 4th, 2021",
        font_color=HexColor("#e01b84"),
        font_size=Decimal(11),
    )
)

Y de manera similar, agregaremos el título de descripción general del producto y algún texto ficticio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# product overview
layout.add(
    Paragraph(
        "Product Overview", font_color=HexColor("000000"), font_size=Decimal(21)
    )
)

layout.add(
   Paragraph(
        """
        Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. 
        Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean. 
        A small river named Duden flows by their place and supplies it with the necessary regelialia.
        """
    )
)

layout.add(
    Paragraph(
        """
        It is a paradisematic country, in which roasted parts of sentences fly into your mouth. 
        Even the all-powerful Pointing has no control about the blind texts it is an almost unorthographic life. 
        One day however a small line of blind text by the name of Lorem Ipsum decided to leave for the far World of Grammar.
        """,
        margin_bottom=Decimal(12)
    )
)

Nota: Preste atención al último “Párrafo” donde agregamos explícitamente un margen inferior. Eso es solo un pequeño ajuste visual para asegurar que haya un poco más de espacio entre ese “Párrafo” y el siguiente contenido, que será una imagen.

Cuando ejecutamos este código, deberíamos obtener algo como esto:

adding information

Por último, podemos añadir la información del producto. Podríamos tener una Imagen junto con una lista de algunas de las características del producto. De nuevo, podemos usar una Tabla para lograr el aspecto de lado a lado.

A menudo, hay un título encima de la lista de funciones, por lo que vamos a tener una Tabla con 2 columnas (imagen y funciones) y 2 filas (una para el título y otra para las funciones).

Dado que la tabla no se usa como una tabla, sino solo para lograr la apariencia de lado a lado, no agregaremos un borde a la tabla:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# New imports
from borb.pdf.canvas.layout.image.image import Image
from borb.pdf.canvas.layout.table.table import TableCell  
from borb.pdf.canvas.layout.table.fixed_column_width_table import FixedColumnWidthTable
from borb.pdf.canvas.layout.list.unordered_list import UnorderedList
  
# Table with image and key features
layout.add(
    FixedColumnWidthTable(
        number_of_rows=2,
        number_of_columns=2,
        column_widths=[Decimal(0.3), Decimal(0.7)],
    )
    .add(
        TableCell(
            Image(
                  "https://www.att.com/catalog/en/skus/images/apple-iphone%2012-purple-450x350.png",
                width=Decimal(128),
                height=Decimal(128),
            ),
            row_span=2,
        )
    )
    .add(
        Paragraph(
            "Key Features",
            font_color=HexColor("e01b84"),
            font="Helvetica-Bold",
            padding_bottom=Decimal(10),
        )
    )
    .add(
        UnorderedList()
        .add(Paragraph("Nam aliquet ex eget felis lobortis aliquet sit amet ut risus."))
        .add(Paragraph("Maecenas sit amet odio ut erat tincidunt consectetur accumsan ut nunc."))
        .add(Paragraph("Phasellus eget magna et justo malesuada fringilla."))
        .add(Paragraph("Maecenas vitae dui ac nisi aliquam malesuada in consequat sapien."))
    )
    .no_borders()
)

Nuevamente, hemos agregado un padding_bottom en algunas celdas de la Tabla solo para proporcionar algo de espacio adicional. El PDF resultante está casi terminado:

Los pasos finales restantes son agregar la ilustración en la esquina superior derecha y en el pie de página.

Usando el objeto Shape en borb

borb puede representar cualquier Forma en la Página. Shape representa una secuencia arbitraria de puntos (representada como typing.Tuple[Decimal, Decimal]), todos los cuales forman una línea continua. Esto significa que puede ser bastante creativo con las formas que desea crear.

Comenzaremos definiendo un método que represente los triángulos y cuadrados en la esquina superior derecha de la Página:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# New imports
from borb.pdf.canvas.geometry.rectangle import Rectangle
from borb.pdf.canvas.layout.image.shape import Shape
from borb.pdf.page.page_size import PageSize
import typing
import random
  
  
def add_gray_artwork_upper_right_corner(page: Page) -> None:
  """
  This method will add a gray artwork of squares and triangles in the upper right corner
  of the given Page
  """
    grays: typing.List[HexColor] = [
        HexColor("A9A9A9"),
        HexColor("D3D3D3"),
        HexColor("DCDCDC"),
        HexColor("E0E0E0"),
        HexColor("E8E8E8"),
        HexColor("F0F0F0"),
    ]
    ps: typing.Tuple[Decimal, Decimal] = PageSize.A4_PORTRAIT.value
    N: int = 4
    M: Decimal = Decimal(32)
    
    # Draw triangles
    for i in range(0, N):
        x: Decimal = ps[0] - N * M + i * M
        y: Decimal = ps[1] - (i + 1) * M
        rg: HexColor = random.choice(grays)
        Shape(
            points=[(x + M, y), (x + M, y + M), (x, y + M)],
            stroke_color=rg,
            fill_color=rg,
        ).layout(page, Rectangle(x, y, M, M))
        
    # Draw squares
    for i in range(0, N - 1):
        for j in range(0, N - 1):
            if j > i:
                continue
            x: Decimal = ps[0] - (N - 1) * M + i * M
            y: Decimal = ps[1] - (j + 1) * M
            rg: HexColor = random.choice(grays)
            Shape(
                points=[(x, y), (x + M, y), (x + M, y + M), (x, y + M)],
                stroke_color=rg,
                fill_color=rg,
            ).layout(page, Rectangle(x, y, M, M))

Ahora podemos llamar a este método en el método principal y darle a nuestro PDF un poco de paz extra:

Del mismo modo, podríamos agregar algunos gráficos al final de la página:

  • Una línea para separar el pie de página del contenido principal de la página.
  • Un pequeño elemento geométrico para equilibrar el gráfico geométrico en la parte superior de la página.

Escribamos otro método para hacer todo eso:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from borb.pdf.canvas.line_art.line_art_factory import LineArtFactory

def add_colored_artwork_bottom_right_corner(page: Page) -> None:
  """
  This method will add a blue/purple artwork of lines 
  and squares to the bottom right corner
  of the given Page
  """
    ps: typing.Tuple[Decimal, Decimal] = PageSize.A4_PORTRAIT.value
    
    # Square
    Shape(
      points=[
          (ps[0] - 32, 40),
          (ps[0], 40),
          (ps[0], 40 + 32),
          (ps[0] - 32, 40 + 32),
      ],
      stroke_color=HexColor("d53067"),
      fill_color=HexColor("d53067"),
    ).layout(page, Rectangle(ps[0] - 32, 40, 32, 32))
    
    # Square
    Shape(
      points=[
          (ps[0] - 64, 40),
          (ps[0] - 32, 40),
          (ps[0] - 32, 40 + 32),
          (ps[0] - 64, 40 + 32),
      ],
      stroke_color=HexColor("eb3f79"),
      fill_color=HexColor("eb3f79"),
    ).layout(page, Rectangle(ps[0] - 64, 40, 32, 32))
    
    # Triangle
    Shape(
      points=[
          (ps[0] - 96, 40),
          (ps[0] - 64, 40),
          (ps[0] - 64, 40 + 32),
      ],
      stroke_color=HexColor("e01b84"),
      fill_color=HexColor("e01b84"),
    ).layout(page, Rectangle(ps[0] - 96, 40, 32, 32))
        
    # Line
    r: Rectangle = Rectangle(Decimal(0), Decimal(32), ps[0], Decimal(8))
    Shape(
      points=LineArtFactory.rectangle(r),
      stroke_color=HexColor("283592"),
      fill_color=HexColor("283592"),
    ).layout(page, r)

Nuevamente, podemos llamar a este método desde el método principal. La página resultante debería verse así:

Conclusión

En esta guía, hemos echado un vistazo a algunos de los componentes básicos de los documentos PDF utilizando borb. Hemos configurado el relleno y el margen, así como el tamaño y el color de la fuente. También hemos generado gráficos utilizando el objeto Shape y un código QR en el que se puede hacer clic.

Con estos componentes básicos, hemos creado un folleto para un supuesto producto de una empresa imaginaria, automatizando el proceso de creación de documentos PDF interactivos.