mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
Merge pull request #13361 from brendandahl/patterns-fixes
Fix several issues with radial/axial shadings and tiling patterns.
This commit is contained in:
commit
ba99e54c66
10 changed files with 492 additions and 50 deletions
|
@ -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,
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue