0
# Font Handling and Text
1
2
TrueType and OpenType font support for rendering text on images with full typography control including size, spacing, alignment, and styling options. The ImageFont module provides comprehensive font loading and text rendering capabilities.
3
4
## Capabilities
5
6
### Font Loading
7
8
Load and manage different types of fonts.
9
10
```python { .api }
11
def load_default(size=None):
12
"""
13
Load the default PIL font.
14
15
Parameters:
16
- size (float): Font size (if None, uses default size)
17
18
Returns:
19
FreeTypeFont | ImageFont: Default font object
20
"""
21
22
def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
23
"""
24
Load a TrueType or OpenType font from file.
25
26
Parameters:
27
- font (str | bytes | Path): Font filename, bytes, or pathlib.Path
28
- size (float): Font size in points
29
- index (int): Font index for font collections (TTC files)
30
- encoding (str): Character encoding
31
- layout_engine (int): Text layout engine (LAYOUT_BASIC or LAYOUT_RAQM)
32
33
Returns:
34
FreeTypeFont: Font object
35
36
Raises:
37
OSError: If font file cannot be loaded
38
"""
39
40
def load(filename):
41
"""
42
Load a bitmap font from file.
43
44
Parameters:
45
- filename (str): Font filename (.pil format)
46
47
Returns:
48
ImageFont: Bitmap font object
49
"""
50
51
def load_path(filename):
52
"""
53
Load a bitmap font, searching in font path.
54
55
Parameters:
56
- filename (str | bytes): Font filename
57
58
Returns:
59
ImageFont: Bitmap font object
60
"""
61
```
62
63
### Font Classes
64
65
Font object types for different font formats.
66
67
```python { .api }
68
class FreeTypeFont:
69
"""TrueType/OpenType font support with advanced typography features."""
70
71
@property
72
def family(self) -> str:
73
"""Font family name."""
74
75
@property
76
def style(self) -> str:
77
"""Font style name."""
78
79
@property
80
def size(self) -> float:
81
"""Font size in points."""
82
83
def getsize(self, text, direction=None, features=None, language=None, stroke_width=0):
84
"""
85
Get the size of given text (deprecated, use getbbox).
86
87
Parameters:
88
- text (str): Text to measure
89
- direction (str): Text direction ("ltr", "rtl", "ttb")
90
- features (list): OpenType features
91
- language (str): Language code
92
- stroke_width (int): Text stroke width
93
94
Returns:
95
tuple: Text size as (width, height)
96
"""
97
98
def getbbox(self, text, anchor=None, direction=None, features=None, language=None, stroke_width=0, embedded_color=False):
99
"""
100
Get the bounding box of given text.
101
102
Parameters:
103
- text (str): Text to measure
104
- anchor (str): Text anchor point
105
- direction (str): Text direction
106
- features (list): OpenType features
107
- language (str): Language code
108
- stroke_width (int): Text stroke width
109
- embedded_color (bool): Whether to use embedded color fonts
110
111
Returns:
112
tuple: Bounding box as (left, top, right, bottom)
113
"""
114
115
def getlength(self, text, direction=None, features=None, language=None, embedded_color=False):
116
"""
117
Get the length of given text.
118
119
Parameters:
120
- text (str): Text to measure
121
- direction (str): Text direction
122
- features (list): OpenType features
123
- language (str): Language code
124
- embedded_color (bool): Whether to use embedded color fonts
125
126
Returns:
127
float: Text length in pixels
128
"""
129
130
def getmask(self, text, mode="", direction=None, features=None, language=None, stroke_width=0, anchor=None, ink=0, start=None):
131
"""
132
Create a text mask.
133
134
Parameters:
135
- text (str): Text to render
136
- mode (str): Mask mode
137
- direction (str): Text direction
138
- features (list): OpenType features
139
- language (str): Language code
140
- stroke_width (int): Text stroke width
141
- anchor (str): Text anchor point
142
- ink (int): Ink parameter for bitmap fonts
143
- start (tuple): Starting offset
144
145
Returns:
146
Image: Text mask
147
"""
148
149
def font_variant(self, font=None, size=None, index=None, encoding=None, layout_engine=None):
150
"""
151
Create a variant of this font with different parameters.
152
153
Parameters:
154
- font (str): Font filename (uses current if None)
155
- size (float): Font size (uses current if None)
156
- index (int): Font index (uses current if None)
157
- encoding (str): Character encoding (uses current if None)
158
- layout_engine (int): Layout engine (uses current if None)
159
160
Returns:
161
FreeTypeFont: New font variant
162
"""
163
164
def set_variation_by_name(self, name):
165
"""
166
Set font variation by name (for variable fonts).
167
168
Parameters:
169
- name (str): Variation name
170
"""
171
172
def set_variation_by_axes(self, axes):
173
"""
174
Set font variation by axes (for variable fonts).
175
176
Parameters:
177
- axes (dict): Axis values as {axis_name: value}
178
"""
179
180
def get_variation_names(self):
181
"""
182
Get available variation names (for variable fonts).
183
184
Returns:
185
list: Available variation names
186
"""
187
188
def get_variation_axes(self):
189
"""
190
Get available variation axes (for variable fonts).
191
192
Returns:
193
list: Available axes information
194
"""
195
196
class ImageFont:
197
"""Bitmap font support for simple text rendering."""
198
199
def getsize(self, text):
200
"""
201
Get the size of given text.
202
203
Parameters:
204
- text (str): Text to measure
205
206
Returns:
207
tuple: Text size as (width, height)
208
"""
209
210
def getmask(self, text, mode="", direction=None, features=None, language=None, stroke_width=0, anchor=None, ink=0, start=None):
211
"""
212
Create a text mask.
213
214
Parameters:
215
- text (str): Text to render
216
- mode (str): Mask mode
217
218
Returns:
219
Image: Text mask
220
"""
221
222
class TransposedFont:
223
"""Font wrapper that applies a transformation to rendered text."""
224
225
def __init__(self, font, orientation=None):
226
"""
227
Create a transposed font.
228
229
Parameters:
230
- font (FreeTypeFont | ImageFont): Base font
231
- orientation (int): Text orientation
232
"""
233
```
234
235
### Layout Engines
236
237
Constants for text layout engines.
238
239
```python { .api }
240
LAYOUT_BASIC: int # Basic text layout engine
241
LAYOUT_RAQM: int # Advanced text layout engine (supports complex scripts)
242
```
243
244
## Usage Examples
245
246
### Basic Font Loading and Text Rendering
247
248
```python
249
from PIL import Image, ImageDraw, ImageFont
250
251
# Create a blank image
252
img = Image.new("RGB", (400, 200), "white")
253
draw = ImageDraw.Draw(img)
254
255
# Load different fonts
256
try:
257
# Load system fonts
258
title_font = ImageFont.truetype("arial.ttf", 36)
259
body_font = ImageFont.truetype("arial.ttf", 18)
260
bold_font = ImageFont.truetype("arialbd.ttf", 24)
261
except OSError:
262
# Fallback to default font if system fonts not available
263
title_font = ImageFont.load_default()
264
body_font = ImageFont.load_default()
265
bold_font = ImageFont.load_default()
266
267
# Draw text with different fonts
268
draw.text((200, 50), "Main Title", font=title_font, fill="black", anchor="mm")
269
draw.text((200, 100), "Subtitle text", font=bold_font, fill="blue", anchor="mm")
270
draw.text((200, 140), "Body text with default styling", font=body_font, fill="gray", anchor="mm")
271
272
img.save("font_example.png")
273
```
274
275
### Advanced Typography Features
276
277
```python
278
from PIL import Image, ImageDraw, ImageFont
279
280
img = Image.new("RGB", (600, 400), "white")
281
draw = ImageDraw.Draw(img)
282
283
# Load font with specific size
284
font = ImageFont.truetype("times.ttf", 24)
285
286
# Text with different anchors
287
text = "Anchor Demo"
288
y_positions = [50, 100, 150, 200]
289
anchors = ["lt", "mt", "rt", "mm"]
290
anchor_names = ["left-top", "middle-top", "right-top", "middle-middle"]
291
292
for y, anchor, name in zip(y_positions, anchors, anchor_names):
293
# Draw reference line
294
draw.line([(0, y), (600, y)], fill="lightgray", width=1)
295
draw.line([(300, y-20), (300, y+20)], fill="lightgray", width=1)
296
297
# Draw text with anchor
298
draw.text((300, y), text, font=font, fill="black", anchor=anchor)
299
300
# Label the anchor type
301
draw.text((10, y), f"{anchor} ({name})", font=ImageFont.load_default(), fill="red")
302
303
img.save("anchor_demo.png")
304
```
305
306
### Text Measurement and Layout
307
308
```python
309
from PIL import Image, ImageDraw, ImageFont
310
311
# Create image
312
img = Image.new("RGB", (500, 300), "white")
313
draw = ImageDraw.Draw(img)
314
315
# Load font
316
font = ImageFont.truetype("arial.ttf", 20)
317
318
# Text to measure
319
text = "Sample text for measurement"
320
321
# Get text bounding box
322
bbox = font.getbbox(text)
323
text_width = bbox[2] - bbox[0]
324
text_height = bbox[3] - bbox[1]
325
326
print(f"Text dimensions: {text_width} x {text_height}")
327
print(f"Bounding box: {bbox}")
328
329
# Center text using measurements
330
img_width, img_height = img.size
331
x = (img_width - text_width) // 2
332
y = (img_height - text_height) // 2
333
334
# Draw text
335
draw.text((x, y), text, font=font, fill="black")
336
337
# Draw bounding box for visualization
338
draw.rectangle([x + bbox[0], y + bbox[1], x + bbox[2], y + bbox[3]],
339
outline="red", width=1)
340
341
img.save("text_measurement.png")
342
```
343
344
### Multiline Text and Formatting
345
346
```python
347
from PIL import Image, ImageDraw, ImageFont
348
349
img = Image.new("RGB", (400, 300), "white")
350
draw = ImageDraw.Draw(img)
351
352
font = ImageFont.truetype("arial.ttf", 16)
353
354
# Multiline text with different alignments
355
multiline_text = """This is a sample of multiline text
356
that demonstrates different
357
alignment options and
358
line spacing controls."""
359
360
# Left aligned
361
draw.multiline_text((50, 50), multiline_text, font=font, fill="black",
362
align="left", spacing=8)
363
364
# Center aligned
365
draw.multiline_text((200, 50), multiline_text, font=font, fill="blue",
366
align="center", spacing=12)
367
368
# Right aligned
369
draw.multiline_text((350, 50), multiline_text, font=font, fill="green",
370
align="right", spacing=6)
371
372
img.save("multiline_text.png")
373
```
374
375
### Text Effects and Styling
376
377
```python
378
from PIL import Image, ImageDraw, ImageFont
379
380
img = Image.new("RGB", (500, 200), "white")
381
draw = ImageDraw.Draw(img)
382
383
font = ImageFont.truetype("arial.ttf", 48)
384
text = "STYLED TEXT"
385
386
# Shadow effect
387
shadow_offset = 3
388
draw.text((102 + shadow_offset, 52 + shadow_offset), text, font=font, fill="gray")
389
draw.text((102, 52), text, font=font, fill="black")
390
391
# Outline effect (stroke)
392
if hasattr(font, 'getbbox'): # Check if FreeType font
393
draw.text((100, 100), text, font=font, fill="white",
394
stroke_width=2, stroke_fill="black")
395
396
img.save("text_effects.png")
397
```
398
399
### Working with Different Text Directions
400
401
```python
402
from PIL import Image, ImageDraw, ImageFont
403
404
img = Image.new("RGB", (400, 400), "white")
405
draw = ImageDraw.Draw(img)
406
407
font = ImageFont.truetype("arial.ttf", 24)
408
409
# Left-to-right text (default)
410
draw.text((50, 50), "Left to Right Text", font=font, fill="black", direction="ltr")
411
412
# Right-to-left text (for Arabic, Hebrew, etc.)
413
arabic_text = "النص العربي" # "Arabic text" in Arabic
414
try:
415
draw.text((350, 100), arabic_text, font=font, fill="blue",
416
direction="rtl", anchor="ra")
417
except:
418
# Fallback if font doesn't support Arabic
419
draw.text((350, 100), "RTL Text", font=font, fill="blue",
420
direction="rtl", anchor="ra")
421
422
# Vertical text
423
draw.text((200, 150), "Vertical", font=font, fill="green", direction="ttb")
424
425
img.save("text_directions.png")
426
```
427
428
### Variable Font Support
429
430
```python
431
from PIL import Image, ImageDraw, ImageFont
432
433
# Load a variable font (if available)
434
try:
435
var_font = ImageFont.truetype("variable_font.ttf", 36)
436
437
img = Image.new("RGB", (600, 300), "white")
438
draw = ImageDraw.Draw(img)
439
440
# Get available variations
441
variations = var_font.get_variation_names()
442
axes = var_font.get_variation_axes()
443
444
print("Available variations:", variations)
445
print("Available axes:", axes)
446
447
# Apply different variations
448
positions = [(100, 50), (100, 100), (100, 150), (100, 200)]
449
450
for i, pos in enumerate(positions):
451
# Create font variant with different weight
452
weight = 200 + i * 200 # Vary from 200 to 800
453
var_font.set_variation_by_axes({"wght": weight})
454
455
draw.text(pos, f"Weight {weight}", font=var_font, fill="black")
456
457
img.save("variable_font.png")
458
459
except OSError:
460
print("Variable font not available")
461
```
462
463
### Font Information and Properties
464
465
```python
466
from PIL import ImageFont
467
468
# Load font and inspect properties
469
font = ImageFont.truetype("arial.ttf", 24)
470
471
print(f"Font family: {font.family}")
472
print(f"Font style: {font.style}")
473
print(f"Font size: {font.size}")
474
475
# Test text measurements
476
test_text = "Hello, World!"
477
bbox = font.getbbox(test_text)
478
length = font.getlength(test_text)
479
480
print(f"Text: '{test_text}'")
481
print(f"Bounding box: {bbox}")
482
print(f"Text length: {length}")
483
484
# Compare with different sizes
485
sizes = [12, 18, 24, 36, 48]
486
for size in sizes:
487
font_size = ImageFont.truetype("arial.ttf", size)
488
bbox = font_size.getbbox(test_text)
489
width = bbox[2] - bbox[0]
490
height = bbox[3] - bbox[1]
491
print(f"Size {size}: {width} x {height} pixels")
492
```
493
494
### Professional Text Layout
495
496
```python
497
from PIL import Image, ImageDraw, ImageFont
498
499
def create_text_layout(title, subtitle, body, width=600, height=400):
500
"""Create a professional text layout with proper spacing."""
501
img = Image.new("RGB", (width, height), "white")
502
draw = ImageDraw.Draw(img)
503
504
# Load fonts
505
title_font = ImageFont.truetype("arial.ttf", 32)
506
subtitle_font = ImageFont.truetype("arial.ttf", 20)
507
body_font = ImageFont.truetype("arial.ttf", 14)
508
509
# Layout parameters
510
margin = 40
511
line_spacing = 10
512
current_y = margin
513
514
# Draw title
515
draw.text((width // 2, current_y), title, font=title_font,
516
fill="black", anchor="mt")
517
title_bbox = title_font.getbbox(title)
518
current_y += (title_bbox[3] - title_bbox[1]) + line_spacing * 2
519
520
# Draw subtitle
521
draw.text((width // 2, current_y), subtitle, font=subtitle_font,
522
fill="blue", anchor="mt")
523
subtitle_bbox = subtitle_font.getbbox(subtitle)
524
current_y += (subtitle_bbox[3] - subtitle_bbox[1]) + line_spacing * 3
525
526
# Draw body text (wrapped)
527
words = body.split()
528
lines = []
529
current_line = []
530
531
for word in words:
532
test_line = " ".join(current_line + [word])
533
test_width = body_font.getlength(test_line)
534
535
if test_width <= width - 2 * margin:
536
current_line.append(word)
537
else:
538
if current_line:
539
lines.append(" ".join(current_line))
540
current_line = [word]
541
else:
542
lines.append(word) # Single word longer than line
543
544
if current_line:
545
lines.append(" ".join(current_line))
546
547
# Draw wrapped text
548
for line in lines:
549
draw.text((margin, current_y), line, font=body_font, fill="black")
550
line_bbox = body_font.getbbox(line)
551
current_y += (line_bbox[3] - line_bbox[1]) + line_spacing
552
553
return img
554
555
# Create professional layout
556
title = "Professional Document"
557
subtitle = "Advanced Typography with Pillow"
558
body = """This is an example of professional text layout using Python's Pillow library.
559
The text is properly wrapped, spaced, and formatted to create a clean, readable document.
560
Multiple font sizes and colors are used to create visual hierarchy."""
561
562
layout = create_text_layout(title, subtitle, body)
563
layout.save("professional_layout.png")
564
```