or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

constants-enums.mddrawing-context.mdgeometry.mdindex.mdpatterns.mdsurfaces.mdtext-fonts.md

text-fonts.mddocs/

0

# Text and Fonts

1

2

Cairo provides comprehensive text rendering capabilities with support for multiple font backends, advanced typography features, and precise text measurement. The text system includes font selection, rendering options, glyph-level control, and metrics calculation for professional typography and layout applications.

3

4

## Capabilities

5

6

### Font Face Management

7

8

```python { .api }

9

class FontFace:

10

def __init__(self) -> None:

11

"""Base font face class (typically not instantiated directly)."""

12

13

def get_type(self) -> FontType:

14

"""Get font face type (TOY, FT, WIN32, QUARTZ, USER, DWRITE)."""

15

16

class ToyFontFace(FontFace):

17

def __init__(self, family: str, slant: FontSlant = FontSlant.NORMAL, weight: FontWeight = FontWeight.NORMAL) -> None:

18

"""Create simple font face from family name.

19

20

Args:

21

family: Font family name (e.g., "Arial", "Times New Roman")

22

slant: Font slant (NORMAL, ITALIC, OBLIQUE)

23

weight: Font weight (NORMAL, BOLD)

24

"""

25

26

def get_family(self) -> str:

27

"""Get font family name."""

28

29

def get_slant(self) -> FontSlant:

30

"""Get font slant."""

31

32

def get_weight(self) -> FontWeight:

33

"""Get font weight."""

34

```

35

36

### Font Rendering Options

37

38

```python { .api }

39

class FontOptions:

40

def __init__(self) -> None:

41

"""Create font options with default settings."""

42

43

def copy(self) -> FontOptions:

44

"""Create copy of font options."""

45

46

def merge(self, other: FontOptions) -> None:

47

"""Merge another font options into this one."""

48

49

def hash(self) -> int:

50

"""Get hash value for font options."""

51

52

def equal(self, other: FontOptions) -> bool:

53

"""Check equality with another font options."""

54

55

def set_antialias(self, antialias: Antialias) -> None:

56

"""Set antialiasing mode for font rendering."""

57

58

def get_antialias(self) -> Antialias:

59

"""Get antialiasing mode."""

60

61

def set_subpixel_order(self, subpixel_order: SubpixelOrder) -> None:

62

"""Set subpixel order for LCD text rendering."""

63

64

def get_subpixel_order(self) -> SubpixelOrder:

65

"""Get subpixel order."""

66

67

def set_hint_style(self, hint_style: HintStyle) -> None:

68

"""Set font hinting style."""

69

70

def get_hint_style(self) -> HintStyle:

71

"""Get font hinting style."""

72

73

def set_hint_metrics(self, hint_metrics: HintMetrics) -> None:

74

"""Set font metrics hinting."""

75

76

def get_hint_metrics(self) -> HintMetrics:

77

"""Get font metrics hinting."""

78

79

def set_variations(self, variations: str) -> None:

80

"""Set font variations string for variable fonts."""

81

82

def get_variations(self) -> str:

83

"""Get font variations string."""

84

85

def set_color_mode(self, color_mode: ColorMode) -> None:

86

"""Set color font rendering mode."""

87

88

def get_color_mode(self) -> ColorMode:

89

"""Get color font rendering mode."""

90

91

def set_color_palette(self, palette_index: int) -> None:

92

"""Set color palette index for color fonts."""

93

94

def get_color_palette(self) -> int:

95

"""Get color palette index."""

96

97

def set_custom_palette_color(self, index: int, red: float, green: float, blue: float, alpha: float) -> None:

98

"""Set custom color for palette entry."""

99

100

def get_custom_palette_color(self, index: int) -> tuple[float, float, float, float]:

101

"""Get custom color for palette entry."""

102

```

103

104

### Scaled Fonts

105

106

