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

surfaces.mddocs/

0

# Surfaces

1

2

Surface classes provide the rendering targets for Cairo drawing operations. Each surface type represents a different output format or backend, from in-memory image buffers to vector formats like PDF and SVG. Surfaces manage the destination for all drawing operations performed through a Context.

3

4

## Capabilities

5

6

### Base Surface Class

7

8

```python { .api }

9

class Surface:

10

def finish(self) -> None:

11

"""Do any pending drawing and free resources used by surface."""

12

13

def flush(self) -> None:

14

"""Do any pending drawing for the surface."""

15

16

def get_content(self) -> Content:

17

"""Get content type of surface (COLOR, ALPHA, or COLOR_ALPHA)."""

18

19

def get_device(self) -> Device:

20

"""Get device associated with surface."""

21

22

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

23

"""Retrieve default font rendering options for surface."""

24

25

def mark_dirty(self) -> None:

26

"""Tell cairo that drawing has been done to surface."""

27

28

def mark_dirty_rectangle(self, x: int, y: int, width: int, height: int) -> None:

29

"""Mark a rectangular region as dirty."""

30

31

def set_device_scale(self, x_scale: float, y_scale: float) -> None:

32

"""Set device scale factors."""

33

34

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

35

"""Get device scale factors."""

36

37

def set_device_offset(self, x_offset: float, y_offset: float) -> None:

38

"""Set device offset."""

39

40

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

41

"""Get device offset."""

42

43

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

44

"""Get fallback resolution in pixels per inch."""

45

46

def set_fallback_resolution(self, x_pixels_per_inch: float, y_pixels_per_inch: float) -> None:

47

"""Set fallback resolution."""

48

49

def copy_page(self) -> None:

50

"""Emit current page for backends that support multiple pages."""

51

52

def show_page(self) -> None:

53

"""Emit and clear current page for backends that support multiple pages."""

54

55

def has_show_text_glyphs(self) -> bool:

56

"""Check if surface can render text and glyphs simultaneously."""

57

58

def set_mime_data(self, mime_type: str, data: bytes) -> None:

59

"""Attach MIME data to surface."""

60

61

def get_mime_data(self, mime_type: str) -> bytes:

62

"""Get MIME data previously attached to surface."""

63

64

def supports_mime_type(self, mime_type: str) -> bool:

65

"""Check if surface supports given MIME type."""

66

67

def create_for_rectangle(self, x: float, y: float, width: float, height: float) -> Surface:

68

"""Create sub-surface from rectangular region of this surface."""

69

70

def create_similar_image(self, format: Format, width: int, height: int) -> ImageSurface:

71

"""Create similar image surface with specified format and dimensions."""

72

73

def map_to_image(self, extents: Rectangle | None = None) -> ImageSurface:

74

"""Map surface or region to image surface for direct pixel access."""

75

76

def unmap_image(self, image: ImageSurface) -> None:

77

"""Unmap previously mapped image surface."""

78

79

def __enter__(self) -> Surface:

80

"""Enter context manager."""

81

82

def __exit__(self, exc_type, exc_val, exc_tb) -> None:

83

"""Exit context manager and finish surface."""

84

```

85

86

### Image Surface

87

88

```python { .api }

89

class ImageSurface(Surface):

90

def __init__(self, format: Format, width: int, height: int) -> None:

91

"""Create image surface with specified format and dimensions.

92

93

Args:

94

format: Pixel format (FORMAT_ARGB32, FORMAT_RGB24, etc.)

95

width: Width in pixels

96

height: Height in pixels

97

"""

98

99

@classmethod

100

def create_from_png(cls, filename: str) -> ImageSurface:

101

"""Create image surface from PNG file."""

102

103

@classmethod

104

def create_from_png_stream(cls, file: BinaryIO) -> ImageSurface:

105

"""Create image surface from PNG data stream."""

106

107

@classmethod

108

def create_for_data(cls, data: memoryview, format: Format, width: int, height: int, stride: int) -> ImageSurface:

109

"""Create image surface using provided data buffer."""

110

111

def write_to_png(self, filename: str) -> None:

112

"""Write surface contents to PNG file."""

113

114

def write_to_png_stream(self, file: BinaryIO) -> None:

115

"""Write surface contents to PNG data stream."""

116

117

def get_data(self) -> memoryview:

118

"""Get raw pixel data as memory view."""

119

120

def get_format(self) -> Format:

121

"""Get pixel format of surface."""

122

123

def get_width(self) -> int:

124

"""Get width in pixels."""

125

126

def get_height(self) -> int:

127

"""Get height in pixels."""

128

129

def get_stride(self) -> int:

130

"""Get stride (bytes per row) of surface."""

131

132

@staticmethod

133

def format_stride_for_width(format: Format, width: int) -> int:

134

"""Calculate stride for given format and width."""

135

```

