| date | 2026-02-25 |
|---|---|
| title | Transparency Groups Soft Masks Gradient Alpha |
@libpdf/core supports parts of PDF transparency today:
- constant opacity through ExtGState (
ca/CA) - blend mode through ExtGState (
BM) - image alpha through image XObject
SMask
But it does not expose the two reusable primitives that power PDF transparency as a whole:
- transparency groups (
/Groupon form XObjects) - soft masks (
/SMaskon ExtGState)
This forces common tasks like gradient fade-out to rely on narrow behavior rather than the native PDF model.
- Add transparency-group authoring on form XObjects (
/Groupwith isolation/knockout/color-space controls). - Add soft-mask authoring on ExtGState for both
LuminosityandAlphasubtypes. - Add
opacityto gradient color stops and map non-uniform stop opacity to soft-mask composition automatically. - Keep all transparency controls composable: soft mask + constant opacity + blend mode in one graphics state.
- Preserve the two-layer API design: low-level COS control + high-level ergonomic defaults.
AIS(alpha-is-shape)- soft-mask transfer functions
- matte/premultiplied-alpha image-mask authoring
- parsing existing soft masks from loaded PDFs
- Canvas API integration (separate consumer)
- It aligns implementation with PDF 1.4+ transparency semantics instead of adding effect-specific branches.
- It unblocks multiple effects from one foundation: gradient alpha, vignette masks, text-as-mask, grouped compositing.
- It reduces future feature cost by making transparency behavior explicit in low-level objects.
- It preserves library consistency with existing composable drawing architecture.
- ExtGState constant opacity (
ca,CA) - ExtGState blend modes (
BM) - image XObject
SMask
- Form XObject
/Groupauthoring for transparent compositing islands - ExtGState
/SMaskdictionaries for arbitrary mask content - Gradient-stop alpha channel semantics
- integrated draw-time orchestration when alpha varies spatially
FormXObjectOptions.groupfor transparency-group dictionary emissionFormXObjectOptions.resourcesto support self-contained group contentExtGStateOptions.softMaskwithLuminosity,Alpha, and explicitNoneColorStop.opacityon gradient APIs (defaulting to opaque)- automatic decomposition for varying-opacity gradients:
- color shading for chroma
- grayscale shading for opacity
- soft-mask wiring at draw time
- compatibility rules so
ca/CA+BM+SMaskcan coexist
- parser exposure of soft masks in loaded PDFs
- new high-level helper methods that duplicate existing primitives without new capability
- renderer/canvas behavior modeling beyond PDF output generation
const gradient = pdf.createLinearGradient({
angle: 90,
length: 220,
stops: [
{ offset: 0, color: rgb(0.9, 0.2, 0.2), opacity: 1 },
{ offset: 1, color: rgb(0.9, 0.2, 0.2), opacity: 0 },
],
});
const pattern = pdf.createShadingPattern({ shading: gradient });
page.drawRectangle({ x: 72, y: 450, width: 240, height: 90, pattern });Expectation: users do not manually construct mask groups for this path.
const maskGroup = pdf.createFormXObject({
bbox: { x: 0, y: 0, width: 300, height: 100 },
group: { colorSpace: "DeviceGray", isolated: true },
operators: [
/* text, paths, gradients, images */
],
resources: maskResources,
});
const maskedGs = pdf.createExtGState({
softMask: { subtype: "Luminosity", group: maskGroup },
fillOpacity: 0.85,
blendMode: "Multiply",
});Expectation: soft mask composes cleanly with existing opacity and blending.
const groupForm = pdf.createFormXObject({
bbox: { x: 0, y: 0, width: 200, height: 200 },
group: { colorSpace: "DeviceRGB", isolated: true, knockout: false },
operators: [
/* overlapping transparent marks */
],
resources: groupResources,
});Expectation: viewers composite inner content as a group and then composite group output onto backdrop.
- Form XObject options include a transparency-group object and optional resources dictionary.
- ExtGState options include soft-mask definition (
Luminosity|Alpha|None). - Soft-mask source content is always a form XObject (the native PDF model).
ColorStop.opacity?: numberon gradient APIs.- Existing draw methods transparently choose the right path:
- opaque stops only: current path
- uniform non-1 opacity: constant opacity path
- varying opacity: soft-mask path
- No separate
createTransparencyGroup()API if it would only wrap form XObject creation. - No separate
createSoftMask()API if it would only wrap ExtGState creation.
Rationale: keep surface area minimal while exposing full capability.
- Own dictionary modeling/serialization for
/Groupand/SMaskstructures. - Enforce structural validity at object construction boundaries.
- Owns gradient-opacity analysis and decision routing (opaque/uniform/varying).
- Owns draw-time resource assembly where geometry/bounds are known.
- Exposes intent (
opacityon stops) with sensible defaults. - Delegates to low-level primitives to preserve predictable PDF output.
This follows existing architecture rather than introducing a parallel transparency subsystem.
BM,ca/CA, andSMaskare independent ExtGState terms and must be allowed together.- Effective source opacity uses multiplicative composition (constant opacity and mask opacity both matter).
- Group behavior follows PDF transparency-group semantics.
- Documentation should call out that entering a transparency group changes compositing context.
- All stops opaque => no new transparency objects.
- All stops share same non-1 opacity => constant opacity only.
- Stops vary => soft-mask path.
ColorStop.opacitynormalized/clamped to[0, 1].- Luminosity masks require compatible color-space expectations on source group.
- Backdrop color (if present) must match group color-space component count.
- Alpha-mask guidance favors isolated groups for predictable behavior.
- Explicit
softMask: "None"supported as state-reset mechanism.
- Transparency output requires PDF 1.4+ semantics.
- Plan decision: define whether writer should auto-bump minimum output version when transparency primitives are used.
- No behavior change for existing opaque gradients or non-transparency drawing calls.
- Add form XObject transparency-group support and resources attachment.
- Add ExtGState soft-mask model (
Alpha,Luminosity,None). - Ensure serialization emits spec-correct dictionaries.
- Add
ColorStop.opacityand default behavior. - Add stop-opacity classification (opaque/uniform/varying).
- Preserve current gradient behavior for opaque-only input.
- Wire varying-opacity gradients through soft-mask resources.
- Ensure one coherent graphics-state path that can include blend mode and constant opacity.
- Keep registration/resource behavior stable across repeated draw calls.
- Add constructor-level validation and developer-facing errors.
- Add docs/examples for:
- gradient fade
- text/shape luminosity masks
- alpha masks
- compositional behavior with blend + opacity
/Groupdictionary emission for form XObjects with optional fields./SMaskdictionary emission forLuminosity,Alpha, andNone.- Validation failures for incompatible subtype/group/backdrop inputs.
- Stop-opacity normalization and path classification behavior.
- Draw calls with varying-opacity gradients produce expected transparency resources.
- Combined behavior: one graphics state can carry blend mode + constant opacity + soft mask.
- Fill/stroke pathways both validated when pattern-based alpha is involved.
- Opaque gradient output remains byte-structure compatible with current behavior where applicable.
- Existing opacity/blend-mode tests remain stable.
- Linear and radial fade-to-transparent fixtures.
- Text-as-luminosity-mask fixture.
- Alpha-subtype fixture driven by transparent source content.
- Backdrop color fixture.
- Update transparency sections in API docs with a conceptual model:
- group compositing
- soft-mask subtypes
- gradient opacity decomposition
- Add cookbook examples for common effects (fade, vignette, masked image).
- Add explicit note about
softMask: "None"for low-level state management.
- Resource growth: varying-opacity gradients may add extra objects.
- Mitigation: preserve fast paths and add future caching hooks.
- BBox sensitivity: mask-group bounds control visible edges.
- Mitigation: clear bounding-box rules and integration tests around geometry.
- Semantic confusion: users may conflate group opacity with mask opacity.
- Mitigation: docs/examples showing composition model explicitly.
- Version mismatch: transparency in pre-1.4 outputs is invalid.
- Mitigation: enforce/auto-bump policy and test writer metadata.
- Should writer always auto-bump to at least 1.4 when transparency features are present?
- Should we cache mask resources for repeated equivalent geometry in a first pass, or defer until profiling indicates need?
- Should high-level API expose a convenience reset for soft masks, or keep reset solely low-level?
- Users can author transparency groups through form XObject options.
- Users can author
LuminosityandAlphasoft masks through ExtGState options. ColorStop.opacityenables gradient alpha behavior without manual mask plumbing.- Soft mask, blend mode, and constant opacity compose on the same graphics state.
- Opaque-gradient and existing drawing behavior remain backward compatible.
- Generated files render correctly in major PDF viewers for covered fixtures.