Merge pull request #14569 from brendandahl/smask-state
Fix canvas state getting out of sync from smasks. (bug 1755507)
This commit is contained in:
commit
530af48b8e
@ -191,6 +191,10 @@ function mirrorContextOperations(ctx, destCtx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addContextCurrentTransform(ctx) {
|
function addContextCurrentTransform(ctx) {
|
||||||
|
if (ctx._transformStack) {
|
||||||
|
// Reset the transform stack.
|
||||||
|
ctx._transformStack = [];
|
||||||
|
}
|
||||||
// If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
|
// If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
|
||||||
if (ctx.mozCurrentTransform) {
|
if (ctx.mozCurrentTransform) {
|
||||||
return;
|
return;
|
||||||
@ -265,6 +269,9 @@ function addContextCurrentTransform(ctx) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ctx.restore = function ctxRestore() {
|
ctx.restore = function ctxRestore() {
|
||||||
|
if (this._transformStack.length === 0) {
|
||||||
|
warn("Tried to restore a ctx when the stack was already empty.");
|
||||||
|
}
|
||||||
const prev = this._transformStack.pop();
|
const prev = this._transformStack.pop();
|
||||||
if (prev) {
|
if (prev) {
|
||||||
this._transformMatrix = prev;
|
this._transformMatrix = prev;
|
||||||
@ -1242,7 +1249,7 @@ class CanvasGraphics {
|
|||||||
|
|
||||||
endDrawing() {
|
endDrawing() {
|
||||||
// Finishing all opened operations such as SMask group painting.
|
// Finishing all opened operations such as SMask group painting.
|
||||||
while (this.stateStack.length || this.current.activeSMask !== null) {
|
while (this.stateStack.length || this.inSMaskMode) {
|
||||||
this.restore();
|
this.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1503,8 +1510,12 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get inSMaskMode() {
|
||||||
|
return !!this.suspendedCtx;
|
||||||
|
}
|
||||||
|
|
||||||
checkSMaskState() {
|
checkSMaskState() {
|
||||||
const inSMaskMode = !!this.suspendedCtx;
|
const inSMaskMode = this.inSMaskMode;
|
||||||
if (this.current.activeSMask && !inSMaskMode) {
|
if (this.current.activeSMask && !inSMaskMode) {
|
||||||
this.beginSMaskMode();
|
this.beginSMaskMode();
|
||||||
} else if (!this.current.activeSMask && inSMaskMode) {
|
} else if (!this.current.activeSMask && inSMaskMode) {
|
||||||
@ -1523,7 +1534,7 @@ class CanvasGraphics {
|
|||||||
* the right order on the canvas' graphics state stack.
|
* the right order on the canvas' graphics state stack.
|
||||||
*/
|
*/
|
||||||
beginSMaskMode() {
|
beginSMaskMode() {
|
||||||
if (this.suspendedCtx) {
|
if (this.inSMaskMode) {
|
||||||
throw new Error("beginSMaskMode called while already in smask mode");
|
throw new Error("beginSMaskMode called while already in smask mode");
|
||||||
}
|
}
|
||||||
const drawnWidth = this.ctx.canvas.width;
|
const drawnWidth = this.ctx.canvas.width;
|
||||||
@ -1550,7 +1561,7 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
endSMaskMode() {
|
endSMaskMode() {
|
||||||
if (!this.suspendedCtx) {
|
if (!this.inSMaskMode) {
|
||||||
throw new Error("endSMaskMode called while not in smask mode");
|
throw new Error("endSMaskMode called while not in smask mode");
|
||||||
}
|
}
|
||||||
// The soft mask is done, now restore the suspended canvas as the main
|
// The soft mask is done, now restore the suspended canvas as the main
|
||||||
@ -1559,7 +1570,6 @@ class CanvasGraphics {
|
|||||||
copyCtxState(this.ctx, this.suspendedCtx);
|
copyCtxState(this.ctx, this.suspendedCtx);
|
||||||
this.ctx = this.suspendedCtx;
|
this.ctx = this.suspendedCtx;
|
||||||
|
|
||||||
this.current.activeSMask = null;
|
|
||||||
this.suspendedCtx = null;
|
this.suspendedCtx = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1589,20 +1599,36 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.ctx.save();
|
if (this.inSMaskMode) {
|
||||||
|
// SMask mode may be turned on/off causing us to lose graphics state.
|
||||||
|
// Copy the temporary canvas state to the main(suspended) canvas to keep
|
||||||
|
// it in sync.
|
||||||
|
copyCtxState(this.ctx, this.suspendedCtx);
|
||||||
|
// Don't bother calling save on the temporary canvas since state is not
|
||||||
|
// saved there.
|
||||||
|
this.suspendedCtx.save();
|
||||||
|
} else {
|
||||||
|
this.ctx.save();
|
||||||
|
}
|
||||||
const old = this.current;
|
const old = this.current;
|
||||||
this.stateStack.push(old);
|
this.stateStack.push(old);
|
||||||
this.current = old.clone();
|
this.current = old.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
restore() {
|
restore() {
|
||||||
if (this.stateStack.length === 0 && this.current.activeSMask) {
|
if (this.stateStack.length === 0 && this.inSMaskMode) {
|
||||||
this.endSMaskMode();
|
this.endSMaskMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stateStack.length !== 0) {
|
if (this.stateStack.length !== 0) {
|
||||||
this.current = this.stateStack.pop();
|
this.current = this.stateStack.pop();
|
||||||
this.ctx.restore();
|
if (this.inSMaskMode) {
|
||||||
|
// Graphics state is stored on the main(suspended) canvas. Restore its
|
||||||
|
// state then copy it over to the temporary canvas.
|
||||||
|
this.suspendedCtx.restore();
|
||||||
|
copyCtxState(this.suspendedCtx, this.ctx);
|
||||||
|
} else {
|
||||||
|
this.ctx.restore();
|
||||||
|
}
|
||||||
this.checkSMaskState();
|
this.checkSMaskState();
|
||||||
|
|
||||||
// Ensure that the clipping path is reset (fixes issue6413.pdf).
|
// Ensure that the clipping path is reset (fixes issue6413.pdf).
|
||||||
@ -2525,9 +2551,8 @@ class CanvasGraphics {
|
|||||||
this.save();
|
this.save();
|
||||||
// If there's an active soft mask we don't want it enabled for the group, so
|
// If there's an active soft mask we don't want it enabled for the group, so
|
||||||
// clear it out. The mask and suspended canvas will be restored in endGroup.
|
// clear it out. The mask and suspended canvas will be restored in endGroup.
|
||||||
const suspendedCtx = this.suspendedCtx;
|
if (this.inSMaskMode) {
|
||||||
if (this.current.activeSMask) {
|
this.endSMaskMode();
|
||||||
this.suspendedCtx = null;
|
|
||||||
this.current.activeSMask = null;
|
this.current.activeSMask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2646,10 +2671,7 @@ class CanvasGraphics {
|
|||||||
["ca", 1],
|
["ca", 1],
|
||||||
["CA", 1],
|
["CA", 1],
|
||||||
]);
|
]);
|
||||||
this.groupStack.push({
|
this.groupStack.push(currentCtx);
|
||||||
ctx: currentCtx,
|
|
||||||
suspendedCtx,
|
|
||||||
});
|
|
||||||
this.groupLevel++;
|
this.groupLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2659,16 +2681,12 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
this.groupLevel--;
|
this.groupLevel--;
|
||||||
const groupCtx = this.ctx;
|
const groupCtx = this.ctx;
|
||||||
const { ctx, suspendedCtx } = this.groupStack.pop();
|
const ctx = this.groupStack.pop();
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
// Turn off image smoothing to avoid sub pixel interpolation which can
|
// Turn off image smoothing to avoid sub pixel interpolation which can
|
||||||
// look kind of blurry for some pdfs.
|
// look kind of blurry for some pdfs.
|
||||||
this.ctx.imageSmoothingEnabled = false;
|
this.ctx.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
if (suspendedCtx) {
|
|
||||||
this.suspendedCtx = suspendedCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.smask) {
|
if (group.smask) {
|
||||||
this.tempSMask = this.smaskStack.pop();
|
this.tempSMask = this.smaskStack.pop();
|
||||||
this.restore();
|
this.restore();
|
||||||
|
@ -577,6 +577,10 @@ class TilingPattern {
|
|||||||
tmpCtx.translate(-(dimx.scale * adjustedX0), -(dimy.scale * adjustedY0));
|
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);
|
||||||
|
|
||||||
|
// To match CanvasGraphics beginDrawing we must save the context here or
|
||||||
|
// else we end up with unbalanced save/restores.
|
||||||
|
tmpCtx.save();
|
||||||
|
|
||||||
this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);
|
this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);
|
||||||
|
|
||||||
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
||||||
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
@ -175,6 +175,7 @@
|
|||||||
!issue8565.pdf
|
!issue8565.pdf
|
||||||
!clippath.pdf
|
!clippath.pdf
|
||||||
!issue8795_reduced.pdf
|
!issue8795_reduced.pdf
|
||||||
|
!bug1755507.pdf
|
||||||
!close-path-bug.pdf
|
!close-path-bug.pdf
|
||||||
!issue6019.pdf
|
!issue6019.pdf
|
||||||
!issue6621.pdf
|
!issue6621.pdf
|
||||||
@ -491,6 +492,7 @@
|
|||||||
!pr12564.pdf
|
!pr12564.pdf
|
||||||
!pr12828.pdf
|
!pr12828.pdf
|
||||||
!secHandler.pdf
|
!secHandler.pdf
|
||||||
|
!issue14297.pdf
|
||||||
!rc_annotation.pdf
|
!rc_annotation.pdf
|
||||||
!issue14267.pdf
|
!issue14267.pdf
|
||||||
!PDFBOX-4352-0.pdf
|
!PDFBOX-4352-0.pdf
|
||||||
|
BIN
test/pdfs/bug1755507.pdf
Normal file
BIN
test/pdfs/bug1755507.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue14297.pdf
Normal file
BIN
test/pdfs/issue14297.pdf
Normal file
Binary file not shown.
@ -2164,6 +2164,12 @@
|
|||||||
"type": "eq",
|
"type": "eq",
|
||||||
"about": "Glyph that gets mapped to unicode non-breaking-space."
|
"about": "Glyph that gets mapped to unicode non-breaking-space."
|
||||||
},
|
},
|
||||||
|
{ "id": "issue14297",
|
||||||
|
"file": "pdfs/issue14297.pdf",
|
||||||
|
"md5": "46eb3d4d4bc47c8009fc4c699d213a18",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "simpletype3font",
|
{ "id": "simpletype3font",
|
||||||
"file": "pdfs/simpletype3font.pdf",
|
"file": "pdfs/simpletype3font.pdf",
|
||||||
"md5": "b374c7543920840c61999e9e86939f99",
|
"md5": "b374c7543920840c61999e9e86939f99",
|
||||||
@ -2513,6 +2519,12 @@
|
|||||||
"lastPage": 2,
|
"lastPage": 2,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "bug1755507",
|
||||||
|
"file": "pdfs/bug1755507.pdf",
|
||||||
|
"md5": "319d73b8fd680cdc583d69b7f7ab29e9",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue9367",
|
{ "id": "issue9367",
|
||||||
"file": "pdfs/issue9367.pdf",
|
"file": "pdfs/issue9367.pdf",
|
||||||
"md5": "81a2c6f1fe5d1bb00ff0479aa6547155",
|
"md5": "81a2c6f1fe5d1bb00ff0479aa6547155",
|
||||||
|
Loading…
Reference in New Issue
Block a user