136

137

### PDF Surface

138

139

```python { .api }

140

class PDFSurface(Surface):

141

def __init__(self, filename: str, width_in_points: float, height_in_points: float) -> None:

142

"""Create PDF surface writing to file.

143

144

Args:

145

filename: Output PDF filename

146

width_in_points: Page width in points (1/72 inch)

147

height_in_points: Page height in points (1/72 inch)

148

"""

149

150

@classmethod

151

def create_for_stream(cls, file: BinaryIO, width_in_points: float, height_in_points: float) -> PDFSurface:

152

"""Create PDF surface writing to stream."""

153

154

def restrict_to_version(self, version: PDFVersion) -> None:

155

"""Restrict output to specific PDF version."""

156

157

@staticmethod

158

def get_versions() -> list[PDFVersion]:

159

"""Get list of supported PDF versions."""

160

161

@staticmethod

162

def version_to_string(version: PDFVersion) -> str:

163

"""Get string representation of PDF version."""

164

165

def set_size(self, width_in_points: float, height_in_points: float) -> None:

166

"""Change size of PDF surface for subsequent pages."""

167

168

def add_outline(self, parent_id: int, text: str, link_attribs: str, flags: PDFOutlineFlags) -> int:

169

"""Add item to PDF outline (bookmarks).

170

171

Args:

172

parent_id: Parent outline item ID (use PDF_OUTLINE_ROOT for root)

173

text: Display text for outline item

174

link_attribs: Link attributes string

175

flags: Outline item flags

176

177

Returns:

178

ID of created outline item

179

"""

180

181

def set_metadata(self, metadata: PDFMetadata, value: str) -> None:

182

"""Set PDF metadata."""

183

184

def set_page_label(self, label: str) -> None:

185

"""Set label for current page."""

186

187

def set_thumbnail_size(self, width: int, height: int) -> None:

188

"""Set thumbnail image size for pages."""

189

190

def set_custom_metadata(self, name: str, value: str) -> None:

191

"""Set custom metadata field."""

192

```

193

194

### PostScript Surface

195

196

```python { .api }

197

class PSSurface(Surface):

198

def __init__(self, filename: str, width_in_points: float, height_in_points: float) -> None:

199

"""Create PostScript surface writing to file.

200

201

Args:

202

filename: Output PostScript filename

203

width_in_points: Page width in points

204

height_in_points: Page height in points

205

"""

206

207

@classmethod

208

def create_for_stream(cls, file: BinaryIO, width_in_points: float, height_in_points: float) -> PSSurface:

209

"""Create PostScript surface writing to stream."""

210

211

def restrict_to_level(self, level: PSLevel) -> None:

212

"""Restrict output to specific PostScript level."""

213

214

@staticmethod

215

def get_levels() -> list[PSLevel]:

216

"""Get list of supported PostScript levels."""

217

218

@staticmethod

219

def level_to_string(level: PSLevel) -> str:

220

"""Get string representation of PostScript level."""

221

222

def set_eps(self, eps: bool) -> None:

223

"""Enable/disable Encapsulated PostScript mode."""

224

225

def get_eps(self) -> bool:

226

"""Check if EPS mode is enabled."""

227

228

def set_size(self, width_in_points: float, height_in_points: float) -> None:

229

"""Change size for subsequent pages."""

230

231

def dsc_comment(self, comment: str) -> None:

232

"""Emit DSC comment to PostScript output."""

233

234

def dsc_begin_setup(self) -> None:

235

"""Begin setup section in DSC-compliant PostScript."""

236

237

def dsc_begin_page_setup(self) -> None:

238

"""Begin page setup section."""

239

```

