Skip to content

Tutorial

Documentación completa de los métodos: Documentación de la API de fpdf.FPDF

Tutorial 1 - Ejemplo básico

Empecemos con el ejemplo clásico:

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("helvetica", "B", 16)
pdf.cell(40, 10, "Hello World!")
pdf.output("tuto1.pdf")

PDF resultante

Luego de incluir la biblioteca, creamos un objeto FPDF. El constructor FPDF es usado aquí con los valores predeterminados: Las páginas están en A4 vertical y la unidad de medida es milímetros. Podría haberse especificado explícitamente con:

pdf = FPDF(orientation="P", unit="mm", format="A4")

Es posible configurar el PDF en modo horizontal (L) o usar otros formatos de página como carta (Letter) y oficio (Legal) y unidades de medida (pt, cm, in).

Por el momento no hay una página, entonces tenemos que agregar una con add_page. El origen es la esquina superior izquierda y la posición actual está ubicada por defecto a 1 cm de los bordes; los márgenes pueden ser cambiados con set_margins.

Antes de que podamos imprimir texto, es obligatorio seleccionar una fuente con set_font, de lo contrario, el documento sería inválido. Elegimos helvetica en negrita 16:

pdf.set_font('helvetica', 'B', 16)

Podríamos haber especificado cursiva con I, subrayado con U o fuente regular con una cadena de texto vacía (o cualquier combinación). Nota que el tamaño de la fuente es dado en puntos, no en milímetros (u otra unidad de medida del usuario); ésta es la única excepción. Las otras fuentes estándar son Times, Courier, Symbol y ZapfDingbats.

Podemos ahora imprimir una celda con cell. Una celda es un área rectangular, posiblemente enmarcada, que contiene algún texto. Se imprime en la posición actual. Especificamos sus dimensiones, su texto (centrado o alineado), si los bordes deberían ser dibujados, y a donde la posición actual se mueve después (a la derecha, abajo o al principio de la próxima linea). Para agregar un marco, haremos esto:

pdf.cell(40, 10, '¡Hola mundo!', 1)

Para agregar una nueva celda próxima a él, con texto centrado y luego ir a la siguiente línea, haríamos:

pdf.cell(60, 10, 'Hecho con FPDF.', new_x="LMARGIN", new_y="NEXT", align='C')

Nota: el salto de línea puede hacerse también con ln. Este método permite especificar adicionalmente la altura del salto.

Finalmente, el documento es cerrado y guardado en la ruta provista usando output. Sin ningún parámetro provisto, output() devuelve el búfer bytearray del PDF.

Tutorial 2 - Encabezado, pie de página, salto de página e imagen

Aquí hay un ejemplo de dos páginas con encabezado, pie de página y logo:

from fpdf import FPDF


class PDF(FPDF):
    def header(self):
        # Rendering logo:
        self.image("../docs/fpdf2-logo.png", 10, 8, 33)
        # Setting font: helvetica bold 15
        self.set_font("helvetica", "B", 15)
        # Moving cursor to the right:
        self.cell(80)
        # Printing title:
        self.cell(30, 10, "Title", border=1, align="C")
        # Performing a line break:
        self.ln(20)

    def footer(self):
        # Position cursor at 1.5 cm from bottom:
        self.set_y(-15)
        # Setting font: helvetica italic 8
        self.set_font("helvetica", "I", 8)
        # Printing page number:
        self.cell(0, 10, f"Page {self.page_no()}/{{nb}}", align="C")


# Instantiation of inherited class
pdf = PDF()
pdf.add_page()
pdf.set_font("Times", size=12)
for i in range(1, 41):
    pdf.cell(0, 10, f"Printing line number {i}", new_x="LMARGIN", new_y="NEXT")
pdf.output("new-tuto2.pdf")

PDF resultante

