From b42dd39aae4acbe11a19d624a37492dfbb2b29dc Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Tue, 31 Mar 2026 21:47:24 +0200 Subject: [PATCH] Fix radial gradient when the two circles have an intersection Fix #257. --- src/display/pattern_helper.js | 20 +++++++++----------- test/test_manifest.json | 10 ++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/display/pattern_helper.js b/src/display/pattern_helper.js index d56e92423..df4f76869 100644 --- a/src/display/pattern_helper.js +++ b/src/display/pattern_helper.js @@ -84,21 +84,19 @@ class RadialAxialShadingPattern extends BaseShadingPattern { return this._type === "radial"; } - // Returns true when the smaller circle's center (p0 when r0 ≤ r1) lies - // outside the larger circle. In that case the canvas radial gradient picks - // t > 1 solutions for points inside the outer circle and maps them to the - // transparent stop we append for extendEnd=false, making the gradient - // invisible. A two-pass draw (reversed first, normal on top) fixes this - // (see #20851). - _isCircleCenterOutside() { - if (!this.isRadial() || this._r0 > this._r1) { + // Return true when the a circle of a radial gradient isn't fully included in + // the other circle, which means that the gradient is conic and we need to + // draw the reversed gradient first to correctly render the pattern (see + // #20851 and #257). + areConic() { + if (!this.isRadial()) { return false; } const dist = Math.hypot( this._p0[0] - this._p1[0], this._p0[1] - this._p1[1] ); - return dist > this._r1; + return dist + this._r1 > this._r0 && dist + this._r0 > this._r1; } _createGradient(ctx, transform = null) { @@ -242,7 +240,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern { } applyBoundingBox(tmpCtx, this._bbox); - if (this._isCircleCenterOutside()) { + if (this.areConic()) { tmpCtx.fillStyle = this._createReversedGradient(tmpCtx); tmpCtx.fill(); } @@ -257,7 +255,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern { // Shading fills are applied relative to the current matrix which is also // how canvas gradients work, so there's no need to do anything special // here. - if (this._isCircleCenterOutside()) { + if (this.areConic()) { // Draw the reversed gradient first so the normal gradient can // correctly overlay it (see _isCircleCenterOutside for details). ctx.save(); diff --git a/test/test_manifest.json b/test/test_manifest.json index 98d6d0192..70782ca2b 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -14042,5 +14042,15 @@ "md5": "7796f0131e7d6428c1bf24a73ff13f95", "rounds": 1, "type": "eq" + }, + { + "id": "pdfspec-radial-gradient", + "file": "pdfs/pdf.pdf", + "md5": "dbdb23c939d2be09b43126c3c56060c7", + "link": true, + "firstPage": 1144, + "lastPage": 1144, + "rounds": 1, + "type": "eq" } ]