From e18fa3fc455aebe34a014f28d74df350bd7c1dc1 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 20 Jun 2020 11:34:41 +0200 Subject: [PATCH] Tweak the `QueueOptimizer` to recognize `OPS.paintImageMaskXObject` operators as *repeated* when the "skew" transformation matrix elements are non-zero (issue 8078) *First of all, I should mention that my understanding of the finer details of the `QueueOptimizer` (and its related `CanvasGraphics` methods) is somewhat limited.* Hence I'm not sure if there's actually a very good reason for *only* considering ImageMasks where the "skew" transformation matrix elements are zero as *repeated*, however simply looking at the code I just don't see why these elements cannot be non-zero as long as they are *all identical* for the ImageMasks. Furthermore, looking at the *group* case (which is what we're currently falling back to), there's no particular limitation placed upon the transformation matrix elements. While this patch obviously isn't enough to *completely* fix the issue, since there should be a visible Pattern rendered as well[1], it seem (at least to me) like enough of an improvement that submitting this is justified. With these changes the referenced PDF document will no longer hang the *entire* browser, and rendering also finishes in a *reasonable* time (< 10 seconds for me) which seem fine given the *huge* number of identical inline images present.[2] --- [1] Temporarily changing the Pattern to a solid color *does* render the correct/expected area, which suggests that the remaining problem is a pre-existing issue related to the Pattern-handling itself rather than the `QueueOptimizer` functionality. [2] The document isn't exactly rendered immediately in e.g. Adobe Reader either. --- src/core/operator_list.js | 18 ++++++++++-------- src/display/canvas.js | 13 +++++++++++-- test/pdfs/issue8078.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 4 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test/pdfs/issue8078.pdf.link diff --git a/src/core/operator_list.js b/src/core/operator_list.js index b3b9c045c1be5..ca1ff25057ce0 100644 --- a/src/core/operator_list.js +++ b/src/core/operator_list.js @@ -229,13 +229,13 @@ var QueueOptimizer = (function QueueOptimizerClosure() { var isSameImage = false; var iTransform, transformArgs; var firstPIMXOArg0 = argsArray[iFirstPIMXO][0]; - if ( - argsArray[iFirstTransform][1] === 0 && - argsArray[iFirstTransform][2] === 0 - ) { + const firstTransformArg0 = argsArray[iFirstTransform][0], + firstTransformArg1 = argsArray[iFirstTransform][1], + firstTransformArg2 = argsArray[iFirstTransform][2], + firstTransformArg3 = argsArray[iFirstTransform][3]; + + if (firstTransformArg1 === firstTransformArg2) { isSameImage = true; - var firstTransformArg0 = argsArray[iFirstTransform][0]; - var firstTransformArg3 = argsArray[iFirstTransform][3]; iTransform = iFirstTransform + 4; var iPIMXO = iFirstPIMXO + 4; for (q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) { @@ -243,8 +243,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() { if ( argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || - transformArgs[1] !== 0 || - transformArgs[2] !== 0 || + transformArgs[1] !== firstTransformArg1 || + transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3 ) { if (q < MIN_IMAGES_IN_MASKS_BLOCK) { @@ -272,6 +272,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() { argsArray.splice(iFirstSave, count * 4, [ firstPIMXOArg0, firstTransformArg0, + firstTransformArg1, + firstTransformArg2, firstTransformArg3, positions, ]); diff --git a/src/display/canvas.js b/src/display/canvas.js index 8cae8db99de58..bdfb16f405501 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -2157,9 +2157,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.paintInlineImageXObject(maskCanvas.canvas); }, - paintImageMaskXObjectRepeat: function CanvasGraphics_paintImageMaskXObjectRepeat( + paintImageMaskXObjectRepeat( imgData, scaleX, + skewX = 0, + skewY = 0, scaleY, positions ) { @@ -2190,7 +2192,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var ctx = this.ctx; for (var i = 0, ii = positions.length; i < ii; i += 2) { ctx.save(); - ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]); + ctx.transform( + scaleX, + skewX, + skewY, + scaleY, + positions[i], + positions[i + 1] + ); ctx.scale(1, -1); ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); ctx.restore(); diff --git a/test/pdfs/issue8078.pdf.link b/test/pdfs/issue8078.pdf.link new file mode 100644 index 0000000000000..103c89331afaf --- /dev/null +++ b/test/pdfs/issue8078.pdf.link @@ -0,0 +1 @@ +https://bugs.ghostscript.com/attachment.cgi?id=7455 diff --git a/test/test_manifest.json b/test/test_manifest.json index db8b70ee4f3c6..f724bff8cce71 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -711,6 +711,14 @@ "type": "eq", "about": "Type1 font with |Ref|s in the Differences array of the Encoding dictionary." }, + { "id": "issue8078", + "file": "pdfs/issue8078.pdf", + "md5": "8b7d74bc24b4157393e4e88a511c05f1", + "link": true, + "rounds": 1, + "lastPage": 1, + "type": "eq" + }, { "id": "issue8092", "file": "pdfs/issue8092.pdf", "md5": "e4f3376b35fd132580246c3db1fbd738",