diff --git a/src/builder/mask-image.ts b/src/builder/mask-image.ts index 46a24112..20920a73 100644 --- a/src/builder/mask-image.ts +++ b/src/builder/mask-image.ts @@ -45,7 +45,15 @@ export default async function buildMaskImage( }) } - mask = buildXMLString('mask', { id: miId }, mask) + const mode = maskImage[0]?.mode + const maskType = + mode === 'alpha' ? 'alpha' : mode === 'luminance' ? 'luminance' : undefined + + mask = buildXMLString( + 'mask', + { id: miId, ...(maskType ? { 'mask-type': maskType } : {}) }, + mask + ) return [miId, mask] } diff --git a/src/parser/mask.ts b/src/parser/mask.ts index 60ecf93a..9efa03e8 100644 --- a/src/parser/mask.ts +++ b/src/parser/mask.ts @@ -13,6 +13,7 @@ export interface MaskProperty { repeat: string origin: string clip: string + mode: string } export function parseMask( @@ -26,6 +27,7 @@ export function parseMask( repeat: getMaskProperty(style, 'repeat') || 'repeat', origin: getMaskProperty(style, 'origin') || 'border-box', clip: getMaskProperty(style, 'origin') || 'border-box', + mode: getMaskProperty(style, 'mode') || 'match-source', } let maskImages = splitEffects(maskImage).filter((v) => v && v !== 'none') diff --git a/test/mask-image.test.tsx b/test/mask-image.test.tsx index 488274a0..49e9ae4f 100644 --- a/test/mask-image.test.tsx +++ b/test/mask-image.test.tsx @@ -210,6 +210,34 @@ describe('Mask-*', () => { expect(toImage(svg, 100)).toMatchImageSnapshot() }) + it('should support mask-mode', async () => { + const svgs = await Promise.all( + (['alpha', 'luminance', 'match-source'] as const).map((maskMode) => + satori( +
, + { width: 100, height: 100, fonts } + ) + ) + ) + + expect(svgs[0]).toContain('mask-type="alpha"') + expect(svgs[1]).toContain('mask-type="luminance"') + expect(svgs[2]).not.toContain('mask-type=') + + svgs.forEach((svg) => { + expect(toImage(svg, 100)).toMatchImageSnapshot() + }) + }) + it('should support mask-image on positioned elements', async () => { const svg = await satori(