240

241

### SVG Surface

242

243

```python { .api }

244

class SVGSurface(Surface):

245

def __init__(self, filename: str, width_in_points: float, height_in_points: float) -> None:

246

"""Create SVG surface writing to file.

247

248

Args:

249

filename: Output SVG filename

250

width_in_points: Surface width in points

251

height_in_points: Surface height in points

252

"""

253

254

@classmethod

255

def create_for_stream(cls, file: BinaryIO, width_in_points: float, height_in_points: float) -> SVGSurface:

256

"""Create SVG surface writing to stream."""

257

258

def restrict_to_version(self, version: SVGVersion) -> None:

259

"""Restrict output to specific SVG version."""

260

261

@staticmethod

262

def get_versions() -> list[SVGVersion]:

263

"""Get list of supported SVG versions."""

264

265

@staticmethod

266

def version_to_string(version: SVGVersion) -> str:

267

"""Get string representation of SVG version."""

268

269

def set_document_unit(self, unit: SVGUnit) -> None:

270

"""Set unit used for SVG surface."""

271

272

def get_document_unit(self) -> SVGUnit:

273

"""Get unit used for SVG surface."""

274

```

275

276

### Recording Surface

277

278

```python { .api }

279

class RecordingSurface(Surface):

280

def __init__(self, content: Content, extents: Rectangle | None = None) -> None:

281

"""Create recording surface for caching drawing operations.

282

283

Args:

284

content: Content type (COLOR, ALPHA, or COLOR_ALPHA)

285

extents: Recording extents or None for unbounded

286

"""

287

288

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

289

"""Get bounding box of recorded operations."""

290

291

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

292

"""Get recording extents."""

293

```

294

295

### Other Surface Types

296

297

```python { .api }

298

class Win32Surface(Surface):

299

def __init__(self, hdc: int) -> None:

300

"""Create surface targeting Windows HDC."""

301

302

class Win32PrintingSurface(Surface):

303

def __init__(self, hdc: int) -> None:

304

"""Create surface for Windows printing."""

305

306

class XCBSurface(Surface):

307

pass # Platform-specific initialization

308

309

class XlibSurface(Surface):

310

pass # Platform-specific initialization

311

312

class TeeSurface(Surface):

313

def __init__(self, primary: Surface) -> None:

314

"""Create tee surface directing to primary surface."""

315

316

def add(self, target: Surface) -> None:

317

"""Add additional target surface."""

318

319

def remove(self, target: Surface) -> None:

320

"""Remove target surface."""

321

322

def index(self, index: int) -> Surface:

323

"""Get surface at given index."""

324

325

class ScriptSurface(Surface):

326

def __init__(self, device: ScriptDevice, content: Content, width: float, height: float) -> None:

327

"""Create surface for Cairo script recording."""

328

```

329

330

### Device Classes

331

332

```python { .api }

333

class Device:

334

def __init__(self) -> None:

335

"""Base device class (typically not instantiated directly)."""

336

337

def finish(self) -> None:

338

"""Finish device and free associated resources."""

339

340

def flush(self) -> None:

341

"""Flush pending operations on device."""

342

343

def get_type(self) -> DeviceType:

344

"""Get device type."""

345

346

class ScriptDevice(Device):

347

def __init__(self, filename: str) -> None:

348

"""Create script recording device writing to file.

349

350

Args:

351

filename: Output script filename

352

"""

353

354

@classmethod

355

def create_for_stream(cls, file: BinaryIO) -> ScriptDevice:

356

"""Create script device writing to stream."""

357

358

def set_mode(self, mode: ScriptMode) -> None:

359

"""Set script recording mode (ASCII or BINARY)."""

360

361

def get_mode(self) -> ScriptMode:

362

"""Get current script recording mode."""

363

364

def write_comment(self, comment: str) -> None:

365

"""Write comment to script output."""

366

367

def from_recording_surface(self, recording_surface: RecordingSurface) -> None:

368

"""Replay recording surface to script device."""

369

```

