Merge pull request #13361 from brendandahl/patterns-fixes

Fix several issues with radial/axial shadings and tiling patterns.
This commit is contained in:
Tim van der Meij 2021-05-12 20:27:37 +02:00 committed by GitHub
commit ba99e54c66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 492 additions and 50 deletions

View File

@ -245,18 +245,17 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
unreachable(`getPattern type unknown: ${shadingType}`);
}
const matrix = this.matrix;
if (matrix) {
p0 = Util.applyTransform(p0, matrix);
p1 = Util.applyTransform(p1, matrix);
if (shadingType === ShadingType.RADIAL) {
const scale = Util.singularValueDecompose2dScale(matrix);
r0 *= scale[0];
r1 *= scale[1];
}
}
return ["RadialAxial", type, this.bbox, this.colorStops, p0, p1, r0, r1];
return [
"RadialAxial",
type,
this.bbox,
this.colorStops,
p0,
p1,
r0,
r1,
this.matrix,
];
},
};

View File

@ -1359,24 +1359,11 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
ctx.globalAlpha = this.current.strokeAlpha;
if (this.contentVisible) {
if (typeof strokeColor === "object" && strokeColor?.getPattern) {
// for patterns, we transform to pattern space, calculate
// the pattern, call stroke, and restore to user space
ctx.save();
// The current transform will be replaced while building the pattern,
// but the line width needs to be adjusted by the current transform,
// so we must scale it. To properly fix this we should be using a
// pattern transform instead (see #10955).
const transform = ctx.mozCurrentTransform;
const scale = Util.singularValueDecompose2dScale(transform)[0];
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
const lineWidth = this.getSinglePixelWidth();
const scaledLineWidth = this.current.lineWidth * scale;
if (lineWidth < 0 && -lineWidth >= scaledLineWidth) {
ctx.resetTransform();
ctx.lineWidth = Math.round(this._combinedScaleFactor);
} else {
ctx.lineWidth = Math.max(lineWidth, scaledLineWidth);
}
ctx.save();
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
// Prevent drawing too thin lines by enforcing a minimum line width.
ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
ctx.stroke();
ctx.restore();
} else {
@ -1417,9 +1404,6 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
if (isPatternFill) {
ctx.save();
if (this.baseTransform) {
ctx.setTransform.apply(ctx, this.baseTransform);
}
ctx.fillStyle = fillColor.getPattern(ctx, this);
needRestore = true;
}

View File

@ -17,6 +17,19 @@ import { FormatError, info, Util } from "../shared/util.js";
const ShadingIRs = {};
let svgElement;
// TODO: remove this when Firefox ESR supports DOMMatrix.
function createMatrix(matrix) {
if (typeof DOMMatrix !== "undefined") {
return new DOMMatrix(matrix);
}
if (!svgElement) {
svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
}
return svgElement.createSVGMatrix(matrix);
}
function applyBoundingBox(ctx, bbox) {
if (!bbox || typeof Path2D === "undefined") {
return;
@ -37,21 +50,57 @@ ShadingIRs.RadialAxial = {
const p1 = raw[5];
const r0 = raw[6];
const r1 = raw[7];
const matrix = raw[8];
return {
getPattern: function RadialAxial_getPattern(ctx) {
applyBoundingBox(ctx, bbox);
getPattern: function RadialAxial_getPattern(ctx, owner, shadingFill) {
const tmpCanvas = owner.cachedCanvases.getCanvas(
"pattern",
ctx.canvas.width,
ctx.canvas.height,
true
);
const tmpCtx = tmpCanvas.context;
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
tmpCtx.beginPath();
tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
if (!shadingFill) {
tmpCtx.setTransform.apply(tmpCtx, owner.baseTransform);
if (matrix) {
tmpCtx.transform.apply(tmpCtx, matrix);
}
} else {
tmpCtx.setTransform.apply(tmpCtx, ctx.mozCurrentTransform);
}
applyBoundingBox(tmpCtx, bbox);
let grad;
if (type === "axial") {
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
grad = tmpCtx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
} else if (type === "radial") {
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
grad = tmpCtx.createRadialGradient(
p0[0],
p0[1],
r0,
p1[0],
p1[1],
r1
);
}
for (let i = 0, ii = colorStops.length; i < ii; ++i) {
const c = colorStops[i];
grad.addColorStop(c[0], c[1]);
}
return grad;
tmpCtx.fillStyle = grad;
tmpCtx.fill();
const pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
pattern.setTransform(createMatrix(ctx.mozCurrentTransformInverse));
return pattern;
},
};
},
@ -483,19 +532,17 @@ const TilingPattern = (function TilingPatternClosure() {
graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
// transform coordinates to pattern space
graphics.transform(1, 0, 0, 1, -x0, -y0);
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
graphics.executeOperatorList(operatorList);
this.ctx.transform(1, 0, 0, 1, x0, y0);
// Rescale canvas so that the ctx.createPattern call generates a pattern
// with the desired size.
this.ctx.scale(1 / dimx.scale, 1 / dimy.scale);
return tmpCanvas.canvas;
return {
canvas: tmpCanvas.canvas,
scaleX: dimx.scale,
scaleY: dimy.scale,
};
},
getSizeAndScale: function TilingPattern_getSizeAndScale(
@ -557,15 +604,34 @@ const TilingPattern = (function TilingPatternClosure() {
}
},
getPattern: function TilingPattern_getPattern(ctx, owner) {
getPattern: function TilingPattern_getPattern(ctx, owner, shadingFill) {
ctx = this.ctx;
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix.
ctx.setTransform.apply(ctx, this.baseTransform);
ctx.transform.apply(ctx, this.matrix);
let matrix = ctx.mozCurrentTransformInverse;
if (!shadingFill) {
matrix = Util.transform(matrix, owner.baseTransform);
if (this.matrix) {
matrix = Util.transform(matrix, this.matrix);
}
}
const temporaryPatternCanvas = this.createPatternCanvas(owner);
return ctx.createPattern(temporaryPatternCanvas, "repeat");
let domMatrix = createMatrix(matrix);
// Rescale and so that the ctx.createPattern call generates a pattern with
// the desired size.
domMatrix = domMatrix.scale(
1 / temporaryPatternCanvas.scaleX,
1 / temporaryPatternCanvas.scaleY
);
const pattern = ctx.createPattern(
temporaryPatternCanvas.canvas,
"repeat"
);
pattern.setTransform(domMatrix);
return pattern;
},
};

View File

@ -43,6 +43,7 @@
!issue7406.pdf
!issue7426.pdf
!issue7439.pdf
!issue7847_radial.pdf
!issue7446.pdf
!issue7492.pdf
!issue7544.pdf
@ -102,6 +103,7 @@
!issue11242_reduced.pdf
!issue11279.pdf
!issue11362.pdf
!issue13325_reduced.pdf
!issue11578_reduced.pdf
!issue11651.pdf
!issue11878.pdf
@ -297,6 +299,7 @@
!bug1028735.pdf
!bug1046314.pdf
!bug1065245.pdf
!issue6769.pdf
!bug1151216.pdf
!bug1175962.pdf
!bug1020226.pdf
@ -367,6 +370,7 @@
!issue5481.pdf
!issue5567.pdf
!issue5701.pdf
!issue6769_no_matrix.pdf
!issue12007_reduced.pdf
!issue5896.pdf
!issue6010_1.pdf
@ -380,6 +384,7 @@
!issue13271.pdf
!issue6298.pdf
!issue6889.pdf
!issue11473.pdf
!bug1001080.pdf
!bug1671312_reduced.pdf
!bug1671312_ArialNarrow.pdf

BIN
test/pdfs/issue11473.pdf Normal file

Binary file not shown.

Binary file not shown.

114
test/pdfs/issue6769.pdf Normal file
View File

@ -0,0 +1,114 @@
%PDF-1.5
%âãÏÓ
1 0 obj
<<
/Kids [2 0 R]
/Type /Pages
/Count 1
>>
endobj
2 0 obj
<<
/Resources 3 0 R
/Type /Page
/Parent 1 0 R
/Contents 4 0 R
/MediaBox [0 0 100 100]
/Group
<<
/CS /DeviceRGB
/I true
/Type /Group
/S /Transparency
>>
>>
endobj
3 0 obj
<<
/ExtGState
<<
/a0
<<
/CA 1
/ca 1
>>
>>
/Pattern
<<
/p5 5 0 R
>>
>>
endobj
5 0 obj
<<
/PatternType 2
/Type /Pattern
/Shading
<<
/Coords [0 0 0 0 0 100]
/Extend [true true]
/Function 6 0 R
/ShadingType 3
/Domain [0 1]
/ColorSpace /DeviceRGB
>>
/Matrix [1 0 0 -1 0 10]
>>
endobj
4 0 obj
<<
/Length 77
>>
stream
q
/Pattern CS /p5 SCN /a0 gs
0.3 0 0 0.1 0 0 cm
50 w
100 100 m
300 700 l
S
Q
endstream
endobj
6 0 obj
<<
/FunctionType 2
/N 1
/C1 [0 1 0]
/C0 [0 0 1]
/Domain [0 1]
>>
endobj
7 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj
8 0 obj
<<
/Creator (cairo 1.13.1 \(http://cairographics.org\))
/Producer (cairo 1.13.1 \(http://cairographics.org\))
>>
endobj xref
0 9
0000000000 65535 f
0000000015 00000 n
0000000074 00000 n
0000000247 00000 n
0000000542 00000 n
0000000337 00000 n
0000000672 00000 n
0000000754 00000 n
0000000805 00000 n
trailer
<<
/Info 8 0 R
/Root 7 0 R
/Size 9
>>
startxref
934
%%EOF

View File

@ -0,0 +1,114 @@
%PDF-1.5
%âãÏÓ
1 0 obj
<<
/Kids [2 0 R]
/Type /Pages
/Count 1
>>
endobj
2 0 obj
<<
/Resources 3 0 R
/Type /Page
/Parent 1 0 R
/Contents 4 0 R
/MediaBox [0 0 100 100]
/Group
<<
/CS /DeviceRGB
/I true
/Type /Group
/S /Transparency
>>
>>
endobj
3 0 obj
<<
/ExtGState
<<
/a0
<<
/CA 1
/ca 1
>>
>>
/Pattern
<<
/p5 5 0 R
>>
>>
endobj
5 0 obj
<<
/PatternType 2
/Type /Pattern
/Shading
<<
/Coords [0 0 0 0 0 100]
/Extend [true true]
/Function 6 0 R
/ShadingType 3
/Domain [0 1]
/ColorSpace /DeviceRGB
>>
/Matrix [2 0 0 2 0 0]
>>
endobj
4 0 obj
<<
/Length 77
>>
stream
q
/Pattern CS /p5 SCN /a0 gs
0.3 0 0 0.1 0 0 cm
50 w
100 100 m
300 700 l
S
Q
endstream
endobj
6 0 obj
<<
/FunctionType 2
/N 1
/C1 [0 1 0]
/C0 [0 0 1]
/Domain [0 1]
>>
endobj
7 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj
8 0 obj
<<
/Creator (cairo 1.13.1 \(http://cairographics.org\))
/Producer (cairo 1.13.1 \(http://cairographics.org\))
>>
endobj xref
0 9
0000000000 65535 f
0000000015 00000 n
0000000074 00000 n
0000000247 00000 n
0000000542 00000 n
0000000337 00000 n
0000000672 00000 n
0000000754 00000 n
0000000805 00000 n
trailer
<<
/Info 8 0 R
/Root 7 0 R
/Size 9
>>
startxref
934
%%EOF

View File

@ -0,0 +1,130 @@
%PDF-1.3
%ÿÿÿÿ
6 0 obj
<<
/FunctionType 2
/Domain [0 1]
/C0 [1 1 0]
/C1 [0 0.5019607843137255 0]
/N 1
>>
endobj
7 0 obj
<<
/FunctionType 2
/Domain [0 1]
/C0 [0 0.5019607843137255 0]
/C1 [1 1 1]
/N 1
>>
endobj
8 0 obj
<<
/FunctionType 3
/Domain [0 1]
/Functions [6 0 R 7 0 R]
/Bounds [0.5]
/Encode [0 1 0 1]
>>
endobj
9 0 obj
<<
/ShadingType 3
/ColorSpace /DeviceRGB
/Coords [0.5 0.5 0 0.5 0.5 0.5]
/Function 8 0 R
/Extend [true true]
>>
endobj
10 0 obj
<<
/Type /Pattern
/PatternType 2
/Shading 9 0 R
/Matrix [440 0 0 -200 20 220]
>>
endobj
11 0 obj
<<
/Type /ExtGState
/ca 1
>>
endobj
5 0 obj
<<
/Type /Page
/Parent 1 0 R
/MediaBox [0 0 480 240]
/Contents 3 0 R
/Resources 4 0 R
>>
endobj
4 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Pattern <<
/Sh1 10 0 R
>>
/ExtGState <<
/Gs1 11 0 R
>>
>>
endobj
3 0 obj
<<
/Length 66
>>
stream
1 0 0 -1 0 240 cm
20 20 440 200 re
/Pattern cs
/Sh1 scn
/Gs1 gs
f
endstream
endobj
12 0 obj
<<
/Producer (PDFKit)
/Creator (PDFKit)
/CreationDate (D:20161124005340Z)
>>
endobj
2 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj
1 0 obj
<<
/Type /Pages
/Count 1
/Kids [5 0 R]
>>
endobj
xref
0 13
0000000000 65535 f
0000001071 00000 n
0000001022 00000 n
0000000813 00000 n
0000000690 00000 n
0000000586 00000 n
0000000015 00000 n
0000000112 00000 n
0000000209 00000 n
0000000317 00000 n
0000000444 00000 n
0000000541 00000 n
0000000929 00000 n
trailer
<<
/Size 13
/Root 2 0 R
/Info 12 0 R
>>
startxref
1128
%%EOF

View File

@ -491,6 +491,12 @@
"lastPage": 2,
"type": "load"
},
{ "id": "issue7847_radial",
"file": "pdfs/issue7847_radial.pdf",
"md5": "59dc206ec5dd6e6f701943e4694e4147",
"rounds": 1,
"type": "eq"
},
{ "id": "rotation",
"file": "pdfs/rotation.pdf",
"md5": "4fb25ada00ce7528569d9791c14decf5",
@ -643,6 +649,12 @@
"rounds": 1,
"type": "eq"
},
{ "id": "issue6769_no_matrix",
"file": "pdfs/issue6769_no_matrix.pdf",
"md5": "f88806137fb4592975052ff3508d7b1e",
"rounds": 1,
"type": "eq"
},
{ "id": "unix01",
"file": "pdfs/unix01.pdf",
"md5": "2742999f0bf9b9c035dbb0736096e220",
@ -2558,6 +2570,12 @@
"rounds": 1,
"type": "eq"
},
{ "id": "issue6769",
"file": "pdfs/issue6769.pdf",
"md5": "724b94d6bd1e8010403328446d0ab061",
"rounds": 1,
"type": "eq"
},
{ "id": "issue4304",
"file": "pdfs/issue4304.pdf",
"md5": "1b1205bf0d7c1ad22a154b60da8e694d",
@ -3982,6 +4000,12 @@
"link": true,
"type": "eq"
},
{ "id": "issue11473",
"file": "pdfs/issue11473.pdf",
"md5": "c530851b0523c81784ab4ea3115a7c67",
"rounds": 1,
"type": "eq"
},
{ "id": "bug1140761",
"file": "pdfs/bug1140761.pdf",
"md5": "b74eced7634d4f248dc6265f8225d432",
@ -4398,6 +4422,12 @@
"type": "eq",
"about": "Support for CMap GBKp-EUC-H"
},
{ "id": "issue13325_reduced",
"file": "pdfs/issue13325_reduced.pdf",
"md5": "a9311230d2e3388070711be8748a1aa0",
"rounds": 1,
"type": "eq"
},
{ "id": "issue2829",
"file": "pdfs/issue2829.pdf",
"md5": "f32b28cf8792f6ccc470446bfbb38584",