Skip to content

Combine with Markdown

Several fpdf2 methods allow Markdown syntax elements:

  • FPDF.cell() has an optional markdown=True parameter that makes it possible to use **bold**, __italics__, ~~strikethrough~~ or --underlined-- Markdown markers
  • FPDF.multi_cell() & FPDF.table() methods have a similar feature

But fpdf2 also allows for basic conversion from HTML to PDF (cf. HTML). This can be combined with a Markdown-rendering library in order to generate PDF documents from Markdown:

mistletoe

The mistletoe library follows the CommonMark specification:

pip install mistletoe

from mistletoe import markdown

html = markdown(
    """
# Top title (ATX)

Subtitle (setext)
-----------------

### An even lower heading (ATX)

**Text in bold**

_Text in italics_

~~Strikethrough~~

[This is a link](https://github.com/PyFPDF/fpdf2)

<https://py-pdf.github.io/fpdf2/>

This is an unordered list:
* an item
* another item

This is an ordered list:
1. first item
2. second item
3. third item with an unordered sublist:
    * an item
    * another item

Inline `code span`

A table:

| Foo | Bar | Baz |
| ---:|:---:|:--- |
| Foo | Bar | Baz |

Actual HTML:

<dl>
  <dt>Term1</dt><dd>Definition1</dd>
  <dt>Term2</dt><dd>Definition2</dd>
</dl>

Some horizontal thematic breaks:

***
---
___

![Alternate description](https://py-pdf.github.io/fpdf2/fpdf2-logo.png)
"""
)

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.write_html(html)
pdf.output("pdf-from-markdown-with-mistletoe.pdf")

The library can be easily extended: Creating a custom token and renderer.

Rendering unicode characters

from mistletoe import markdown

html = markdown(
    """
# Unicode:

| Emoji | Description |
| --- | - |
| 😀 | GRINNING FACE |
| 😁 | GRINNING FACE WITH SMILING EYES |
| 😈 | SMILING FACE WITH HORNS |

# A checklist:

* ☐ item 1
* ☑ item 2
* ☐ item 3
"""
)

from fpdf import FPDF

pdf = FPDF()
pdf.add_font("DejaVuSans", fname="test/fonts/DejaVuSans.ttf")
pdf.add_font("DejaVuSans", fname="test/fonts/DejaVuSans-Bold.ttf", style="B")
pdf.set_font("DejaVuSans", size=24)
pdf.add_page()
pdf.write_html(html)
pdf.output("pdf-from-markdown-with-mistletoe-unicode.pdf")

Result:

markdown-it-py

The markdown-it-py library also follows the CommonMark specification:

pip install markdown-it-py

from markdown_it import MarkdownIt

md = (
    MarkdownIt("commonmark", {"breaks": True, "html": True})
    .enable("strikethrough")
    .enable("table")
)
html = md.render(
    """
# Top title (ATX)

Subtitle (setext)
-----------------

### An even lower heading (ATX)

**Text in bold**

_Text in italics_

~~Strikethrough~~

[This is a link](https://github.com/PyFPDF/fpdf2)

<https://py-pdf.github.io/fpdf2/>

This is an unordered list:
* an item
* another item

This is an ordered list:
1. first item
2. second item
3. third item with an unordered sublist:
    * an item
    * another item

Inline `code span`

A table:

| Foo | Bar | Baz |
| ---:|:---:|:--- |
| Foo | Bar | Baz |

Actual HTML:

<dl>
  <dt>Term1</dt><dd>Definition1</dd>
  <dt>Term2</dt><dd>Definition2</dd>
</dl>

Some horizontal thematic breaks:

***
---
___

![Alternate description](https://py-pdf.github.io/fpdf2/fpdf2-logo.png)
"""
)

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.write_html(html)
pdf.output("pdf-from-markdown-with-markdown-it.pdf")

Plugin extensions: the strikethrough & table plugins are embedded within the core package, and many other plugins are then available via the mdit-py-plugins package, including:

  • Footnotes
  • Definition lists
  • Task lists
  • Heading anchors
  • LaTeX math
  • Containers
  • Word count

mistune

There is also the mistune library, that may be the fastest, but it does not follow the CommonMark spec:

pip install mistune

from mistune import html

html = html(
    """
# Top title (ATX)

Subtitle (setext)
-----------------

### An even lower heading (ATX)

**Text in bold**

_Text in italics_

~~Strikethrough~~

[This is a link](https://github.com/PyFPDF/fpdf2)

<https://py-pdf.github.io/fpdf2/>

This is an unordered list:
* an item
* another item

This is an ordered list:
1. first item
2. second item
3. third item with an unordered sublist:
    * an item
    * another item

Inline `code span`

A table:

| Foo | Bar | Baz |
| ---:|:---:|:--- |
| Foo | Bar | Baz |

Actual HTML:

<dl>
  <dt>Term1</dt><dd>Definition1</dd>
  <dt>Term2</dt><dd>Definition2</dd>
</dl>

Some horizontal thematic breaks:

***
---
___

![Alternate description](https://py-pdf.github.io/fpdf2/fpdf2-logo.png)
"""
)

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.write_html(html)
pdf.output("pdf-from-markdown-with-mistune.pdf")

Python-Markdown

There is also the Python-Markdown library, which is the oldest Markdown rendering Python lib still active, but it does not follow the CommonMark spec:

pip install markdown

from markdown import markdown

html = markdown(
    """
# Top title (ATX)

Subtitle (setext)
-----------------

### An even lower heading (ATX)

**Text in bold**

_Text in italics_

[This is a link](https://github.com/PyFPDF/fpdf2)

<https://py-pdf.github.io/fpdf2/>

This is an unordered list:

* an item
* another item

This is an ordered list:

1. first item
2. second item
3. third item with an unordered sublist:
    * an item
    * another item

Inline `code span`

A table:

Foo | Bar | Baz
--- | --- | ---
Foo | Bar | Baz

Definition list:

Term
: Definition

Actual HTML:

<dl>
  <dt>Term1</dt><dd>Definition1</dd>
  <dt>Term2</dt><dd>Definition2</dd>
</dl>

Some horizontal thematic breaks:

***

---

___

![Alternate description](https://py-pdf.github.io/fpdf2/fpdf2-logo.png)
""",
    extensions=["def_list", "sane_lists", "tables"],
)

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.write_html(html)
pdf.output("pdf-from-markdown-with-markdown.pdf")