Este ejemplo hace uso de los métodos header y footer para procesar encabezados y pies de página. Estos son invocados automáticamente. Ellos ya existen en la clase FPDF pero no hacen nada, por lo tanto tenemos que extender la clase y sobreescribirlos.

El logo es impreso con el método image especificando su esquina superior izquierda y su ancho. La altura es calculada automáticamente para respetar las proporciones de la imagen.

Para imprimir el número de página, un valor nulo es pasado como ancho de celda. Esto significa que la celda deberá ser extendida hasta el margen derecho de la página; es útil para centrar texto. El número de página actual es devuelto por el método page_no; respecto al número total de páginas, éste es obtenido mediante el valor especial {nb} que será sustituido al cerrar el documento (este valor especial puede ser cambiado con alias_nb_pages()). Nota el uso del método set_y que permite establecer la posición en una ubicación absoluta en la página, empezando desde arriba o desde abajo.

Otra característica interesante es usada aquí: el salto de página automático. Tan pronto como una celda cruzaría el límite de la página (por defecto a 2 centímetros del borde inferior), un salto es realizado y la fuente es restaurada. Aunque el encabezado y pie de página tienen su propia fuente (helvetica), el cuerpo continúa en Times. Este mecanismo de restauración automática también se aplica a los colores y al ancho de la línea. El límite que dispara los saltos de página puede establecerse con set_auto_page_break.

Tutorial 3 - Saltos de línea y colores

Continuemos con un ejemplo que imprime párrafos justificados. También ilustra el uso de colores.

from fpdf import FPDF


class PDF(FPDF):
    def header(self):
        # Setting font: helvetica bold 15
        self.set_font("helvetica", "B", 15)
        # Calculating width of title and setting cursor position:
        width = self.get_string_width(self.title) + 6
        self.set_x((210 - width) / 2)
        # Setting colors for frame, background and text:
        self.set_draw_color(0, 80, 180)
        self.set_fill_color(230, 230, 0)
        self.set_text_color(220, 50, 50)
        # Setting thickness of the frame (1 mm)
        self.set_line_width(1)
        # Printing title:
        self.cell(
            width,
            9,
            self.title,
            border=1,
            new_x="LMARGIN",
            new_y="NEXT",
            align="C",
            fill=True,
        )
        # Performing a line break:
        self.ln(10)

    def footer(self):
        # Setting position at 1.5 cm from bottom:
        self.set_y(-15)
        # Setting font: helvetica italic 8
        self.set_font("helvetica", "I", 8)
        # Setting text color to gray:
        self.set_text_color(128)
        # Printing page number
        self.cell(0, 10, f"Page {self.page_no()}", align="C")

    def chapter_title(self, num, label):
        # Setting font: helvetica 12
        self.set_font("helvetica", "", 12)
        # Setting background color
        self.set_fill_color(200, 220, 255)
        # Printing chapter name:
        self.cell(
            0,
            6,
            f"Chapter {num} : {label}",
            new_x="LMARGIN",
            new_y="NEXT",
            align="L",
            fill=True,
        )
        # Performing a line break:
        self.ln(4)

    def chapter_body(self, filepath):
        # Reading text file:
        with open(filepath, "rb") as fh:
            txt = fh.read().decode("latin-1")
        # Setting font: Times 12
        self.set_font("Times", size=12)
        # Printing justified text:
        self.multi_cell(0, 5, txt)
        # Performing a line break:
        self.ln()
        # Final mention in italics:
        self.set_font(style="I")
        self.cell(0, 5, "(end of excerpt)")

    def print_chapter(self, num, title, filepath):
        self.add_page()
        self.chapter_title(num, title)
        self.chapter_body(filepath)


pdf = PDF()
pdf.set_title("20000 Leagues Under the Seas")
pdf.set_author("Jules Verne")
pdf.print_chapter(1, "A RUNAWAY REEF", "20k_c1.txt")
pdf.print_chapter(2, "THE PROS AND CONS", "20k_c1.txt")
pdf.output("tuto3.pdf")

