Merge pull request #13683 from brendandahl/mask-fixes
Fix transformations when painting image masks and tiling patterns.
This commit is contained in:
commit
05ebb6329b
@ -13,11 +13,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
|
||||||
createMatrix,
|
|
||||||
getShadingPattern,
|
|
||||||
TilingPattern,
|
|
||||||
} from "./pattern_helper.js";
|
|
||||||
import {
|
import {
|
||||||
FONT_IDENTITY_MATRIX,
|
FONT_IDENTITY_MATRIX,
|
||||||
IDENTITY_MATRIX,
|
IDENTITY_MATRIX,
|
||||||
@ -32,6 +27,7 @@ import {
|
|||||||
Util,
|
Util,
|
||||||
warn,
|
warn,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
|
import { getShadingPattern, TilingPattern } from "./pattern_helper.js";
|
||||||
|
|
||||||
// <canvas> contexts store most of the state we need natively.
|
// <canvas> contexts store most of the state we need natively.
|
||||||
// However, PDF needs a bit more state, which we store here.
|
// However, PDF needs a bit more state, which we store here.
|
||||||
@ -197,17 +193,6 @@ function addContextCurrentTransform(ctx) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAdjustmentTransformation(transform, width, height) {
|
|
||||||
// The pattern will be created at the size of the current page or form object,
|
|
||||||
// but the mask is usually scaled differently and offset, so we must account
|
|
||||||
// for these to shift and rescale the pattern to the correctly location.
|
|
||||||
let patternTransform = createMatrix(transform);
|
|
||||||
patternTransform = patternTransform.scale(1 / width, -1 / height);
|
|
||||||
patternTransform = patternTransform.translate(0, -height);
|
|
||||||
patternTransform = patternTransform.inverse();
|
|
||||||
return patternTransform;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CachedCanvases {
|
class CachedCanvases {
|
||||||
constructor(canvasFactory) {
|
constructor(canvasFactory) {
|
||||||
this.canvasFactory = canvasFactory;
|
this.canvasFactory = canvasFactory;
|
||||||
@ -1046,6 +1031,154 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_scaleImage(img, inverseTransform) {
|
||||||
|
// Vertical or horizontal scaling shall not be more than 2 to not lose the
|
||||||
|
// pixels during drawImage operation, painting on the temporary canvas(es)
|
||||||
|
// that are twice smaller in size.
|
||||||
|
const width = img.width;
|
||||||
|
const height = img.height;
|
||||||
|
let widthScale = Math.max(
|
||||||
|
Math.hypot(inverseTransform[0], inverseTransform[1]),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
let heightScale = Math.max(
|
||||||
|
Math.hypot(inverseTransform[2], inverseTransform[3]),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
let paintWidth = width,
|
||||||
|
paintHeight = height;
|
||||||
|
let tmpCanvasId = "prescale1";
|
||||||
|
let tmpCanvas, tmpCtx;
|
||||||
|
while (
|
||||||
|
(widthScale > 2 && paintWidth > 1) ||
|
||||||
|
(heightScale > 2 && paintHeight > 1)
|
||||||
|
) {
|
||||||
|
let newWidth = paintWidth,
|
||||||
|
newHeight = paintHeight;
|
||||||
|
if (widthScale > 2 && paintWidth > 1) {
|
||||||
|
newWidth = Math.ceil(paintWidth / 2);
|
||||||
|
widthScale /= paintWidth / newWidth;
|
||||||
|
}
|
||||||
|
if (heightScale > 2 && paintHeight > 1) {
|
||||||
|
newHeight = Math.ceil(paintHeight / 2);
|
||||||
|
heightScale /= paintHeight / newHeight;
|
||||||
|
}
|
||||||
|
tmpCanvas = this.cachedCanvases.getCanvas(
|
||||||
|
tmpCanvasId,
|
||||||
|
newWidth,
|
||||||
|
newHeight
|
||||||
|
);
|
||||||
|
tmpCtx = tmpCanvas.context;
|
||||||
|
tmpCtx.clearRect(0, 0, newWidth, newHeight);
|
||||||
|
tmpCtx.drawImage(
|
||||||
|
img,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
paintWidth,
|
||||||
|
paintHeight,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
newWidth,
|
||||||
|
newHeight
|
||||||
|
);
|
||||||
|
img = tmpCanvas.canvas;
|
||||||
|
paintWidth = newWidth;
|
||||||
|
paintHeight = newHeight;
|
||||||
|
tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
img,
|
||||||
|
paintWidth,
|
||||||
|
paintHeight,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_createMaskCanvas(img) {
|
||||||
|
const ctx = this.ctx;
|
||||||
|
const width = img.width,
|
||||||
|
height = img.height;
|
||||||
|
const fillColor = this.current.fillColor;
|
||||||
|
const isPatternFill = this.current.patternFill;
|
||||||
|
const maskCanvas = this.cachedCanvases.getCanvas(
|
||||||
|
"maskCanvas",
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
);
|
||||||
|
const maskCtx = maskCanvas.context;
|
||||||
|
putBinaryImageMask(maskCtx, img);
|
||||||
|
|
||||||
|
// Create the mask canvas at the size it will be drawn at and also set
|
||||||
|
// its transform to match the current transform so if there are any
|
||||||
|
// patterns applied they will be applied relative to the correct
|
||||||
|
// transform.
|
||||||
|
const objToCanvas = ctx.mozCurrentTransform;
|
||||||
|
let maskToCanvas = Util.transform(objToCanvas, [
|
||||||
|
1 / width,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
-1 / height,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
|
||||||
|
const cord1 = Util.applyTransform([0, 0], maskToCanvas);
|
||||||
|
const cord2 = Util.applyTransform([width, height], maskToCanvas);
|
||||||
|
const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);
|
||||||
|
const drawnWidth = Math.ceil(rect[2] - rect[0]);
|
||||||
|
const drawnHeight = Math.ceil(rect[3] - rect[1]);
|
||||||
|
const fillCanvas = this.cachedCanvases.getCanvas(
|
||||||
|
"fillCanvas",
|
||||||
|
drawnWidth,
|
||||||
|
drawnHeight,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const fillCtx = fillCanvas.context;
|
||||||
|
// The offset will be the top-left cordinate mask.
|
||||||
|
const offsetX = Math.min(cord1[0], cord2[0]);
|
||||||
|
const offsetY = Math.min(cord1[1], cord2[1]);
|
||||||
|
fillCtx.translate(-offsetX, -offsetY);
|
||||||
|
fillCtx.transform.apply(fillCtx, maskToCanvas);
|
||||||
|
// Pre-scale if needed to improve image smoothing.
|
||||||
|
const scaled = this._scaleImage(
|
||||||
|
maskCanvas.canvas,
|
||||||
|
fillCtx.mozCurrentTransformInverse
|
||||||
|
);
|
||||||
|
fillCtx.drawImage(
|
||||||
|
scaled.img,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
scaled.img.width,
|
||||||
|
scaled.img.height,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
);
|
||||||
|
fillCtx.globalCompositeOperation = "source-in";
|
||||||
|
|
||||||
|
const inverse = Util.transform(fillCtx.mozCurrentTransformInverse, [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
-offsetX,
|
||||||
|
-offsetY,
|
||||||
|
]);
|
||||||
|
fillCtx.fillStyle = isPatternFill
|
||||||
|
? fillColor.getPattern(ctx, this, inverse, false)
|
||||||
|
: fillColor;
|
||||||
|
|
||||||
|
fillCtx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// Round the offsets to avoid drawing fractional pixels.
|
||||||
|
return {
|
||||||
|
canvas: fillCanvas.canvas,
|
||||||
|
offsetX: Math.round(offsetX),
|
||||||
|
offsetY: Math.round(offsetY),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Graphics state
|
// Graphics state
|
||||||
setLineWidth(width) {
|
setLineWidth(width) {
|
||||||
this.current.lineWidth = width;
|
this.current.lineWidth = width;
|
||||||
@ -1375,7 +1508,11 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
if (typeof strokeColor === "object" && strokeColor?.getPattern) {
|
if (typeof strokeColor === "object" && strokeColor?.getPattern) {
|
||||||
const lineWidth = this.getSinglePixelWidth();
|
const lineWidth = this.getSinglePixelWidth();
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
|
ctx.strokeStyle = strokeColor.getPattern(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
ctx.mozCurrentTransformInverse
|
||||||
|
);
|
||||||
// Prevent drawing too thin lines by enforcing a minimum line width.
|
// Prevent drawing too thin lines by enforcing a minimum line width.
|
||||||
ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
|
ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
@ -1418,7 +1555,11 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
if (isPatternFill) {
|
if (isPatternFill) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.fillStyle = fillColor.getPattern(ctx, this);
|
ctx.fillStyle = fillColor.getPattern(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
ctx.mozCurrentTransformInverse
|
||||||
|
);
|
||||||
needRestore = true;
|
needRestore = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1748,7 +1889,11 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// TODO: Patterns are not applied correctly to text if a non-embedded
|
// TODO: Patterns are not applied correctly to text if a non-embedded
|
||||||
// font is used. E.g. issue 8111 and ShowText-ShadingPattern.pdf.
|
// font is used. E.g. issue 8111 and ShowText-ShadingPattern.pdf.
|
||||||
ctx.save();
|
ctx.save();
|
||||||
const pattern = current.fillColor.getPattern(ctx, this);
|
const pattern = current.fillColor.getPattern(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
ctx.mozCurrentTransformInverse
|
||||||
|
);
|
||||||
patternTransform = ctx.mozCurrentTransform;
|
patternTransform = ctx.mozCurrentTransform;
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
ctx.fillStyle = pattern;
|
ctx.fillStyle = pattern;
|
||||||
@ -2022,7 +2167,12 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
const pattern = getShadingPattern(patternIR);
|
const pattern = getShadingPattern(patternIR);
|
||||||
ctx.fillStyle = pattern.getPattern(ctx, this, true);
|
ctx.fillStyle = pattern.getPattern(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
ctx.mozCurrentTransformInverse,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
const inv = ctx.mozCurrentTransformInverse;
|
const inv = ctx.mozCurrentTransformInverse;
|
||||||
if (inv) {
|
if (inv) {
|
||||||
@ -2279,8 +2429,6 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
const width = img.width,
|
const width = img.width,
|
||||||
height = img.height;
|
height = img.height;
|
||||||
const fillColor = this.current.fillColor;
|
|
||||||
const isPatternFill = this.current.patternFill;
|
|
||||||
|
|
||||||
const glyph = this.processingType3;
|
const glyph = this.processingType3;
|
||||||
|
|
||||||
@ -2296,35 +2444,15 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
glyph.compiled(ctx);
|
glyph.compiled(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const mask = this._createMaskCanvas(img);
|
||||||
|
const maskCanvas = mask.canvas;
|
||||||
|
|
||||||
const maskCanvas = this.cachedCanvases.getCanvas(
|
ctx.save();
|
||||||
"maskCanvas",
|
// The mask is drawn with the transform applied. Reset the current
|
||||||
width,
|
// transform to draw to the identity.
|
||||||
height
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
);
|
ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
|
||||||
const maskCtx = maskCanvas.context;
|
ctx.restore();
|
||||||
maskCtx.save();
|
|
||||||
|
|
||||||
putBinaryImageMask(maskCtx, img);
|
|
||||||
|
|
||||||
maskCtx.globalCompositeOperation = "source-in";
|
|
||||||
|
|
||||||
let patternTransform = null;
|
|
||||||
if (isPatternFill) {
|
|
||||||
patternTransform = getAdjustmentTransformation(
|
|
||||||
ctx.mozCurrentTransform,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
maskCtx.fillStyle = isPatternFill
|
|
||||||
? fillColor.getPattern(maskCtx, this, false, patternTransform)
|
|
||||||
: fillColor;
|
|
||||||
maskCtx.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
maskCtx.restore();
|
|
||||||
|
|
||||||
this.paintInlineImageXObject(maskCanvas.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paintImageMaskXObjectRepeat(
|
paintImageMaskXObjectRepeat(
|
||||||
@ -2338,54 +2466,27 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
if (!this.contentVisible) {
|
if (!this.contentVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const width = imgData.width;
|
|
||||||
const height = imgData.height;
|
|
||||||
const fillColor = this.current.fillColor;
|
|
||||||
const isPatternFill = this.current.patternFill;
|
|
||||||
|
|
||||||
const maskCanvas = this.cachedCanvases.getCanvas(
|
|
||||||
"maskCanvas",
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
const maskCtx = maskCanvas.context;
|
|
||||||
maskCtx.save();
|
|
||||||
|
|
||||||
putBinaryImageMask(maskCtx, imgData);
|
|
||||||
|
|
||||||
maskCtx.globalCompositeOperation = "source-in";
|
|
||||||
|
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
let patternTransform = null;
|
ctx.save();
|
||||||
if (isPatternFill) {
|
const currentTransform = ctx.mozCurrentTransform;
|
||||||
patternTransform = getAdjustmentTransformation(
|
ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
|
||||||
ctx.mozCurrentTransform,
|
const mask = this._createMaskCanvas(imgData);
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
maskCtx.fillStyle = isPatternFill
|
|
||||||
? fillColor.getPattern(maskCtx, this, false, patternTransform)
|
|
||||||
: fillColor;
|
|
||||||
maskCtx.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
maskCtx.restore();
|
|
||||||
|
|
||||||
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
for (let i = 0, ii = positions.length; i < ii; i += 2) {
|
for (let i = 0, ii = positions.length; i < ii; i += 2) {
|
||||||
ctx.save();
|
const trans = Util.transform(currentTransform, [
|
||||||
ctx.transform(
|
|
||||||
scaleX,
|
scaleX,
|
||||||
skewX,
|
skewX,
|
||||||
skewY,
|
skewY,
|
||||||
scaleY,
|
scaleY,
|
||||||
positions[i],
|
positions[i],
|
||||||
positions[i + 1]
|
positions[i + 1],
|
||||||
);
|
]);
|
||||||
ctx.scale(1, -1);
|
|
||||||
ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
|
const [x, y] = Util.applyTransform([0, 0], trans);
|
||||||
ctx.restore();
|
ctx.drawImage(mask.canvas, x, y);
|
||||||
}
|
}
|
||||||
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
paintImageMaskXObjectGroup(images) {
|
paintImageMaskXObjectGroup(images) {
|
||||||
@ -2413,17 +2514,13 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
maskCtx.globalCompositeOperation = "source-in";
|
maskCtx.globalCompositeOperation = "source-in";
|
||||||
|
|
||||||
let patternTransform = null;
|
|
||||||
if (isPatternFill) {
|
|
||||||
patternTransform = getAdjustmentTransformation(
|
|
||||||
ctx.mozCurrentTransform,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
maskCtx.fillStyle = isPatternFill
|
maskCtx.fillStyle = isPatternFill
|
||||||
? fillColor.getPattern(maskCtx, this, false, patternTransform)
|
? fillColor.getPattern(
|
||||||
|
maskCtx,
|
||||||
|
this,
|
||||||
|
ctx.mozCurrentTransformInverse,
|
||||||
|
false
|
||||||
|
)
|
||||||
: fillColor;
|
: fillColor;
|
||||||
maskCtx.fillRect(0, 0, width, height);
|
maskCtx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
@ -2491,17 +2588,7 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// scale the image to the unit square
|
// scale the image to the unit square
|
||||||
ctx.scale(1 / width, -1 / height);
|
ctx.scale(1 / width, -1 / height);
|
||||||
|
|
||||||
const currentTransform = ctx.mozCurrentTransformInverse;
|
let imgToPaint;
|
||||||
let widthScale = Math.max(
|
|
||||||
Math.hypot(currentTransform[0], currentTransform[1]),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
let heightScale = Math.max(
|
|
||||||
Math.hypot(currentTransform[2], currentTransform[3]),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
let imgToPaint, tmpCanvas, tmpCtx;
|
|
||||||
// typeof check is needed due to node.js support, see issue #8489
|
// typeof check is needed due to node.js support, see issue #8489
|
||||||
if (
|
if (
|
||||||
(typeof HTMLElement === "function" && imgData instanceof HTMLElement) ||
|
(typeof HTMLElement === "function" && imgData instanceof HTMLElement) ||
|
||||||
@ -2509,61 +2596,26 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
) {
|
) {
|
||||||
imgToPaint = imgData;
|
imgToPaint = imgData;
|
||||||
} else {
|
} else {
|
||||||
tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
|
const tmpCanvas = this.cachedCanvases.getCanvas(
|
||||||
tmpCtx = tmpCanvas.context;
|
"inlineImage",
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
);
|
||||||
|
const tmpCtx = tmpCanvas.context;
|
||||||
putBinaryImageData(tmpCtx, imgData, this.current.transferMaps);
|
putBinaryImageData(tmpCtx, imgData, this.current.transferMaps);
|
||||||
imgToPaint = tmpCanvas.canvas;
|
imgToPaint = tmpCanvas.canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
let paintWidth = width,
|
const scaled = this._scaleImage(
|
||||||
paintHeight = height;
|
|
||||||
let tmpCanvasId = "prescale1";
|
|
||||||
// Vertical or horizontal scaling shall not be more than 2 to not lose the
|
|
||||||
// pixels during drawImage operation, painting on the temporary canvas(es)
|
|
||||||
// that are twice smaller in size.
|
|
||||||
while (
|
|
||||||
(widthScale > 2 && paintWidth > 1) ||
|
|
||||||
(heightScale > 2 && paintHeight > 1)
|
|
||||||
) {
|
|
||||||
let newWidth = paintWidth,
|
|
||||||
newHeight = paintHeight;
|
|
||||||
if (widthScale > 2 && paintWidth > 1) {
|
|
||||||
newWidth = Math.ceil(paintWidth / 2);
|
|
||||||
widthScale /= paintWidth / newWidth;
|
|
||||||
}
|
|
||||||
if (heightScale > 2 && paintHeight > 1) {
|
|
||||||
newHeight = Math.ceil(paintHeight / 2);
|
|
||||||
heightScale /= paintHeight / newHeight;
|
|
||||||
}
|
|
||||||
tmpCanvas = this.cachedCanvases.getCanvas(
|
|
||||||
tmpCanvasId,
|
|
||||||
newWidth,
|
|
||||||
newHeight
|
|
||||||
);
|
|
||||||
tmpCtx = tmpCanvas.context;
|
|
||||||
tmpCtx.clearRect(0, 0, newWidth, newHeight);
|
|
||||||
tmpCtx.drawImage(
|
|
||||||
imgToPaint,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
paintWidth,
|
|
||||||
paintHeight,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
newWidth,
|
|
||||||
newHeight
|
|
||||||
);
|
|
||||||
imgToPaint = tmpCanvas.canvas;
|
|
||||||
paintWidth = newWidth;
|
|
||||||
paintHeight = newHeight;
|
|
||||||
tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
|
|
||||||
}
|
|
||||||
ctx.drawImage(
|
|
||||||
imgToPaint,
|
imgToPaint,
|
||||||
|
ctx.mozCurrentTransformInverse
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
scaled.img,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
paintWidth,
|
scaled.paintWidth,
|
||||||
paintHeight,
|
scaled.paintHeight,
|
||||||
0,
|
0,
|
||||||
-height,
|
-height,
|
||||||
width,
|
width,
|
||||||
@ -2576,8 +2628,8 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
imgData,
|
imgData,
|
||||||
left: position[0],
|
left: position[0],
|
||||||
top: position[1],
|
top: position[1],
|
||||||
width: width / currentTransform[0],
|
width: width / ctx.mozCurrentTransformInverse[0],
|
||||||
height: height / currentTransform[3],
|
height: height / ctx.mozCurrentTransformInverse[3],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.restore();
|
this.restore();
|
||||||
|
@ -72,7 +72,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
|
|||||||
this._matrix = IR[8];
|
this._matrix = IR[8];
|
||||||
}
|
}
|
||||||
|
|
||||||
getPattern(ctx, owner, shadingFill = false, patternTransform = null) {
|
getPattern(ctx, owner, inverse, shadingFill = false) {
|
||||||
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
||||||
"pattern",
|
"pattern",
|
||||||
owner.ctx.canvas.width,
|
owner.ctx.canvas.width,
|
||||||
@ -121,11 +121,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
|
|||||||
tmpCtx.fill();
|
tmpCtx.fill();
|
||||||
|
|
||||||
const pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
|
const pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
|
||||||
if (patternTransform) {
|
pattern.setTransform(createMatrix(inverse));
|
||||||
pattern.setTransform(patternTransform);
|
|
||||||
} else {
|
|
||||||
pattern.setTransform(createMatrix(ctx.mozCurrentTransformInverse));
|
|
||||||
}
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,7 +376,7 @@ class MeshShadingPattern extends BaseShadingPattern {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getPattern(ctx, owner, shadingFill = false, patternTransform = null) {
|
getPattern(ctx, owner, inverse, shadingFill = false) {
|
||||||
applyBoundingBox(ctx, this._bbox);
|
applyBoundingBox(ctx, this._bbox);
|
||||||
let scale;
|
let scale;
|
||||||
if (shadingFill) {
|
if (shadingFill) {
|
||||||
@ -535,9 +531,25 @@ class TilingPattern {
|
|||||||
|
|
||||||
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
||||||
|
|
||||||
|
let adjustedX0 = x0;
|
||||||
|
let adjustedY0 = y0;
|
||||||
|
let adjustedX1 = x1;
|
||||||
|
let adjustedY1 = y1;
|
||||||
|
// Some bounding boxes have negative x0/y0 cordinates which will cause the
|
||||||
|
// some of the drawing to be off of the canvas. To avoid this shift the
|
||||||
|
// bounding box over.
|
||||||
|
if (x0 < 0) {
|
||||||
|
adjustedX0 = 0;
|
||||||
|
adjustedX1 += Math.abs(x0);
|
||||||
|
}
|
||||||
|
if (y0 < 0) {
|
||||||
|
adjustedY0 = 0;
|
||||||
|
adjustedY1 += Math.abs(y0);
|
||||||
|
}
|
||||||
|
tmpCtx.translate(-(dimx.scale * adjustedX0), -(dimy.scale * adjustedY0));
|
||||||
graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
|
graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
|
||||||
|
|
||||||
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
|
this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);
|
||||||
|
|
||||||
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
||||||
|
|
||||||
@ -549,6 +561,8 @@ class TilingPattern {
|
|||||||
canvas: tmpCanvas.canvas,
|
canvas: tmpCanvas.canvas,
|
||||||
scaleX: dimx.scale,
|
scaleX: dimx.scale,
|
||||||
scaleY: dimy.scale,
|
scaleY: dimy.scale,
|
||||||
|
offsetX: adjustedX0,
|
||||||
|
offsetY: adjustedY0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,14 +583,12 @@ class TilingPattern {
|
|||||||
return { scale, size };
|
return { scale, size };
|
||||||
}
|
}
|
||||||
|
|
||||||
clipBbox(graphics, bbox, x0, y0, x1, y1) {
|
clipBbox(graphics, x0, y0, x1, y1) {
|
||||||
if (Array.isArray(bbox) && bbox.length === 4) {
|
const bboxWidth = x1 - x0;
|
||||||
const bboxWidth = x1 - x0;
|
const bboxHeight = y1 - y0;
|
||||||
const bboxHeight = y1 - y0;
|
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
||||||
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
graphics.clip();
|
||||||
graphics.clip();
|
graphics.endPath();
|
||||||
graphics.endPath();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFillAndStrokeStyleToContext(graphics, paintType, color) {
|
setFillAndStrokeStyleToContext(graphics, paintType, color) {
|
||||||
@ -603,10 +615,9 @@ class TilingPattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPattern(ctx, owner, shadingFill = false, patternTransform = null) {
|
getPattern(ctx, owner, inverse, shadingFill = false) {
|
||||||
ctx = this.ctx;
|
|
||||||
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix.
|
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix.
|
||||||
let matrix = ctx.mozCurrentTransformInverse;
|
let matrix = inverse;
|
||||||
if (!shadingFill) {
|
if (!shadingFill) {
|
||||||
matrix = Util.transform(matrix, owner.baseTransform);
|
matrix = Util.transform(matrix, owner.baseTransform);
|
||||||
if (this.matrix) {
|
if (this.matrix) {
|
||||||
@ -619,6 +630,10 @@ class TilingPattern {
|
|||||||
let domMatrix = createMatrix(matrix);
|
let domMatrix = createMatrix(matrix);
|
||||||
// Rescale and so that the ctx.createPattern call generates a pattern with
|
// Rescale and so that the ctx.createPattern call generates a pattern with
|
||||||
// the desired size.
|
// the desired size.
|
||||||
|
domMatrix = domMatrix.translate(
|
||||||
|
temporaryPatternCanvas.offsetX,
|
||||||
|
temporaryPatternCanvas.offsetY
|
||||||
|
);
|
||||||
domMatrix = domMatrix.scale(
|
domMatrix = domMatrix.scale(
|
||||||
1 / temporaryPatternCanvas.scaleX,
|
1 / temporaryPatternCanvas.scaleX,
|
||||||
1 / temporaryPatternCanvas.scaleY
|
1 / temporaryPatternCanvas.scaleY
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -206,6 +206,7 @@
|
|||||||
!issue11403_reduced.pdf
|
!issue11403_reduced.pdf
|
||||||
!issue2074.pdf
|
!issue2074.pdf
|
||||||
!scan-bad.pdf
|
!scan-bad.pdf
|
||||||
|
!issue13561_reduced.pdf
|
||||||
!bug847420.pdf
|
!bug847420.pdf
|
||||||
!bug860632.pdf
|
!bug860632.pdf
|
||||||
!bug894572.pdf
|
!bug894572.pdf
|
||||||
|
BIN
test/pdfs/issue13561_reduced.pdf
Normal file
BIN
test/pdfs/issue13561_reduced.pdf
Normal file
Binary file not shown.
@ -878,6 +878,12 @@
|
|||||||
"lastPage": 1,
|
"lastPage": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue13561_reduced",
|
||||||
|
"file": "pdfs/issue13561_reduced.pdf",
|
||||||
|
"md5": "e68c315d6349530180dd90f93027147e",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue5202",
|
{ "id": "issue5202",
|
||||||
"file": "pdfs/issue5202.pdf",
|
"file": "pdfs/issue5202.pdf",
|
||||||
"md5": "bb9cc69211112e66aab40828086a4e5a",
|
"md5": "bb9cc69211112e66aab40828086a4e5a",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user