```python { .api }

107

class ScaledFont:

108

def __init__(self, font_face: FontFace, font_matrix: Matrix, ctm: Matrix, options: FontOptions) -> None:

109

"""Create scaled font from font face and matrices.

110

111

Args:

112

font_face: Font face to scale

113

font_matrix: Font transformation matrix

114

ctm: Current transformation matrix

115

options: Font rendering options

116

"""

117

118

def get_type(self) -> FontType:

119

"""Get scaled font type."""

120

121

def get_reference_count(self) -> int:

122

"""Get reference count."""

123

124

def get_font_face(self) -> FontFace:

125

"""Get underlying font face."""

126

127

def get_font_options(self) -> FontOptions:

128

"""Get font options."""

129

130

def get_font_matrix(self) -> Matrix:

131

"""Get font transformation matrix."""

132

133

def get_ctm(self) -> Matrix:

134

"""Get current transformation matrix."""

135

136

def get_scale_matrix(self) -> Matrix:

137

"""Get combined scale matrix."""

138

139

def extents(self) -> tuple[float, float, float, float, float]:

140

"""Get font extents (ascent, descent, height, max_x_advance, max_y_advance)."""

141

142

def text_extents(self, text: str) -> TextExtents:

143

"""Get text extents for given string."""

144

145

def glyph_extents(self, glyphs: list[Glyph]) -> TextExtents:

146

"""Get glyph extents for glyph array."""

147

148

def text_to_glyphs(self, x: float, y: float, text: str) -> tuple[list[Glyph], list[TextCluster]]:

149

"""Convert text to glyphs with cluster information."""

150

151

def get_color_palette(self, palette_index: int) -> list[tuple[float, float, float, float]]:

152

"""Get color palette for color fonts."""

153

154

def get_type(self) -> FontType:

155

"""Get scaled font type."""

156

157

def get_reference_count(self) -> int:

158

"""Get reference count for the scaled font."""

159

```

160

161

### Text Measurement Classes

162

163

```python { .api }

164

class TextExtents:

165

def __init__(self, x_bearing: float, y_bearing: float, width: float, height: float, x_advance: float, y_advance: float) -> None:

166

"""Text measurement data.

167

168

Args:

169

x_bearing: Horizontal distance from origin to leftmost part

170

y_bearing: Vertical distance from origin to topmost part

171

width: Width of text

172

height: Height of text

173

x_advance: Horizontal advance to next glyph position

174

y_advance: Vertical advance to next glyph position

175

"""

176

177

@property

178

def x_bearing(self) -> float:

179

"""X bearing value."""

180

181

@property

182

def y_bearing(self) -> float:

183

"""Y bearing value."""

184

185

@property

186

def width(self) -> float:

187

"""Text width."""

188

189

@property

190

def height(self) -> float:

191

"""Text height."""

192

193

@property

194

def x_advance(self) -> float:

195

"""X advance value."""

196

197

@property

198

def y_advance(self) -> float:

199

"""Y advance value."""

200

201

class Glyph:

202

def __init__(self, index: int, x: float, y: float) -> None:

203

"""Glyph positioning data.

204

205

Args:

206

index: Glyph index in font

207

x: X coordinate for glyph placement

208

y: Y coordinate for glyph placement

209

"""

210

211

@property

212

def index(self) -> int:

213

"""Glyph index."""

214

215

@property

216

def x(self) -> float:

217

"""X coordinate."""

218

219

@property

220

def y(self) -> float:

221

"""Y coordinate."""

222

223

class TextCluster:

224

def __init__(self, num_bytes: int, num_glyphs: int) -> None:

225

"""Text cluster mapping data.

226

227

Args:

228

num_bytes: Number of UTF-8 bytes in cluster

229

num_glyphs: Number of glyphs in cluster

230

"""

231

232

@property

233

def num_bytes(self) -> int:

234

"""Number of bytes."""

235

236

@property

237

def num_glyphs(self) -> int:

238

"""Number of glyphs."""

239

```

240

241

### Font Type Enumeration

242

243

```python { .api }

244

class FontType:

245

"""Font face implementation types."""

246

TOY: int = 0 # Simple toy font

247

FT: int = 1 # FreeType font

248

WIN32: int = 2 # Win32 font

249

QUARTZ: int = 3 # Quartz font

250

USER: int = 4 # User font

251

DWRITE: int = 5 # DirectWrite font

252

```

253

254

## Context Text Methods

255

256

Text rendering methods available on Context objects:

257

258