PDF resultante

Texto de Julio Verne

El método get_string_width permite determinar la longitud de una cadena de texto en la fuente actual, usada aquí para calcular la posición y el ancho del marco que rodea al título. Los colores son establecidos (vía set_draw_color, set_fill_color y set_text_color) y el grosor de la línea es establecido a 1 mm (contra 0.2 por defecto) con set_line_width. Finalmente, emitimos la celda (el último parámetro en True para indicar que el fondo debe ser rellenado).

El método usado para imprimir párrafos es multi_cell. El texto es justificado por defecto. Cada vez que una línea alcanza el extremo derecho de la celda o un caracter de retorno de línea (\n) es encontrado, un salto de línea es emitido y una nueva celda es automáticamente creada bajo la actual. Un salto automático es realizado en la ubicación del espacio o guión suave (\u00ad) más cercano antes del límite derecho. Un guión suave será reemplazado por un guión normal cuando un salto de línea se dispara, e ignorado en cualquier otro caso.

Dos propiedades del documento son definidas: el título (set_title) y el autor (set_author). Las propiedades pueden ser vistas de dos formas. La primera es abrir el documento directamente con Acrobat Reader, ir al menú Archivo y elegir la opción Propiedades del Documento. La segunda, también disponible desde el complemento, es hacer clic derecho y seleccionar Propiedades del documento.

Tutorial 4 - Múltiples columnas

Este ejemplo es una variante del anterior, mostrando cómo poner el texto en múltiples columnas.

from fpdf import FPDF


class PDF(FPDF):
    def __init__(self):
        super().__init__()
        self.col = 0  # Current column
        self.y0 = 0  # Ordinate of column start

    def header(self):
        self.set_font("helvetica", "B", 15)
        width = self.get_string_width(self.title) + 6
        self.set_x((210 - width) / 2)
        self.set_draw_color(0, 80, 180)
        self.set_fill_color(230, 230, 0)
        self.set_text_color(220, 50, 50)
        self.set_line_width(1)
        self.cell(
            width,
            9,
            self.title,
            border=1,
            new_x="LMARGIN",
            new_y="NEXT",
            align="C",
            fill=True,
        )
        self.ln(10)
        # Saving ordinate position:
        self.y0 = self.get_y()

    def footer(self):
        self.set_y(-15)
        self.set_font("helvetica", "I", 8)
        self.set_text_color(128)
        self.cell(0, 10, f"Page {self.page_no()}", align="C")

    def set_col(self, col):
        # Set column position:
        self.col = col
        x = 10 + col * 65
        self.set_left_margin(x)
        self.set_x(x)

    @property
    def accept_page_break(self):
        if self.col < 2:
            # Go to next column:
            self.set_col(self.col + 1)
            # Set ordinate to top:
            self.set_y(self.y0)
            # Stay on the same page:
            return False
        # Go back to first column:
        self.set_col(0)
        # Trigger a page break:
        return True

    def chapter_title(self, num, label):
        self.set_font("helvetica", "", 12)
        self.set_fill_color(200, 220, 255)
        self.cell(
            0,
            6,
            f"Chapter {num} : {label}",
            new_x="LMARGIN",
            new_y="NEXT",
            border="L",
            fill=True,
        )
        self.ln(4)
        # Saving ordinate position:
        self.y0 = self.get_y()

    def chapter_body(self, name):
        # Reading text file:
        with open(name, "rb") as fh:
            txt = fh.read().decode("latin-1")
        # Setting font: Times 12
        self.set_font("Times", size=12)
        # Printing text in a 6cm width column:
        self.multi_cell(60, 5, txt)
        self.ln()
        # Final mention in italics:
        self.set_font(style="I")
        self.cell(0, 5, "(end of excerpt)")
        # Start back at first column:
        self.set_col(0)

    def print_chapter(self, num, title, name):
        self.add_page()
        self.chapter_title(num, title)
        self.chapter_body(name)


