feat(drawing): add transparency groups, soft masks, and gradient stop opacity#39
feat(drawing): add transparency groups, soft masks, and gradient stop opacity#39
Conversation
… opacity Implement PDF 1.4+ transparency primitives: - Form XObject transparency group support (isolated/knockout/colorSpace) - ExtGState soft mask authoring (Luminosity, Alpha, None reset) - ColorStop.opacity on gradient APIs with automatic soft-mask composition - Draw-time opacity routing: opaque/uniform/varying fast paths - Composable graphics state: soft mask + blend mode + constant opacity Includes comprehensive visual integration tests and structural validation.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Benchmark ResultsComparisonLoad PDF
Create blank PDF
Add 10 pages
Draw 50 rectangles
Load and save PDF
Load, modify, and save PDF
Extract single page from 100-page PDF
Split 100-page PDF into single-page PDFs
Split 2000-page PDF into single-page PDFs (0.9MB)
Copy 10 pages between documents
Merge 2 x 100-page PDFs
CopyingCopy pages between documents
Duplicate pages within same document
Merge PDFs
Drawingbenchmarks/drawing.bench.ts
Formsbenchmarks/forms.bench.ts
Loadingbenchmarks/loading.bench.ts
Savingbenchmarks/saving.bench.ts
SplittingExtract single page
Split into single-page PDFs
Batch page extraction
Environment
Results are machine-dependent. |
There was a problem hiding this comment.
Pull request overview
This PR adds PDF 1.4+ transparency primitives to the drawing subsystem, enabling soft masks, transparency groups, and per-gradient-stop opacity with automatic draw-time routing for opaque/uniform/varying opacity cases.
Changes:
- Add
ColorStop.opacityand shading-side opacity classification + grayscale “opacity shading” generation for soft-mask composition. - Add low-level authoring for transparency groups on Form XObjects and
/SMaskon ExtGState (including explicit/SMask /Nonereset). - Update high-level drawing/path emission to automatically route varying-opacity shading patterns through soft masks and ensure PDF version >= 1.4 when needed, with new unit + integration tests.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/integration/drawing/low-level-soft-masks.test.ts | Adds visual + structural integration coverage for transparency groups, soft masks, and gradient stop opacity. |
| src/index.ts | Re-exports newly added transparency-related types. |
| src/helpers/pdf-version.ts | Adds shared helpers for parsing/bumping PDF catalog version. |
| src/helpers/pdf-version.test.ts | Unit tests for the PDF version helper behavior. |
| src/drawing/resources/shading.ts | Adds stop opacity support, classification, and grayscale opacity shading generation for masks. |
| src/drawing/resources/shading.test.ts | Unit tests for opacity classification and grayscale mask shading dict creation. |
| src/drawing/resources/pattern.ts | Stores optional shading pattern matrix on the resource instance for reuse in opacity-mask patterns. |
| src/drawing/resources/index.ts | Exposes new ExtGState/Form XObject transparency types from the resources barrel. |
| src/drawing/resources/form-xobject.ts | Adds optional resources and group (transparency group) support to Form XObject authoring. |
| src/drawing/resources/form-xobject.test.ts | Unit tests for /Group and /Resources emission behavior. |
| src/drawing/resources/extgstate.ts | Adds /SMask authoring (Alpha/Luminosity/None) + backdrop validation. |
| src/drawing/resources/extgstate.test.ts | Unit tests for soft mask serialization and validation rules. |
| src/drawing/path-builder.ts | Adds a hook to let the page intercept path painting for pattern-opacity routing. |
| src/api/pdf.ts | Centralizes version upgrades, carries shading “definition” metadata, upgrades version on transparency usage, and passes pattern matrix through. |
| src/api/pdf-page.ts | Implements pattern-opacity routing (opaque/uniform/varying) and soft-mask composition at draw time; ensures min PDF version. |
| .agents/plans/quiet-indigo-fire-transparency-groups-soft-masks-gradient-alpha.md | Design/plan documentation for the transparency feature set. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (options.subtype === "Luminosity") { | ||
| const groupColorSpace = options.group.group?.colorSpace; | ||
|
|
||
| if (!groupColorSpace) { | ||
| throw new Error( | ||
| "Luminosity soft mask requires a form XObject transparency group with a colorSpace", | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| if (!options.backdropColor) { | ||
| return; | ||
| } | ||
|
|
||
| const groupColorSpace = options.group.group?.colorSpace; | ||
|
|
There was a problem hiding this comment.
validateSoftMaskOptions() only enforces a transparency group (options.group.group?.colorSpace) for subtype: "Luminosity" (or when backdropColor is present). Per the PDF soft-mask model, the /G entry in an /SMask should reference a transparency group Form XObject; allowing subtype: "Alpha" with a non-group Form XObject can produce structurally invalid PDFs in some viewers. Consider requiring options.group.group (and thus emitting a /Group dictionary) for both Alpha and Luminosity soft masks, and providing a clear error when the supplied PDFFormXObject lacks transparency-group metadata.
| if (options.subtype === "Luminosity") { | |
| const groupColorSpace = options.group.group?.colorSpace; | |
| if (!groupColorSpace) { | |
| throw new Error( | |
| "Luminosity soft mask requires a form XObject transparency group with a colorSpace", | |
| ); | |
| } | |
| } | |
| if (!options.backdropColor) { | |
| return; | |
| } | |
| const groupColorSpace = options.group.group?.colorSpace; | |
| const transparencyGroup = options.group.group; | |
| if (!transparencyGroup) { | |
| throw new Error( | |
| `Soft mask of subtype "${options.subtype}" requires the mask form XObject to define a transparency group (group)`, | |
| ); | |
| } | |
| const groupColorSpace = transparencyGroup.colorSpace; | |
| if (options.subtype === "Luminosity" && !groupColorSpace) { | |
| throw new Error( | |
| "Luminosity soft mask requires a form XObject transparency group with a colorSpace", | |
| ); | |
| } | |
| if (!options.backdropColor) { | |
| return; | |
| } |
Implement PDF 1.4+ transparency primitives:
Includes comprehensive visual integration tests and structural validation.