```python { .api }

259

# Font selection and configuration

260

def select_font_face(self, family: str, slant: FontSlant, weight: FontWeight) -> None:

261

"""Select font face by family name and style."""

262

263

def set_font_size(self, size: float) -> None:

264

"""Set font size in user units."""

265

266

def set_font_matrix(self, matrix: Matrix) -> None:

267

"""Set font transformation matrix."""

268

269

def get_font_matrix(self) -> Matrix:

270

"""Get font transformation matrix."""

271

272

def set_font_options(self, options: FontOptions) -> None:

273

"""Set font rendering options."""

274

275

def get_font_options(self) -> FontOptions:

276

"""Get font rendering options."""

277

278

def set_font_face(self, font_face: FontFace) -> None:

279

"""Set font face."""

280

281

def get_font_face(self) -> FontFace:

282

"""Get current font face."""

283

284

def set_scaled_font(self, scaled_font: ScaledFont) -> None:

285

"""Set scaled font."""

286

287

def get_scaled_font(self) -> ScaledFont:

288

"""Get current scaled font."""

289

290

# Text rendering

291

def show_text(self, text: str) -> None:

292

"""Render text at current point."""

293

294

def show_glyphs(self, glyphs: list[Glyph]) -> None:

295

"""Render glyphs at specified positions."""

296

297

def show_text_glyphs(self, text: str, glyphs: list[Glyph], clusters: list[TextCluster], cluster_flags: TextClusterFlags) -> None:

298

"""Render text with glyph and cluster information."""

299

300

# Text measurement

301

def text_extents(self, text: str) -> TextExtents:

302

"""Get text extents using current font."""

303

304

def glyph_extents(self, glyphs: list[Glyph]) -> TextExtents:

305

"""Get glyph extents using current font."""

306

307

def font_extents(self) -> tuple[float, float, float, float, float]:

308

"""Get font extents (ascent, descent, height, max_x_advance, max_y_advance)."""

309

310

# Path operations

311

def text_path(self, text: str) -> None:

312

"""Add text outline to current path."""

313

314

def glyph_path(self, glyphs: list[Glyph]) -> None:

315

"""Add glyph outlines to current path."""

316

```

317

318

## Usage Examples

319

320

### Basic Text Rendering

321

322

```python

323

import cairo

324

325

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 300)

326

ctx = cairo.Context(surface)

327

328

# Set background

329

ctx.set_source_rgb(1, 1, 1)

330

ctx.paint()

331

332

# Basic text rendering

333

ctx.set_source_rgb(0, 0, 0)

334

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

335

ctx.set_font_size(24)

336

ctx.move_to(50, 50)

337

ctx.show_text("Hello, Cairo!")

338

339

# Different font styles

340

ctx.select_font_face("Arial", cairo.FONT_SLANT_ITALIC, cairo.FONT_WEIGHT_BOLD)

341

ctx.set_font_size(20)

342

ctx.move_to(50, 100)

343

ctx.show_text("Bold italic text")

344

345

# Different colors

346

ctx.set_source_rgb(0.8, 0.2, 0.2)

347

ctx.select_font_face("Times New Roman", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

348

ctx.set_font_size(18)

349

ctx.move_to(50, 150)

350

ctx.show_text("Red Times text")

351

352

surface.write_to_png("basic_text.png")

353

```

354

355

### Font Options and Hinting

356

357

```python

358

import cairo

359

360

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600, 400)

361

ctx = cairo.Context(surface)

362

363

ctx.set_source_rgb(1, 1, 1)

364

ctx.paint()

365

366

# Create different font options

367

font_options_none = cairo.FontOptions()

368

font_options_none.set_hint_style(cairo.HINT_STYLE_NONE)

369

370

font_options_slight = cairo.FontOptions()

371

font_options_slight.set_hint_style(cairo.HINT_STYLE_SLIGHT)

372

373

font_options_medium = cairo.FontOptions()

374

font_options_medium.set_hint_style(cairo.HINT_STYLE_MEDIUM)

375

376

font_options_full = cairo.FontOptions()

377

font_options_full.set_hint_style(cairo.HINT_STYLE_FULL)

378

379

# Render text with different hinting

380

ctx.set_source_rgb(0, 0, 0)

381

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

382

ctx.set_font_size(16)

383

384

y = 50

385

for label, options in [

386

("No hinting", font_options_none),

387

("Slight hinting", font_options_slight),

388

("Medium hinting", font_options_medium),

389

("Full hinting", font_options_full)

390

]:

391

ctx.set_font_options(options)

392

ctx.move_to(50, y)

393

ctx.show_text(f"{label}: The quick brown fox jumps")

394

y += 30

395

396

# Antialiasing options

397

y += 30

398

for antialias in [cairo.ANTIALIAS_NONE, cairo.ANTIALIAS_GRAY, cairo.ANTIALIAS_SUBPIXEL]:

399

font_options = cairo.FontOptions()

400

font_options.set_antialias(antialias)

401

ctx.set_font_options(font_options)

402

ctx.move_to(50, y)

403

ctx.show_text(f"Antialias {antialias}: Sample text")

404

y += 30

405

406

surface.write_to_png("font_options.png")

407

```

408

409

### Text Measurement and Layout

410

