Diagnosing Cairo Vector-to-Raster Fallback in PDF and PostScript Output
A diagnostic guide for Cairo's silent vector-to-raster fallback in PDF/PostScript output: detect pixelation and file-size bloat, use CAIRO_DEBUG=fallback to trace causes, and apply fixes—remove translucent groups, avoid mesh gradients, or raise fallback resolution—before escalating.
14 Apr 2026, 07:17 UTC

The Problem
When Cairo renders a PDF or PostScript file, it may silently rasterize vector content that should remain scalable. The resulting file shows pixelation when zoomed and can be much larger than expected. This happens because the PDF/PS backends cannot represent some drawing operations natively, so Cairo falls back to rasterizing at a configurable resolution.
Recognizable Condition
- Zooming a PDF beyond 200% reveals jagged edges where vector graphics should stay sharp.
- `pdfimages -list output.pdf` lists image objects for content that should be vector.
- File size is disproportionately large for the visual complexity.
- No explicit error or warning is emitted during rendering.
Cause & Diagnostic Table
| Trigger | Why It Forces Fallback | Typical Code Pattern |
|---|---|---|
| Translucent patterns or gradients | PDF 1.4 supports transparency groups, but Cairo’s PDF backend cannot emit all pattern types when alpha < 1.0. | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT) with alpha stops |
| Mask or group with alpha < 1.0 | Creating a group with CAIRO_CONTENT_COLOR_ALPHA often cannot be replayed as vector. |
cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR_ALPHA); … cairo_pop_group_to_source(cr); cairo_paint_with_alpha(cr, 0.5); |
| Unsupported path operations (mesh gradients, Coons patches) | No vector representation exists in PDF/PS for these constructs. | cairo_mesh_pattern_begin_patch() / cairo_mesh_pattern_end_patch() |
Backend limitation: cairo-pdf / cairo-ps |
Fallback resolution defaults to 300 dpi; any forced rasterization uses this DPI. | Any drawing after the above triggers |
cairo_set_source_surface() with pattern extending beyond surface bounds |
EXTEND_REPEAT/REFLECT on a surface source forces rasterization of the tile. | cairo_pattern_set_extend(surface_pattern, CAIRO_EXTEND_REPEAT) |
Ordered Diagnostic Checks
-
Enable fallback logging. Run your program with:
This requires a Cairo build compiled withCAIRO_DEBUG=fallback ./your_program--enable-debug. Look for lines likefallback: …on stderr to identify the exact operation and surface. -
Verify surface creation. Confirm you are using
cairo_pdf_surface_create()orcairo_pdf_surface_create_for_stream()(not an image surface wrapped incorrectly). Check thatcairo_surface_get_type()returnsCAIRO_SURFACE_TYPE_PDFbefore drawing. -
Inspect source surfaces and patterns. Search code for
cairo_set_source_surface(),cairo_mask(),cairo_paint_with_alpha(), or anycairo_push_group*calls. Each is a potential fallback candidate. -
Check fallback resolution setting. Call
cairo_pdf_surface_set_fallback_resolution(surface, 72)temporarily (before any drawing on a page) to see if file size drops dramatically—this indicates rasterization is the culprit. -
List embedded images. Run:
Vector-only pages should report zero images. Any listed image with non-zero dimensions on a page that should be pure vector confirms fallback.pdfimages -list output.pdf
Fixes Tied to Findings
1. Replace translucent patterns with opaque equivalents
If a linear/radial gradient uses alpha stops (cairo_pattern_add_color_stop_rgba() with alpha < 1.0), rewrite it as opaque stops over a solid background, or split into two drawing passes: background first, then opaque foreground.
2. Avoid group opacity; draw directly to target
Instead of:
cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR_ALPHA);
// draw complex shape
cairo_pop_group_to_source(cr);
cairo_paint_with_alpha(cr, 0.5);
Draw the shape directly with an alpha source:
cairo_set_source_rgba(cr, r, g, b, 0.5);
// draw shape
cairo_fill(cr);
If group compositing is needed, accept rasterization for that group only, or render the group to a high‑resolution image surface and embed it at a reduced scale.
3. Decompose mesh gradients or accept rasterization
Mesh gradients (cairo_mesh_pattern_*) have no vector representation in PDF/PS. Options:
- Approximate with a series of solid‑color patches (triangulate the mesh).
- Render the mesh to a high‑DPI image surface and embed as an image (set fallback resolution to 1200 dpi for print).
4. Increase fallback resolution for print quality
If fallback is unavoidable, raise the rasterization DPI before drawing on each page:
cairo_pdf_surface_set_fallback_resolution(surface, 1200);
Call this immediately after surface creation or cairo_show_page() before any drawing on the new page. It affects only subsequent operations on that page.
5. Use recording surfaces to isolate problematic operations
A recording surface (cairo_recording_surface_create()) captures drawing commands without rendering. Replay it onto your PDF surface:
cairo_surface_t *rec = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL);
cairo_t *rec_cr = cairo_create(rec);
// draw problematic content here
cairo_surface_flush(rec);
cairo_t *pdf_cr = cairo_create(pdf_surface);
cairo_set_source_surface(pdf_cr, rec, 0, 0);
cairo_paint(pdf_cr);
Note: Recording surfaces defer but do not avoid fallback. The decision occurs at replay time onto the PDF/PS surface. This technique helps isolate which commands trigger fallback, not eliminate it.
Limitations and Risks
- File size vs. quality trade‑off: Raising fallback resolution to 1200 dpi can increase output size 16× over 300 dpi for rasterized regions. Monitor with
pdfimages -listand file size checks. - Debug logging availability:
CAIRO_DEBUG=fallbackworks only in debug‑enabled builds. If your production Cairo lacks it, reproduce in a debug build or add temporaryfprintfhooks viacairo_debug_reset_static_data(). - Font subsetting masquerading as fallback: Missing glyphs in embedded subsets can cause PDF viewers to render fallback bitmaps. Verify with
pdffonts output.pdf—all fonts should show “Embedded: yes” and “Subset: yes” with no “Type: Type3” (which indicates raster fallback). - No rollback for fallback resolution:
cairo_pdf_surface_set_fallback_resolution()is a one‑way setting per page. If you set it too high, recreate the surface or start a new page.
Verification Steps
- Create a minimal test case: draw a transparent gradient rectangle on a PDF surface, run with
CAIRO_DEBUG=fallback, confirm fallback messages appear. - Run
pdfimages -list output.pdf—vector content should yield zero images. - Open in a PDF viewer, zoom to 800%. Vector edges remain sharp; raster shows visible pixels.
- Compare file sizes: render the same page with fallback‑resolution 72 dpi vs 300 dpi vs 1200 dpi. Expect roughly linear size scaling with DPI for rasterized content.
Escalation Criteria
If you have eliminated all alpha groups, translucent patterns, mesh gradients, and extended surface patterns, yet fallback persists:
- Produce a minimal reproduction: a single
.cfile (≈50 lines) using only public Cairo API that demonstrates the unexpected fallback. - File a bug at the Cairo GitLab issue tracker with the reproduction, Cairo version (
cairo_version_string()), and backend (cairo_surface_get_type()). - Engage the Cairo mailing list for backend‑specific limitations (e.g., PDF 1.4 transparency group support gaps, PostScript Level 3 constraints).
Quick Reference: Key API Calls
| Function | Purpose | When to Call |
|---|---|---|
cairo_pdf_surface_set_fallback_resolution(surface, dpi) |
Set rasterization DPI for forced fallbacks | Immediately after surface creation or cairo_show_page(), before any drawing |
cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR_ALPHA) |
Create intermediate group (often triggers fallback) | Avoid unless necessary; prefer direct drawing with alpha source |
cairo_debug_reset_static_data() |
Reset internal debug state | Before enabling CAIRO_DEBUG=fallback in long‑running processes |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.