Merge pull request #6606 from Rob--W/pattern-scaling
Improve performance and correctness of Tiling Patterns
This commit is contained in:
commit
b4c3b94592
@ -330,15 +330,28 @@ var TilingPattern = (function TilingPatternClosure() {
|
|||||||
|
|
||||||
info('TilingType: ' + tilingType);
|
info('TilingType: ' + tilingType);
|
||||||
|
|
||||||
|
// A tiling pattern as defined by PDF spec 8.7.2 is a cell whose size is
|
||||||
|
// described by bbox, and may repeat regularly by shifting the cell by
|
||||||
|
// xstep and ystep.
|
||||||
|
// Because the HTML5 canvas API does not support pattern repetition with
|
||||||
|
// gaps in between, we use the xstep/ystep instead of the bbox's size.
|
||||||
|
//
|
||||||
|
// This has the following consequences (similarly for ystep):
|
||||||
|
//
|
||||||
|
// - If xstep is the same as bbox, then there is no observable difference.
|
||||||
|
//
|
||||||
|
// - If xstep is larger than bbox, then the pattern canvas is partially
|
||||||
|
// empty: the area bounded by bbox is painted, the outside area is void.
|
||||||
|
//
|
||||||
|
// - If xstep is smaller than bbox, then the pixels between xstep and the
|
||||||
|
// bbox boundary will be missing. This is INCORRECT behavior.
|
||||||
|
// "Figures on adjacent tiles should not overlap" (PDF spec 8.7.3.1),
|
||||||
|
// but overlapping cells without common pixels are still valid.
|
||||||
|
// TODO: Fix the implementation, to allow this scenario to be painted
|
||||||
|
// correctly.
|
||||||
|
|
||||||
var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
|
var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
|
||||||
|
|
||||||
var topLeft = [x0, y0];
|
|
||||||
// we want the canvas to be as large as the step size
|
|
||||||
var botRight = [x0 + xstep, y0 + ystep];
|
|
||||||
|
|
||||||
var width = botRight[0] - topLeft[0];
|
|
||||||
var height = botRight[1] - topLeft[1];
|
|
||||||
|
|
||||||
// Obtain scale from matrix and current transformation matrix.
|
// Obtain scale from matrix and current transformation matrix.
|
||||||
var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
|
var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
|
||||||
var curMatrixScale = Util.singularValueDecompose2dScale(
|
var curMatrixScale = Util.singularValueDecompose2dScale(
|
||||||
@ -346,50 +359,55 @@ var TilingPattern = (function TilingPatternClosure() {
|
|||||||
var combinedScale = [matrixScale[0] * curMatrixScale[0],
|
var combinedScale = [matrixScale[0] * curMatrixScale[0],
|
||||||
matrixScale[1] * curMatrixScale[1]];
|
matrixScale[1] * curMatrixScale[1]];
|
||||||
|
|
||||||
// MAX_PATTERN_SIZE is used to avoid OOM situation.
|
|
||||||
// Use width and height values that are as close as possible to the end
|
// Use width and height values that are as close as possible to the end
|
||||||
// result when the pattern is used. Too low value makes the pattern look
|
// result when the pattern is used. Too low value makes the pattern look
|
||||||
// blurry. Too large value makes it look too crispy.
|
// blurry. Too large value makes it look too crispy.
|
||||||
width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
|
var dimx = this.getSizeAndScale(xstep, this.ctx.canvas.width,
|
||||||
MAX_PATTERN_SIZE);
|
combinedScale[0]);
|
||||||
|
var dimy = this.getSizeAndScale(ystep, this.ctx.canvas.height,
|
||||||
height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
|
combinedScale[1]);
|
||||||
MAX_PATTERN_SIZE);
|
|
||||||
|
|
||||||
var tmpCanvas = owner.cachedCanvases.getCanvas('pattern',
|
var tmpCanvas = owner.cachedCanvases.getCanvas('pattern',
|
||||||
width, height, true);
|
dimx.size, dimy.size, true);
|
||||||
var tmpCtx = tmpCanvas.context;
|
var tmpCtx = tmpCanvas.context;
|
||||||
var graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
|
var graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
|
||||||
graphics.groupLevel = owner.groupLevel;
|
graphics.groupLevel = owner.groupLevel;
|
||||||
|
|
||||||
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
||||||
|
|
||||||
this.setScale(width, height, xstep, ystep);
|
graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
|
||||||
this.transformToScale(graphics);
|
|
||||||
|
|
||||||
// transform coordinates to pattern space
|
// transform coordinates to pattern space
|
||||||
var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
|
graphics.transform(1, 0, 0, 1, -x0, -y0);
|
||||||
graphics.transform.apply(graphics, tmpTranslate);
|
|
||||||
|
|
||||||
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
|
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
|
||||||
|
|
||||||
graphics.executeOperatorList(operatorList);
|
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 tmpCanvas.canvas;
|
||||||
},
|
},
|
||||||
|
|
||||||
setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
|
getSizeAndScale:
|
||||||
this.scale = [width / xstep, height / ystep];
|
function TilingPattern_getSizeAndScale(step, realOutputSize, scale) {
|
||||||
},
|
// xstep / ystep may be negative -- normalize.
|
||||||
|
step = Math.abs(step);
|
||||||
transformToScale: function TilingPattern_transformToScale(graphics) {
|
// MAX_PATTERN_SIZE is used to avoid OOM situation.
|
||||||
var scale = this.scale;
|
// Use the destination canvas's size if it is bigger than the hard-coded
|
||||||
var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
|
// limit of MAX_PATTERN_SIZE to avoid clipping patterns that cover the
|
||||||
graphics.transform.apply(graphics, tmpScale);
|
// whole canvas.
|
||||||
},
|
var maxSize = Math.max(MAX_PATTERN_SIZE, realOutputSize);
|
||||||
|
var size = Math.ceil(step * scale);
|
||||||
scaleToContext: function TilingPattern_scaleToContext() {
|
if (size >= maxSize) {
|
||||||
var scale = this.scale;
|
size = maxSize;
|
||||||
this.ctx.scale(1 / scale[0], 1 / scale[1]);
|
} else {
|
||||||
|
scale = size / step;
|
||||||
|
}
|
||||||
|
return { scale, size, };
|
||||||
},
|
},
|
||||||
|
|
||||||
clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
|
clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
|
||||||
@ -427,12 +445,12 @@ var TilingPattern = (function TilingPatternClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getPattern: function TilingPattern_getPattern(ctx, owner) {
|
getPattern: function TilingPattern_getPattern(ctx, owner) {
|
||||||
var temporaryPatternCanvas = this.createPatternCanvas(owner);
|
|
||||||
|
|
||||||
ctx = this.ctx;
|
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.setTransform.apply(ctx, this.baseTransform);
|
||||||
ctx.transform.apply(ctx, this.matrix);
|
ctx.transform.apply(ctx, this.matrix);
|
||||||
this.scaleToContext();
|
|
||||||
|
var temporaryPatternCanvas = this.createPatternCanvas(owner);
|
||||||
|
|
||||||
return ctx.createPattern(temporaryPatternCanvas, 'repeat');
|
return ctx.createPattern(temporaryPatternCanvas, 'repeat');
|
||||||
},
|
},
|
||||||
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
@ -342,3 +342,5 @@
|
|||||||
!issue9972-1.pdf
|
!issue9972-1.pdf
|
||||||
!issue9972-2.pdf
|
!issue9972-2.pdf
|
||||||
!issue9972-3.pdf
|
!issue9972-3.pdf
|
||||||
|
!tiling-pattern-box.pdf
|
||||||
|
!tiling-pattern-large-steps.pdf
|
||||||
|
2154
test/pdfs/tiling-pattern-box.pdf
Normal file
2154
test/pdfs/tiling-pattern-box.pdf
Normal file
File diff suppressed because it is too large
Load Diff
90
test/pdfs/tiling-pattern-large-steps.pdf
Normal file
90
test/pdfs/tiling-pattern-large-steps.pdf
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
% A 4000 x 400 PDF with a red square rectangle and 50 units of padding at all sides.
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Catalog
|
||||||
|
/Pages 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Pages
|
||||||
|
/Kids [3 0 R]
|
||||||
|
/Count 1
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [0.0 0.0 4000 400]
|
||||||
|
/Parent 2 0 R
|
||||||
|
/Resources 4 0 R
|
||||||
|
/Contents 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Pattern 6 0 R
|
||||||
|
/ColorSpace 7 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/Length 36
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
50 50 3950 350 re
|
||||||
|
/cs1 cs
|
||||||
|
/p1 scn
|
||||||
|
f
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/p1 8 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/cs1 [/Pattern /DeviceRGB]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Length 43
|
||||||
|
/Type /Pattern
|
||||||
|
/PatternType 1
|
||||||
|
/Resources <<
|
||||||
|
>>
|
||||||
|
/BBox [0 0 3950 350]
|
||||||
|
/PaintType 1
|
||||||
|
/TilingType 1
|
||||||
|
/XStep 90000
|
||||||
|
/YStep 90000
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB cs
|
||||||
|
1 0 0 sc
|
||||||
|
50 50 3950 300 re
|
||||||
|
B
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 9
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000094 00000 n
|
||||||
|
0000000143 00000 n
|
||||||
|
0000000200 00000 n
|
||||||
|
0000000309 00000 n
|
||||||
|
0000000363 00000 n
|
||||||
|
0000000448 00000 n
|
||||||
|
0000000479 00000 n
|
||||||
|
0000000527 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/Root 1 0 R
|
||||||
|
/Size 9
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
740
|
||||||
|
%%EOF
|
@ -3009,6 +3009,22 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "tiling-pattern-box",
|
||||||
|
"file": "pdfs/tiling-pattern-box.pdf",
|
||||||
|
"md5": "09100872824fc14012bd8f9bf4dbc632",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": false,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tiling-pattern-large-steps",
|
||||||
|
"file": "pdfs/tiling-pattern-large-steps.pdf",
|
||||||
|
"md5": "569aac1303c97004aab6a720d9b259b4",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": false,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue6151",
|
{ "id": "issue6151",
|
||||||
"file": "pdfs/issue6151.pdf",
|
"file": "pdfs/issue6151.pdf",
|
||||||
"md5": "926f8c6b25e6f0978759f7947d70e079",
|
"md5": "926f8c6b25e6f0978759f7947d70e079",
|
||||||
|
Loading…
Reference in New Issue
Block a user