Diagnosing Cairo PDF Unicode Text Rendering Failures
When Unicode characters vanish from Cairo PDFs, font embedding, weight mismatches, or text matrix scaling are usually the culprits. Follow this step‑by‑step diagnostic guide to restore visible text.
15 Jun 2026, 08:20 UTC

Problem and takeaway
When a Cairo PDF surface fails to render Unicode characters, the result is often blank space, tofu boxes, or a missing‑glyph rectangle. This diagnostic guide systematically identifies whether the cause is font embedding status, weight/style mismatches, user‑space text matrix scaling, or Pango font‑map DPI settings. The useful takeaway: by checking these four concrete axes in order, you can pinpoint the root cause without guessing.
Diagnostic table
| Condition | Likely cause |
|---|---|
| Characters render as empty boxes or tofu | Font lacks the glyph; Cairo falls back to a default font |
| Emoji or CJK characters disappear entirely | Font is not embedded or has restricted licensing |
| Text appears vanishingly small or uncomfortably large | User‑space scaling exceeds or falls below the 1000‑unit text matrix range |
| Font weight feels wrong after selection | Family, style, weight matrix doesn’t match the PDF’s embedded font |
Ordered checks
- Verify font embedding. Open the generated PDF in a viewer that displays the font dictionary (e.g.,
pdftkorpdfinfo). The embedded font name should match the family you passed tocairo.select_font_face. If the name is absent, the font wasn’t embedded. Ensure the font file path is accessible and has read permissions for the Cairo process. - Confirm font weight and style match. Cairo treats
normal,bold, anditalicas distinct faces. If you selectcairo.FontWeight.Normalbut the PDF was generated with aBoldfont, the glyph lookup fails. Double‑check theFontSlantandFontWeightvalues used during text creation. - Check the 1000‑unit text matrix. Cairo’s PDF surface uses a text matrix with 1000 units per text‑space unit. If your user‑space transformation scales text beyond this range (e.g., a huge
scale()call), the rendered text may fall outside the visible page bounds or become invisible. Usecairo_text_extents()to inspect the computed width and height; extents outside the0–1000interval signal a scaling issue. - Validate Pango font‑map DPI. When using
pango_cairo_create_font_map, the map’s resolution defaults to the display’s DPI. If you’re generating PDFs for print at 300 dpi but the map assumes 72 dpi, text size will shift. Compare text extents before and after font map creation; a large delta indicates a DPI mismatch. - Confirm PDF version compatibility. Older Cairo PDF versions (e.g., 1.3) ignore some Unicode features. Use
cairo_pdf_surface_set_versionbefore drawing, then verify withcairo_pdf_surface_get_version. If the version is too low, Unicode characters outside the BMP may not be handled.
Fixes tied to findings
- Embedding is missing. Ensure the font file is readable by the Cairo process and pass it through Pango’s font map or set
cairo_font_options_taccordingly. For PDF/A compliance, the font must be embedded with a permissive license; otherwise PDF/A‑1a will reject the file. - Weight/style mismatch. Align the
FontWeight/FontSlantvalues between your Cairo call and the font file’s metadata. Usecairo_font_face_get_typeto inspect the selected face and adjust accordingly. - Text matrix scaling. Reduce the user‑space scale factor or normalize the text matrix with
cairo_scale()applied aftercairo_set_text_matrix(), keeping the overall matrix within the 1000‑unit envelope. - DPI mismatch. Set an explicit DPI on the font map with
pango_font_map_set_resolution()before creating the Cairo context, matching the target output device’s resolution. - PDF version too low. Upgrade to 1.7 or later and re‑embed the font.
Escalation criteria
- The PDF conforms to PDF/A‑1a and the font license prohibits embedding; you cannot embed it legally. Consider a different font or obtain an embedding‑allowed version.
- None of the above checks reveal the issue. Capture a minimal reproducer PDF and share it with the Cairo mailing list, including the
cairo_text_extentsoutput for the missing character and the font options used. - The problem appears only on specific OS/font combinations. Isolate the variable (OS font cache, FreeType version) before escalating.
Concrete example (illustrative)
import cairo
# Create a PDF surface – replace '/path/to/font.ttf' with a real font file
surface = cairo.PDFSurface('output.pdf', 612, 792)
ctx = cairo.Context(surface)
# Set PDF version that supports Unicode features
cairo.pdf_surface_set_version(surface, 1.7)
# Select a font family; ensure the .ttf is readable and contains the glyphs
ctx.select_font_face('/path/to/font.ttf',
cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
# Set a large font size – note the 1000‑unit text matrix constraint
ctx.set_font_size(60)
# Render a Unicode emoji (U+1F600)
ctx.move_to(50, 100)
ctx.show_text('😀')
# Save the surface
surface.finish()
After running the snippet, open output.pdf and run pdfinfo output.pdf. Look for the Font entry under the rendered page; it should list the font family you selected. If the entry is missing, the font wasn’t embedded. Check the rendered emoji: if it appears as a tofu box, the font lacks U+1F600. If text looks abnormally small or large, inspect the page scaling – a text‑matrix value outside 0–1000 usually means the user‑space scale needs adjustment.
Verification checklist
- Font dictionary in PDF lists the expected family name.
cairo_text_extentsfor the rendered character returns width/height within0–1000.- PDF version is 1.7 or newer when rendering emoji or CJK.
- Pango font‑map resolution matches the intended output DPI.
Limitations
Cairo’s PDF backend relies on the underlying FreeType library for font rasterization. If FreeType version on the host differs from the target environment, glyph metrics may shift. PDF/A‑1a and PDF/A‑1u enforce strict embedding and licensing rules; fonts with restrictive outlines may cause compliance failures. The 1000‑unit text matrix is a hard limit for Cairo’s PDF implementation; transformations that exceed it will produce undefined rendering behavior on some backends.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.