ReportLab can embed custom TrueType fonts in PDF files by registering the font with TTFont() and pdfmetrics.registerFont(). After registration, the font can be used with both the low-level PDF Canvas and Platypus components such as Paragraph.
setFont() or ParagraphStyle, and then generate the PDF.Install ReportLab with pip:
python -m pip install reportlab
Then import the classes required for TrueType font registration:
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
ReportLab can use standard PDF fonts such as:
Helvetica
Helvetica-Bold
Times-Roman
Times-Bold
Courier
Courier-Bold
These standard fonts can be selected directly:
c.setFont('Helvetica', 14)
A custom TrueType font must first be registered.
pdfmetrics.registerFont(TTFont('MyFont', 'fonts/MyFont-Regular.ttf'))
After registration:
c.setFont('MyFont', 14)
'MyFont' is the internal ReportLab name you choose during registration. It does not have to be identical to the TTF filename.A simple project structure is:
my_project/
|-- create_pdf.py
|-- fonts/
| |-- MyFont-Regular.ttf
| |-- MyFont-Bold.ttf
|-- output/
|-- document.pdf
Keeping fonts with the application is usually easier to deploy than copying font files into the installed ReportLab package directory.
from pathlib import Path
base_dir=Path(__file__).resolve().parent
font_path=base_dir / 'fonts' / 'MyFont-Regular.ttf'
print(font_path)
pathlib avoids hard-coded path separators and works across Windows, macOS and Linux.
Create a TTFont object and register it with pdfmetrics.
from pathlib import Path
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
font_path=Path('fonts/MyFont-Regular.ttf')
pdfmetrics.registerFont(
TTFont('MyFont', str(font_path))
)
The two important arguments are:
TTFont('MyFont', 'fonts/MyFont-Regular.ttf')
^ ^
| |
ReportLab name Font file
Use the ReportLab name when selecting the font later.
The pdfgen.canvas.Canvas class is useful when text and graphics must be positioned directly on a PDF page.
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
font_path=Path('fonts/MyFont-Regular.ttf')
pdfmetrics.registerFont(TTFont('MyFont', str(font_path)))
c=canvas.Canvas('custom-font.pdf', pagesize=letter)
c.setFont('MyFont', 18)
c.drawString(72, 720, 'Welcome to plus2net')
c.save()
The custom font is selected by:
c.setFont('MyFont', 18)
and the text is placed by:
c.drawString(72, 720, 'Welcome to plus2net')
c.setFont('MyFont', 12)
c.drawString(72, 690, '12 point custom font')
c.setFont('MyFont', 24)
c.drawString(72, 650, '24 point custom font')
ReportLab TrueType fonts work with Unicode text, but the selected font must actually contain glyphs for the characters being used.
msg='Caf\u00e9 - Python PDF'
c.drawString(72, 720, msg)
If the font does not contain a required character, choosing that font cannot create the missing glyph.
For multi-line text, wrapping and document layouts, use Platypus Paragraph with a ParagraphStyle.
from pathlib import Path
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import SimpleDocTemplate, Paragraph
font_path=Path('fonts/MyFont-Regular.ttf')
pdfmetrics.registerFont(TTFont('MyFont', str(font_path)))
style=ParagraphStyle(
'CustomFont',
fontName='MyFont',
fontSize=12,
leading=16
)
doc=SimpleDocTemplate('paragraph-font.pdf', pagesize=A4)
story=[
Paragraph('This paragraph uses a custom TrueType font registered with ReportLab.', style)
]
doc.build(story)
The custom font is connected to the paragraph through:
fontName='MyFont'
style=ParagraphStyle(
'CustomFont',
fontName='MyFont',
fontSize=12,
leading=16,
backColor='#F1F1F1',
borderColor='#CCCCCC',
borderWidth=1,
borderPadding=10
)
For longer documents, Platypus handles wrapping and page flow more naturally than manually positioning every line with drawString().
If a custom font provides separate regular, bold, italic and bold-italic TTF files, register each file.
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('MyFont', 'fonts/MyFont-Regular.ttf'))
pdfmetrics.registerFont(TTFont('MyFont-Bold', 'fonts/MyFont-Bold.ttf'))
pdfmetrics.registerFont(TTFont('MyFont-Italic', 'fonts/MyFont-Italic.ttf'))
pdfmetrics.registerFont(TTFont('MyFont-BoldItalic', 'fonts/MyFont-BoldItalic.ttf'))
Then map them as one family:
pdfmetrics.registerFontFamily(
'MyFont',
normal='MyFont',
bold='MyFont-Bold',
italic='MyFont-Italic',
boldItalic='MyFont-BoldItalic'
)
This is important when Paragraph markup uses:
<b>Bold text</b>
<i>Italic text</i>
<b><i>Bold italic text</i></b>
Paragraph markup supports the <font> tag.
text='''This uses the paragraph font.
<font name="Helvetica" color="blue">This part uses Helvetica.</font>
<font name="MyFont" color="red">This part uses the custom font.</font>'''
paragraph=Paragraph(text, style)
The font name used inside the tag must be a font name ReportLab knows about.
A common error is supplying an incorrect path to the TTF file.
from pathlib import Path
font_path=Path('fonts/MyFont-Regular.ttf')
if not font_path.is_file():
raise FileNotFoundError(f'Font file not found: {font_path}')
Register the font only after confirming the file exists:
pdfmetrics.registerFont(
TTFont('MyFont', str(font_path))
)
from pathlib import Path
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
def register_font(name, filename):
font_path=Path(filename)
if not font_path.is_file():
raise FileNotFoundError(f'Font file not found: {font_path}')
pdfmetrics.registerFont(TTFont(name, str(font_path)))
register_font('MyFont', 'fonts/MyFont-Regular.ttf')
A TrueType font may contain Arabic, Devanagari or other complex-script glyphs, but having the glyphs is only one part of correct text rendering.
Some writing systems require:
Modern ReportLab versions include developing support for text shaping and right-to-left scripts. This is particularly relevant to Arabic, Hebrew and Persian and can also affect Indic and other complex scripts.
TTFont() does not by itself guarantee linguistically correct shaping. Use a suitable Unicode font, use the shaping/RTL support appropriate to your ReportLab version, and verify the generated PDF with representative text.| Text Requirement | What Is Needed |
|---|---|
| Latin text with accents | A TrueType font containing the required glyphs |
| Many CJK TrueType fonts | Suitable font coverage |
| Arabic / Persian / Hebrew | Glyph coverage plus RTL/BiDi and, where required, shaping |
| Many Indic scripts | Glyph coverage plus script shaping |
For multilingual projects, test the exact font and the actual language content rather than assuming that successful font registration guarantees correct output.
Custom fonts are software assets with their own licenses. Before distributing a PDF that embeds a font, check that the font license allows PDF embedding and the intended type of distribution.
Open-source fonts often provide clear embedding permissions, while commercial fonts can have different licensing conditions.
After:
pdfmetrics.registerFont(TTFont('MyFont', 'fonts/MyFont-Regular.ttf'))
use:
c.setFont('MyFont', 14)
not:
c.setFont('MyFont-Regular.ttf', 14)
Confirm that the file exists before registering it.
Application font files are easier to maintain in a project directory such as fonts/. Package upgrades or environment changes should not be relied on to preserve your own assets.
Unicode support depends on the glyph coverage of the actual font.
Register and map regular, bold, italic and bold-italic font variants when those styles are required.
Complex scripts can require shaping and bidirectional processing in addition to an appropriate font.
drawString() is appropriate for positioned single-line text. Use Paragraph and Platypus for wrapping and flowing multi-line text.
Verify that your font license allows the required PDF embedding and distribution.
TTFont.pdfmetrics.registerFont().TTFont() argument is the internal ReportLab font name.pathlib for portable font paths.Canvas.setFont() for directly positioned PDF text.ParagraphStyle(fontName=...) for Platypus Paragraphs.registerFontFamily() so Paragraph <b> and <i> markup can map to custom font variants.<font name="..."> to switch fonts inside Paragraph markup.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.