pdf = PDF()
pdf.set_title("20000 Leagues Under the Seas")
pdf.set_author("Jules Verne")
pdf.print_chapter(1, "A RUNAWAY REEF", "20k_c1.txt")
pdf.print_chapter(2, "THE PROS AND CONS", "20k_c1.txt")
pdf.output("tuto4.pdf")

PDF resultante

Texto de Julio Verne

La diferencia clave con el tutorial anterior es el uso de los métodos accept_page_break y set_col.

Utilizando el método accept_page_break, una vez la celda cruce el límite inferior de la página, éste comprobará el número de la columna actual. Si es menor que 2 (decidimos dividir la página en tres columnas) éste invocará al método set_col, incrementando el número de columna y alterando la posición de la siguiente columna tal que el texto pueda continuar aquí.

Una vez el límite inferior de la tercera columna es alcanzado, el método accept_page_break se reiniciará y volverá a la primera columna, desencadenando un salto de página.

Tutorial 5 - Creando tablas

Este tutorial explicará cómo crear tablas fácilmente.

El código creará tres tablas diferentes para explicar lo que puede lograrse con algunos cambios sencillos.

import csv
from fpdf import FPDF


class PDF(FPDF):
    def basic_table(self, headings, rows):
        for heading in headings:
            self.cell(40, 7, heading, 1)
        self.ln()
        for row in rows:
            for col in row:
                self.cell(40, 6, col, 1)
            self.ln()

    def improved_table(self, headings, rows, col_widths=(42, 39, 35, 40)):
        for col_width, heading in zip(col_widths, headings):
            self.cell(col_width, 7, heading, border=1, align="C")
        self.ln()
        for row in rows:
            self.cell(col_widths[0], 6, row[0], border="LR")
            self.cell(col_widths[1], 6, row[1], border="LR")
            self.cell(col_widths[2], 6, row[2], border="LR", align="R")
            self.cell(col_widths[3], 6, row[3], border="LR", align="R")
            self.ln()
        # Closure line:
        self.cell(sum(col_widths), 0, "", border="T")

    def colored_table(self, headings, rows, col_widths=(42, 39, 35, 42)):
        # Colors, line width and bold font:
        self.set_fill_color(255, 100, 0)
        self.set_text_color(255)
        self.set_draw_color(255, 0, 0)
        self.set_line_width(0.3)
        self.set_font(style="B")
        for col_width, heading in zip(col_widths, headings):
            self.cell(col_width, 7, heading, border=1, align="C", fill=True)
        self.ln()
        # Color and font restoration:
        self.set_fill_color(224, 235, 255)
        self.set_text_color(0)
        self.set_font()
        fill = False
        for row in rows:
            self.cell(col_widths[0], 6, row[0], border="LR", align="L", fill=fill)
            self.cell(col_widths[1], 6, row[1], border="LR", align="L", fill=fill)
            self.cell(col_widths[2], 6, row[2], border="LR", align="R", fill=fill)
            self.cell(col_widths[3], 6, row[3], border="LR", align="R", fill=fill)
            self.ln()
            fill = not fill
        self.cell(sum(col_widths), 0, "", "T")


def load_data_from_csv(csv_filepath):
    headings, rows = [], []
    with open(csv_filepath, encoding="utf8") as csv_file:
        for row in csv.reader(csv_file, delimiter=","):
            if not headings:  # extracting column names from first row:
                headings = row
            else:
                rows.append(row)
    return headings, rows


col_names, data = load_data_from_csv("countries.txt")
pdf = PDF()
pdf.set_font("helvetica", size=14)
pdf.add_page()
pdf.basic_table(col_names, data)
pdf.add_page()
pdf.improved_table(col_names, data)
pdf.add_page()
pdf.colored_table(col_names, data)
pdf.output("tuto5.pdf")

PDF resultante - Archivo de texto con países

