diff --git a/src/fill/parseFill.ts b/src/fill/parseFill.ts index 1fb4341b..d58535e7 100644 --- a/src/fill/parseFill.ts +++ b/src/fill/parseFill.ts @@ -9,6 +9,7 @@ import { PatternFill } from './PatternFill' import { ColorFill } from './ColorFill' import { RGBColor } from '../utils/rgbcolor' import { parseColor } from '../utils/parsing' +import { Gradient } from '../nodes/gradient' export function parseFill(fill: string, context: Context): Fill | null { const url = iriReference.exec(fill) @@ -16,7 +17,7 @@ export function parseFill(fill: string, context: Context): Fill | null { const fillUrl = url[1] const fillNode = context.refsHandler.get(fillUrl) if (fillNode && (fillNode instanceof LinearGradient || fillNode instanceof RadialGradient)) { - return new GradientFill(fillUrl, fillNode) + return getGradientFill(fillUrl, fillNode, context) } else if (fillNode && fillNode instanceof Pattern) { return new PatternFill(fillUrl, fillNode) } else { @@ -35,3 +36,25 @@ export function parseFill(fill: string, context: Context): Fill | null { } } } + +function getGradientFill(fillUrl: string, gradient: Gradient, context: Context): Fill | null { + // "It is necessary that at least two stops are defined to have a gradient effect. If no stops are + // defined, then painting shall occur as if 'none' were specified as the paint style. If one stop + // is defined, then paint with the solid color fill using the color defined for that gradient + // stop." + const stops = gradient.getStops(context.styleSheets) + if (stops.length === 0) { + return null + } + if (stops.length === 1) { + const stopColor = stops[0].color + const rgbColor = new RGBColor() + rgbColor.ok = true + rgbColor.r = stopColor[0] + rgbColor.g = stopColor[1] + rgbColor.b = stopColor[2] + rgbColor.a = stops[0].opacity + return new ColorFill(rgbColor) + } + return new GradientFill(fillUrl, gradient) +} diff --git a/src/nodes/gradient.ts b/src/nodes/gradient.ts index dc0bde93..659508d1 100644 --- a/src/nodes/gradient.ts +++ b/src/nodes/gradient.ts @@ -7,10 +7,12 @@ import { RGBColor } from '../utils/rgbcolor' import { SvgNode } from './svgnode' import { GState, Matrix, ShadingPattern, ShadingPatternType } from 'jspdf' import { parseColor } from '../utils/parsing' +import { StyleSheets } from '../context/stylesheets' export abstract class Gradient extends NonRenderedNode { private readonly pdfGradientType: ShadingPatternType private contextColor: RGBColor | null | undefined + private stops: StopData[] | undefined protected constructor( pdfGradientType: ShadingPatternType, @@ -28,12 +30,39 @@ export abstract class Gradient extends NonRenderedNode { return } + const colors: StopData[] = this.getStops(context.styleSheets) + let opacitySum = 0 + let hasOpacity = false + let gState + + colors.forEach(({ opacity }) => { + if (opacity && opacity !== 1) { + opacitySum += opacity + hasOpacity = true + } + }) + + if (hasOpacity) { + gState = new GState({ opacity: opacitySum / colors.length }) + } + + const pattern = new ShadingPattern(this.pdfGradientType, this.getCoordinates(), colors, gState) + context.pdf.addShadingPattern(id, pattern) + } + + abstract getCoordinates(): number[] + + public getStops(styleSheets: StyleSheets): StopData[] { + if (this.stops) { + return this.stops + } + // Only need to calculate contextColor once if (this.contextColor === undefined) { this.contextColor = null let ancestor: SvgNode | null = this as SvgNode while (ancestor) { - const colorAttr = getAttribute(ancestor.element, context.styleSheets, 'color') + const colorAttr = getAttribute(ancestor.element, styleSheets, 'color') if (colorAttr) { this.contextColor = parseColor(colorAttr, null) break @@ -42,40 +71,26 @@ export abstract class Gradient extends NonRenderedNode { } } - const colors: StopData[] = [] - let opacitySum = 0 - let hasOpacity = false - let gState - + const stops: StopData[] = [] this.children.forEach(stop => { if (stop.element.tagName.toLowerCase() === 'stop') { - const colorAttr = getAttribute(stop.element, context.styleSheets, 'color') + const colorAttr = getAttribute(stop.element, styleSheets, 'color') const color = parseColor( - getAttribute(stop.element, context.styleSheets, 'stop-color') || '', + getAttribute(stop.element, styleSheets, 'stop-color') || '', colorAttr ? parseColor(colorAttr, null) : (this.contextColor as RGBColor | null) ) - colors.push({ + const opacity = parseFloat(getAttribute(stop.element, styleSheets, 'stop-opacity') || '1') + stops.push({ offset: Gradient.parseGradientOffset(stop.element.getAttribute('offset') || '0'), - color: [color.r, color.g, color.b] + color: [color.r, color.g, color.b], + opacity }) - const opacity = getAttribute(stop.element, context.styleSheets, 'stop-opacity') - if (opacity && opacity !== '1') { - opacitySum += parseFloat(opacity) - hasOpacity = true - } } }) - if (hasOpacity) { - gState = new GState({ opacity: opacitySum / colors.length }) - } - - const pattern = new ShadingPattern(this.pdfGradientType, this.getCoordinates(), colors, gState) - context.pdf.addShadingPattern(id, pattern) + return (this.stops = stops) } - abstract getCoordinates(): number[] - protected getBoundingBoxCore(context: Context): Rect { return defaultBoundingBox(this.element, context) } @@ -98,7 +113,8 @@ export abstract class Gradient extends NonRenderedNode { } } -interface StopData { +export interface StopData { color: number[] + opacity: number offset: number } diff --git a/test/common/tests.js b/test/common/tests.js index c647c379..79783870 100644 --- a/test/common/tests.js +++ b/test/common/tests.js @@ -25,6 +25,7 @@ window.tests = [ 'font-style', 'gradient-default-coordinates', 'gradient-percent-offset', + 'gradient-stop-defaults', 'gradient-units', 'gradients-and-patterns-mixed', 'hidden-clippath', diff --git a/test/specs/gradient-stop-defaults/reference.pdf b/test/specs/gradient-stop-defaults/reference.pdf new file mode 100644 index 00000000..1d62047b Binary files /dev/null and b/test/specs/gradient-stop-defaults/reference.pdf differ diff --git a/test/specs/gradient-stop-defaults/spec.svg b/test/specs/gradient-stop-defaults/spec.svg new file mode 100644 index 00000000..77c3c0d8 --- /dev/null +++ b/test/specs/gradient-stop-defaults/spec.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + +