jspdf-blend-modes
v0.2.3
Published
Real PDF 1.4 blend modes for jsPDF and svg2pdf.js — Multiply, Screen, Overlay, … and round-tripping CSS mix-blend-mode from SVG.
Maintainers
Readme
jspdf-blend-modes
Real PDF 1.4 blend modes for jsPDF and
svg2pdf.js — all 16 modes, plus a
one-call helper that round-trips CSS mix-blend-mode from a live SVG into
the PDF.
Install
npm install jspdf-blend-modes jspdf svg2pdf.js
# or, if you only need the low-level DOM-free API:
npm install jspdf-blend-modes jspdfjspdf is a peer dependency; svg2pdf.js is an optional peer used only
by renderSvgWithBlendModes.
Quickstart
import { jsPDF } from "jspdf";
import "svg2pdf.js";
import { renderSvgWithBlendModes } from "jspdf-blend-modes";
const pdf = new jsPDF({ unit: "pt", format: [432, 432] });
const svg = document.querySelector<SVGSVGElement>("#my-sticker")!;
await renderSvgWithBlendModes(pdf, svg, { x: 0, y: 0, width: 432, height: 432 });
pdf.save("sticker.pdf");Low-level, DOM-free (SSR-safe):
import { withBlendMode } from "jspdf-blend-modes/gstate";
await withBlendMode(pdf, "Multiply", () => {
pdf.setFillColor(255, 0, 0);
pdf.rect(20, 20, 60, 60, "F");
});API
jspdf-blend-modes/gstate(DOM-free):registerBlendMode,withBlendMode,BLEND_MODES,cssToPdfBlendMode,isBlendMode.jspdf-blend-modes(browser):renderSvgWithBlendModes(pdf, svg, opts)— opts:x/y/width/height,blendSelector?,groupingStrategy?: "by-mode" | "by-element",fixDominantBaseline?.
All 16 PDF 1.4 modes: Normal, Multiply, Screen, Overlay, Darken,
Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
Exclusion, Hue, Saturation, Color, Luminosity.
Compatibility
jsPDF >=2.5.0 <6.0.0 · svg2pdf.js >=2.5.0 <3.0.0 (peer) · Node 18/20/22.
All jsPDF internals are isolated in src/internal-api.ts.
Limitations
- Z-order across modes.
groupingStrategy: "by-mode"(the default) collapses all same-mode elements into one pass, which can reorder them relative to elements in other mode groups. PassgroupingStrategy: "by-element"to preserve strict document order at the cost of one extrapdf.svg(...)call per blend element. - The high-level API is browser-only.
renderSvgWithBlendModescallsgetComputedStyle()and mounts isolated SVGs off-screen. Node and SSR consumers should import from the DOM-freejspdf-blend-modes/gstatesubpath instead. - The SVG must be in the document tree.
renderSvgWithBlendModesthrows an actionable error if the SVG isn't connected: bothgetComputedStyleandgetBoundingClientRectneed a live DOM to produce meaningful values. Mount the SVG (off-screen if necessary) before calling, or render it in your UI normally. - ICC color spaces. Output is sRGB; the library does not set an
OutputIntent. For PDF/X printing pipelines, set the OutputIntent yourself and validate against your printer's ICC profile. - Isolation boundaries. PDF blend modes composite against the current
transparency group. The library uses the page-level group, which matches
CSS for the common cases. If you wrap a blend in a non-
Normalparent group, results may differ from CSS — file an issue with a reproduction and we can add agroupSelectoroption. - Fonts. The library does not register fonts for you. If your SVG uses
a custom font, register it on the
pdfinstance (addFileToVFS/addFont) before callingrenderSvgWithBlendModes. - CSS keywords outside the PDF spec. Values like
plus-lighter,plus-darker, andmix-blend-mode: var(--…)aren't part of the PDF 1.4 spec;cssToPdfBlendModereturnsnullfor those and the matched element is skipped (drawn underNormal). Force a specific mode with the predicate form ofblendSelectorif you need an approximation.
Migrating from a manual ExtGState workaround
If you've been hand-rolling the ExtGState injection through jsPDF's internal events, the library replaces all of it with a one-liner.
Before — direct internal access, hand-wired event subscriptions, a
dummy GState to force /ExtGState into the page Resources, and a manual
q ... Q scope around the second-pass render:
const internal = (pdf as any).internal;
internal.events.subscribe("putResources", () => {
const oid = internal.newObject();
internal.write("<< /Type /ExtGState /BM /Multiply /ca 1 /CA 1 >>");
internal.write("endobj");
// ... separate subscription wires the oid into putGStateDict ...
});
// ... force the dictionary to actually be written ...
pdf.addGState("__force__", new (pdf as any).GState({ opacity: 1 }));
internal.out("q /GsMul gs");
await (pdf as any).svg(accentSvg, { x, y, width, height });
internal.out("Q");After — low-level helper that handles the registration, the force-emit hack, and the scope brackets, and is idempotent:
import { withBlendMode } from "jspdf-blend-modes/gstate";
await withBlendMode(pdf, "Multiply", () =>
pdf.svg(accentSvg, { x, y, width, height })
);Or, if you're round-tripping a CSS mix-blend-mode SVG end-to-end, replace
the entire two-pass orchestration with renderSvgWithBlendModes:
import { renderSvgWithBlendModes } from "jspdf-blend-modes";
await renderSvgWithBlendModes(pdf, svg, {
x: 0,
y: 0,
width: pageWidthPt,
height: pageHeightPt
});Behavioural notes during migration:
- No more direct
(pdf as any).internal—withBlendModealways emits the closingQ, even if your callback throws. - Idempotent registration — call
registerBlendMode(pdf, "Multiply")as many times as you want; the library deduplicates by(pdf, mode, name). - GState names changed — the default name is
Gs<Mode>(e.g.GsMultiply) instead of the ad-hocGsMul. PassregisterBlendMode(pdf, "Multiply", { name: "GsMul" })if you have consumers parsing the raw bytes who depend on the old name.
Demo
Live: https://jarvisluk.github.io/jspdf-blend-modes/ — runs all three exports of the same SVG side by side and renders each generated PDF inline with pdf.js, so you can see the multiply blend land or fail without leaving the page.
Local:
npm install && cd demo/browser && npm install && npm run devContributing
Contributions are welcome. Please read CONTRIBUTING.md for local setup, coding style, testing expectations, commit-message conventions, and the pull request checklist.
This project follows the Contributor Covenant. For security-sensitive reports, please follow SECURITY.md instead of opening a public issue.