411

```python

412

import cairo

413

414

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 300)

415

ctx = cairo.Context(surface)

416

417

ctx.set_source_rgb(1, 1, 1)

418

ctx.paint()

419

420

# Set up font

421

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

422

ctx.set_font_size(24)

423

424

text = "Cairo Text"

425

426

# Get text extents

427

text_extents = ctx.text_extents(text)

428

font_ascent, font_descent, font_height, font_max_x_advance, font_max_y_advance = ctx.font_extents()

429

430

print(f"Text extents:")

431

print(f" x_bearing: {text_extents.x_bearing}")

432

print(f" y_bearing: {text_extents.y_bearing}")

433

print(f" width: {text_extents.width}")

434

print(f" height: {text_extents.height}")

435

print(f" x_advance: {text_extents.x_advance}")

436

print(f" y_advance: {text_extents.y_advance}")

437

438

print(f"Font extents:")

439

print(f" ascent: {font_ascent}")

440

print(f" descent: {font_descent}")

441

print(f" height: {font_height}")

442

443

# Position text

444

x, y = 100, 150

445

ctx.move_to(x, y)

446

447

# Draw text

448

ctx.set_source_rgb(0, 0, 0)

449

ctx.show_text(text)

450

451

# Draw text extents box

452

ctx.set_source_rgba(0.8, 0.2, 0.2, 0.5)

453

ctx.rectangle(x + text_extents.x_bearing, y + text_extents.y_bearing,

454

text_extents.width, text_extents.height)

455

ctx.fill()

456

457

# Draw baseline

458

ctx.set_source_rgb(0, 0.8, 0)

459

ctx.set_line_width(1)

460

ctx.move_to(x - 20, y)

461

ctx.line_to(x + text_extents.x_advance + 20, y)

462

ctx.stroke()

463

464

# Draw advance point

465

ctx.set_source_rgb(0, 0, 0.8)

466

ctx.arc(x + text_extents.x_advance, y, 3, 0, 2 * 3.14159)

467

ctx.fill()

468

469

# Right-aligned text using extents

470

right_text = "Right aligned"

471

right_extents = ctx.text_extents(right_text)

472

ctx.move_to(450 - right_extents.width, 200)

473

ctx.set_source_rgb(0.4, 0.4, 0.4)

474

ctx.show_text(right_text)

475

476

surface.write_to_png("text_measurement.png")

477

```

478

479

### Text Paths and Effects

480

481

```python

482

import cairo

483

484

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 300)

485

ctx = cairo.Context(surface)

486

487

ctx.set_source_rgb(1, 1, 1)

488

ctx.paint()

489

490

# Create text path

491

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)

492

ctx.set_font_size(48)

493

ctx.move_to(50, 100)

494

ctx.text_path("TEXT PATH")

495

496

# Fill with gradient

497

gradient = cairo.LinearGradient(50, 60, 400, 60)

498

gradient.add_color_stop_rgb(0, 1, 0, 0)

499

gradient.add_color_stop_rgb(0.5, 1, 1, 0)

500

gradient.add_color_stop_rgb(1, 0, 0, 1)

501

502

ctx.set_source(gradient)

503

ctx.fill_preserve()

504

505

# Stroke outline

506

ctx.set_source_rgb(0, 0, 0)

507

ctx.set_line_width(2)

508

ctx.stroke()

509

510

# Outlined text effect

511

ctx.move_to(50, 180)

512

ctx.text_path("OUTLINED")

513

514

# Fill

515

ctx.set_source_rgb(1, 1, 1)

516

ctx.fill_preserve()

517

518

# Stroke

519

ctx.set_source_rgb(0.2, 0.2, 0.8)

520

ctx.set_line_width(3)

521

ctx.stroke()

522

523

# Shadow effect

524

shadow_text = "SHADOW"

525

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)

526

ctx.set_font_size(36)

527

528

# Draw shadow

529

ctx.move_to(52, 242)

530

ctx.set_source_rgba(0, 0, 0, 0.5)

531

ctx.show_text(shadow_text)

532

533

# Draw main text

534

ctx.move_to(50, 240)

535

ctx.set_source_rgb(0.8, 0.2, 0.2)

536

ctx.show_text(shadow_text)

537

538

surface.write_to_png("text_effects.png")

539

```

540

541

### Advanced Font Features

542

543

