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
```