Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions src/core/pattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,6 @@ class RadialAxialShading extends BaseShading {
[extendStart, extendEnd] = extendArr;
}

if (
this.shadingType === ShadingType.RADIAL &&
(!extendStart || !extendEnd)
) {
// Radial gradient only currently works if either circle is fully within
// the other circle.
const [x1, y1, r1, x2, y2, r2] = this.coordsArr;
const distance = Math.hypot(x1 - x2, y1 - y2);
if (r1 <= r2 + distance && r2 <= r1 + distance) {
warn("Unsupported radial gradient.");
}
}

this.extendStart = extendStart;
this.extendEnd = extendEnd;

Expand Down
65 changes: 65 additions & 0 deletions src/display/pattern_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@ 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 false;
}
const dist = Math.hypot(
this._p0[0] - this._p1[0],
this._p0[1] - this._p1[1]
);
return dist > this._r1;
}

_createGradient(ctx, transform = null) {
let grad;
let firstPoint = this._p0;
Expand Down Expand Up @@ -125,6 +142,41 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
return grad;
}

_createReversedGradient(ctx, transform = null) {
// Swapped circles: (p1, r1) → (p0, r0), with color stops reversed.
let firstPoint = this._p1;
let secondPoint = this._p0;
if (transform) {
firstPoint = firstPoint.slice();
secondPoint = secondPoint.slice();
Util.applyTransform(firstPoint, transform);
Util.applyTransform(secondPoint, transform);
}
let r0 = this._r1;
let r1 = this._r0;
if (transform) {
const scale = new Float32Array(2);
Util.singularValueDecompose2dScale(transform, scale);
r0 *= scale[0];
r1 *= scale[0];
}
const grad = ctx.createRadialGradient(
firstPoint[0],
firstPoint[1],
r0,
secondPoint[0],
secondPoint[1],
r1
);
const reversedStops = this._colorStops
.map(([t, c]) => [1 - t, c])
.reverse();
for (const [t, c] of reversedStops) {
grad.addColorStop(t, c);
}
return grad;
}

getPattern(ctx, owner, inverse, pathType) {
let pattern;
if (pathType === PathType.STROKE || pathType === PathType.FILL) {
Expand Down Expand Up @@ -193,6 +245,10 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
}
applyBoundingBox(tmpCtx, this._bbox);

if (this._isCircleCenterOutside()) {
tmpCtx.fillStyle = this._createReversedGradient(tmpCtx);
tmpCtx.fill();
}
tmpCtx.fillStyle = this._createGradient(tmpCtx);
tmpCtx.fill();

Expand All @@ -203,6 +259,15 @@ 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()) {
// Draw the reversed gradient first so the normal gradient can
// correctly overlay it (see _isCircleCenterOutside for details).
ctx.save();
applyBoundingBox(ctx, this._bbox);
ctx.fillStyle = this._createReversedGradient(ctx);
ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
ctx.restore();
}
applyBoundingBox(ctx, this._bbox);
pattern = this._createGradient(ctx);
}
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -883,3 +883,4 @@
!nested_outline.pdf
!form_two_pages.pdf
!outlines_se.pdf
!radial_gradients.pdf
Binary file added test/pdfs/radial_gradients.pdf
Binary file not shown.
17 changes: 15 additions & 2 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13079,7 +13079,11 @@
"rounds": 1,
"type": "extract",
"includePages": [0, 2, 12],
"pageMapping": { "1": 1, "3": 2, "13": 3 }
"pageMapping": {
"1": 1,
"3": 2,
"13": 3
}
},
{
"id": "bug900822-encrypted-extract_0",
Expand All @@ -13088,7 +13092,9 @@
"rounds": 1,
"type": "extract",
"includePages": [0],
"pageMapping": { "1": 1 }
"pageMapping": {
"1": 1
}
},
{
"id": "xfa_bug1998843",
Expand Down Expand Up @@ -13985,5 +13991,12 @@
"md5": "7072f6763bf2f0d6df14d5fc86962c5a",
"rounds": 1,
"type": "eq"
},
{
"id": "radial_gradients",
"file": "pdfs/radial_gradients.pdf",
"md5": "80e8bed66b83928698f008c33de47edd",
"rounds": 1,
"type": "eq"
}
]
Loading