```python

544

import cairo

545

546

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600, 400)

547

ctx = cairo.Context(surface)

548

549

ctx.set_source_rgb(1, 1, 1)

550

ctx.paint()

551

552

# Using ToyFontFace

553

toy_font = cairo.ToyFontFace("Georgia", cairo.FONT_SLANT_ITALIC, cairo.FONT_WEIGHT_BOLD)

554

ctx.set_font_face(toy_font)

555

ctx.set_font_size(24)

556

ctx.move_to(50, 50)

557

ctx.set_source_rgb(0, 0, 0)

558

ctx.show_text("ToyFontFace: Georgia Bold Italic")

559

560

# Font matrix transformation

561

matrix = cairo.Matrix()

562

matrix.scale(1.5, 0.8) # Stretch horizontally, compress vertically

563

matrix.rotate(0.1) # Slight rotation

564

565

ctx.set_font_matrix(matrix)

566

ctx.move_to(50, 100)

567

ctx.show_text("Transformed font matrix")

568

569

# Reset font matrix

570

ctx.set_font_matrix(cairo.Matrix())

571

572

# Working with ScaledFont

573

font_face = cairo.ToyFontFace("Arial")

574

font_matrix = cairo.Matrix()

575

font_matrix.scale(20, 20)

576

ctm = cairo.Matrix()

577

options = cairo.FontOptions()

578

579

scaled_font = cairo.ScaledFont(font_face, font_matrix, ctm, options)

580

ctx.set_scaled_font(scaled_font)

581

582

ctx.move_to(50, 150)

583

ctx.show_text("ScaledFont example")

584

585

# Get font extents from scaled font

586

font_extents = scaled_font.extents()

587

print(f"ScaledFont extents: ascent={font_extents[0]}, descent={font_extents[1]}")

588

589

# Text to glyphs conversion (if supported)

590

try:

591

text = "Hello"

592

glyphs, clusters = scaled_font.text_to_glyphs(50, 200, text)

593

594

# Render using glyphs directly

595

ctx.move_to(50, 200)

596

ctx.show_glyphs(glyphs)

597

598

print(f"Text '{text}' converted to {len(glyphs)} glyphs and {len(clusters)} clusters")

599

600

except cairo.Error as e:

601

print(f"Glyph conversion not supported: {e}")

602

ctx.move_to(50, 200)

603

ctx.show_text("Glyph conversion not available")

604

605

surface.write_to_png("advanced_fonts.png")

606

```

607

608

### Multi-line Text Layout

609

610

```python

611

import cairo

612

613

def draw_multiline_text(ctx, text, x, y, line_height):

614

"""Draw multi-line text with proper line spacing."""

615

lines = text.split('\n')

616

for i, line in enumerate(lines):

617

ctx.move_to(x, y + i * line_height)

618

ctx.show_text(line)

619

620

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 400)

621

ctx = cairo.Context(surface)

622

623

ctx.set_source_rgb(1, 1, 1)

624

ctx.paint()

625

626

# Multi-line text example

627

ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

628

ctx.set_font_size(16)

629

ctx.set_source_rgb(0, 0, 0)

630

631

multiline_text = """This is a multi-line text example.

632

Each line is drawn separately using

633

the line height calculated from font

634

extents to ensure proper spacing

635

between lines."""

636

637

# Get font metrics for line spacing

638

font_ascent, font_descent, font_height, _, _ = ctx.font_extents()

639

line_height = font_height + 2 # Small extra spacing

640

641

draw_multiline_text(ctx, multiline_text, 50, 50, line_height)

642

643

# Justified text example

644

ctx.set_font_size(14)

645

words = ["The", "quick", "brown", "fox", "jumps", "over", "lazy", "dog"]

646

line_width = 300

647

x_start = 50

648

y_start = 200

649

650

# Calculate word spacing for justification

651

total_word_width = sum(ctx.text_extents(word).width for word in words)

652

space_width = ctx.text_extents(" ").width

653

available_space = line_width - total_word_width

654

extra_space_per_gap = available_space / (len(words) - 1) if len(words) > 1 else 0

655

656

# Draw justified text

657

x = x_start

658

for i, word in enumerate(words):

659

ctx.move_to(x, y_start)

660

ctx.show_text(word)

661

662

word_width = ctx.text_extents(word).width

663

x += word_width

664

665

if i < len(words) - 1: # Not the last word

666

x += space_width + extra_space_per_gap

667

668

# Draw bounding box for justified text

669

ctx.set_source_rgba(0.8, 0.2, 0.2, 0.3)

670

ctx.rectangle(x_start, y_start - font_ascent, line_width, font_height)

671

ctx.fill()

672

673

surface.write_to_png("multiline_text.png")

674

```