Dado que una tabla es solo una colección de celdas, es natural construir una a partir de ellas.

El primer ejemplo se logra de la forma más básica posible: simples celdas enmarcadas, todas del mismo tamaño y alineadas a la izquierda. El resultado es rudimentario pero muy rápido de obtener.

La segunda tabla incluye algunas mejoras: cada columna tiene su propio ancho, los títulos están centrados y las figuras alineadas a la derecha. Es más, las líneas horizontales han sido removidas. Esto es hecho por medio del parámetro border del método Cell(), el cual especifica qué lados de la celda deben dibujarse. Aquí queremos el izquierdo (L) y el derecho (R). Ahora solo queda el problema de la línea horizontal para terminar la tabla. Hay dos posibilidades para resolverlo: encontrar la última línea en el ciclo, en cuyo caso usamos LRB para el parámetro border; o, como se hizo aquí, agregar la línea una vez el ciclo ha terminado.

La tercera tabla es similar a la segunda, pero usa colores. Los colores de relleno, texto y línea son especificados de manera simple. Un coloreado alternante para las filas es obtenido usando de forma alternada celdas transparentes y rellenas.

Tutorial 6 - Creando enlaces y combinando estilos de texto

Este tutorial explicará varias formas de insertar enlaces dentro de un documento pdf, al igual que cómo agregar enlaces a recursos externos.

También mostrará muchas formas en que podemos usar los diferentes estilos de texto (negrita, cursiva, subrayado) dentro del mismo texto.

from fpdf import FPDF


pdf = FPDF()

# First page:
pdf.add_page()
pdf.set_font("helvetica", size=20)
pdf.write(5, "To find out what's new in self tutorial, click ")
pdf.set_font(style="U")
link = pdf.add_link()
pdf.write(5, "here", link)
pdf.set_font()

# Second page:
pdf.add_page()
pdf.set_link(link)
pdf.image(
    "../docs/fpdf2-logo.png", 10, 10, 50, 0, "", "https://pyfpdf.github.io/fpdf2/"
)
pdf.set_left_margin(60)
pdf.set_font_size(18)
pdf.write_html(
    """You can print text mixing different styles using HTML tags: <b>bold</b>, <i>italic</i>,
<u>underlined</u>, or <b><i><u>all at once</u></i></b>!
<br><br>You can also insert links on text, such as <a href="https://pyfpdf.github.io/fpdf2/">https://pyfpdf.github.io/fpdf2/</a>,
or on an image: the logo is clickable!"""
)
pdf.output("tuto6.pdf")

PDF resultante - Logo de fpdf2

El nuevo método mostrado aquí para imprimir texto es write() . Es muy similar a multi_cell() , siendo las diferencias clave:

  • El final de línea está en el margen derecho y la siguiente línea comienza en el margen izquierdo.
  • La posición actual se desplaza al final del texto.

El método por tanto nos permite escribir un trozo de texto, alterar el estilo de la fuente, y continuar desde el lugar exacto donde quedamos. Por otro lado, su principal desventaja es que no podemos justificar el texto como hacemos con el método multi_cell().

En la primera página del ejemplo usamos write() para este propósito. El comienzo de la oración está escrito usando texto en estilo regular, luego usando el método set_font() cambiamos a subrayado y terminamos la oración.

Para agregar un enlace interno apuntando a la segunda página, usamos el método add_link() , el cual crea un área clicable a la que nombramos "link" que redirige a otro lugar dentro del documento. En la segunda página usamos set_link() para definir el área de destino para el enlace que acabamos de crear.

Para crear un enlace externo usando una imagen, usamos image() . El método tiene la opción de recibir un enlace como uno de sus argumentos. El enlace puede ser tanto interno como externo.

Como alternativa, otra opción para cambiar el estilo de fuente y agregar enlaces es usar el método write_html(). Este es un analizador de html que permite agregar texto, cambiar el estilo de fuente y agregar enlaces usando html.