370

371

## Usage Examples

372

373

### Creating Different Surface Types

374

375

```python

376

import cairo

377

378

# Image surface (in-memory bitmap)

379

image_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 800, 600)

380

381

# PDF surface

382

pdf_surface = cairo.PDFSurface("output.pdf", 612, 792) # US Letter size

383

384

# SVG surface

385

svg_surface = cairo.SVGSurface("output.svg", 400, 300)

386

387

# PostScript surface

388

ps_surface = cairo.PSSurface("output.ps", 612, 792)

389

390

# Recording surface (unbounded)

391

recording_surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None)

392

```

393

394

### Working with Image Surfaces

395

396

```python

397

import cairo

398

399

# Create image surface

400

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

401

ctx = cairo.Context(surface)

402

403

# Draw something

404

ctx.set_source_rgb(0.8, 0.2, 0.2)

405

ctx.rectangle(50, 50, 100, 80)

406

ctx.fill()

407

408

# Save to PNG

409

surface.write_to_png("output.png")

410

411

# Access raw pixel data

412

data = surface.get_data()

413

width = surface.get_width()

414

height = surface.get_height()

415

stride = surface.get_stride()

416

format = surface.get_format()

417

418

print(f"Surface: {width}x{height}, format: {format}, stride: {stride}")

419

```

420

421

### Multi-page PDF Document

422

423

```python

424

import cairo

425

426

# Create PDF surface

427

surface = cairo.PDFSurface("document.pdf", 612, 792) # US Letter

428

ctx = cairo.Context(surface)

429

430

# Configure PDF metadata

431

surface.set_metadata(cairo.PDF_METADATA_TITLE, "My Document")

432

surface.set_metadata(cairo.PDF_METADATA_AUTHOR, "Author Name")

433

434

# Page 1

435

ctx.set_source_rgb(0, 0, 0)

436

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

437

ctx.set_font_size(24)

438

ctx.move_to(50, 100)

439

ctx.show_text("Page 1")

440

441

# Add outline entry

442

outline_id = surface.add_outline(cairo.PDF_OUTLINE_ROOT, "Chapter 1", "page=1", 0)

443

444

surface.show_page()

445

446

# Page 2

447

ctx.move_to(50, 100)

448

ctx.show_text("Page 2")

449

surface.show_page()

450

451

surface.finish()

452

```

453

454

### Recording and Playback

455

456

```python

457

import cairo

458

459

# Create recording surface

460

recording = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None)

461

ctx = cairo.Context(recording)

462

463

# Record some operations

464

ctx.set_source_rgb(0.2, 0.4, 0.8)

465

ctx.rectangle(10, 10, 80, 60)

466

ctx.fill()

467

468

ctx.set_source_rgb(0.8, 0.4, 0.2)

469

ctx.arc(50, 40, 20, 0, 2 * 3.14159)

470

ctx.fill()

471

472

# Get recorded extents

473

x, y, width, height = recording.ink_extents()

474

print(f"Recorded operations cover: {x}, {y}, {width}, {height}")

475

476

# Play back to image surface

477

image = cairo.ImageSurface(cairo.FORMAT_ARGB32, 200, 150)

478

ctx2 = cairo.Context(image)

479

ctx2.set_source_surface(recording)

480

ctx2.paint()

481

482

image.write_to_png("playback.png")

483

```

484

485

### Stream-based Output

486

487

```python

488

import cairo

489

import io

490

491

# Create PDF in memory

492

pdf_buffer = io.BytesIO()

493

surface = cairo.PDFSurface.create_for_stream(pdf_buffer, 400, 300)

494

ctx = cairo.Context(surface)

495

496

# Draw content

497

ctx.set_source_rgb(0.8, 0.2, 0.2)

498

ctx.rectangle(50, 50, 100, 80)

499

ctx.fill()

500

501

surface.finish()

502

503

# Get PDF data

504

pdf_data = pdf_buffer.getvalue()

505

print(f"Generated PDF size: {len(pdf_data)} bytes")

506

507

# Write to file

508

with open("output_from_stream.pdf", "wb") as f:

509

f.write(pdf_data)

510

```