Translucency: alpha colours in PDF output

June 10, 2026 · View on GitHub

DocumentColor carries an optional alpha channel. The PDF backend renders it as a graphics-state alpha constant — genuine translucency where the content behind shows through, not a pre-mixed tint against an assumed background. That matters on tinted pages and layered designs, where a pre-mixed colour would only look right on white.

Creating translucent colours

import com.demcha.compose.document.style.DocumentColor;

DocumentColor glass  = DocumentColor.rgba(255, 255, 255, 178);  // alpha 0–255
DocumentColor tint   = DocumentColor.rgb(20, 80, 95).withOpacity(0.35);

rgba(r, g, b, a) takes an explicit alpha byte (0 = transparent, 255 = opaque); withOpacity(0.0–1.0) derives a translucent copy of any existing colour — handy for turning one brand colour into a family of tints. Opacities outside [0, 1] are rejected at construction.

What honours alpha (and what does not)

In the PDF backend, alpha applies to shape fills and strokes:

  • rectangles, panels (softPanel(...)), and chart bars
  • ellipses, including chart point markers
  • polygons (pie/donut slices, line-chart area fills)
  • inline shapes
  • chart value-label halo chips

Text and lines render fully opaque regardless of alpha, and the semantic DOCX export ignores the alpha channel entirely. If a translucent colour reaches one of those, you get the opaque colour — never an error.

Opaque colours stay byte-identical

The alpha machinery only engages for translucent colours: a fully opaque fill or stroke emits no extended graphics state at all, so documents that never use alpha produce byte-identical PDFs before and after this feature. Each translucent fill is also scoped to its own fragment — the alpha never leaks into content drawn afterwards.

Recipe: a translucent panel over a page background

A frosted-glass card that lets the page tint shimmer through (see page-backgrounds.md for the background API):

document.pageBackground(DocumentColor.rgb(28, 42, 56));     // deep navy page

document.pageFlow()
        .addSection("GlassCard", section -> section
                .softPanel(DocumentColor.rgba(255, 255, 255, 200), 8, 16)
                .addParagraph(p -> p.text("Readable on navy, without going solid white.")))
        .build();

Recipe: chart value-label halos on tinted cards

Line-chart value labels draw behind a halo chip that is themed white. On a tinted card a solid white chip looks like a sticker — a translucent halo blends into the card instead (full chart API in charts.md):

section.chart(lineSpec, ChartStyle.builder()
        .valueLabelHalo(DocumentPaint.solid(DocumentColor.rgba(255, 255, 255, 190)))
        .build());

The same idea drives area(true) line charts: area fills use genuine graphics-state alpha (ChartStyle.areaOpacity, default 0.35), so overlapping series stay legible.

Recipe: layered tints from one brand colour

Because the alpha mixes with whatever is behind it at render time, one base colour plus withOpacity gives consistent tint steps on any surface:

DocumentColor brand = DocumentColor.rgb(20, 80, 95);

section.addShape(120, 24, brand.withOpacity(0.15));   // faint band
section.addShape(120, 24, brand.withOpacity(0.45));   // mid band
section.addShape(120, 24, brand);                     // full strength

Verified end-to-end (including the opaque byte-identity guarantee) by PdfShapeAlphaTest.