Convert src/display/pattern_helper.js
to use standard classes
Note that this patch only covers `TilingPattern`, since the `ShadingIRs`-implementation required additional re-factoring.
This commit is contained in:
parent
438cf1e438
commit
40939d5955
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FormatError, info, Util } from "../shared/util.js";
|
import { FormatError, info, shadow, Util } from "../shared/util.js";
|
||||||
|
|
||||||
const ShadingIRs = {};
|
const ShadingIRs = {};
|
||||||
|
|
||||||
@ -430,19 +430,18 @@ function getShadingPatternFromIR(raw) {
|
|||||||
return shadingIR.fromIR(raw);
|
return shadingIR.fromIR(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const PaintType = {
|
||||||
* @type {any}
|
COLORED: 1,
|
||||||
*/
|
UNCOLORED: 2,
|
||||||
const TilingPattern = (function TilingPatternClosure() {
|
};
|
||||||
const PaintType = {
|
|
||||||
COLORED: 1,
|
|
||||||
UNCOLORED: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
|
class TilingPattern {
|
||||||
|
// 10in @ 300dpi shall be enough.
|
||||||
|
static get MAX_PATTERN_SIZE() {
|
||||||
|
return shadow(this, "MAX_PATTERN_SIZE", 3000);
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
|
||||||
function TilingPattern(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
|
|
||||||
this.operatorList = IR[2];
|
this.operatorList = IR[2];
|
||||||
this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
|
this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
|
||||||
this.bbox = IR[4];
|
this.bbox = IR[4];
|
||||||
@ -451,193 +450,178 @@ const TilingPattern = (function TilingPatternClosure() {
|
|||||||
this.paintType = IR[7];
|
this.paintType = IR[7];
|
||||||
this.tilingType = IR[8];
|
this.tilingType = IR[8];
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
this.ctx = ctx;
|
||||||
this.canvasGraphicsFactory = canvasGraphicsFactory;
|
this.canvasGraphicsFactory = canvasGraphicsFactory;
|
||||||
this.baseTransform = baseTransform;
|
this.baseTransform = baseTransform;
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TilingPattern.prototype = {
|
createPatternCanvas(owner) {
|
||||||
createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
|
const operatorList = this.operatorList;
|
||||||
const operatorList = this.operatorList;
|
const bbox = this.bbox;
|
||||||
const bbox = this.bbox;
|
const xstep = this.xstep;
|
||||||
const xstep = this.xstep;
|
const ystep = this.ystep;
|
||||||
const ystep = this.ystep;
|
const paintType = this.paintType;
|
||||||
const paintType = this.paintType;
|
const tilingType = this.tilingType;
|
||||||
const tilingType = this.tilingType;
|
const color = this.color;
|
||||||
const color = this.color;
|
const canvasGraphicsFactory = this.canvasGraphicsFactory;
|
||||||
const canvasGraphicsFactory = this.canvasGraphicsFactory;
|
|
||||||
|
|
||||||
info("TilingType: " + tilingType);
|
info("TilingType: " + tilingType);
|
||||||
|
|
||||||
// A tiling pattern as defined by PDF spec 8.7.2 is a cell whose size is
|
// 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
|
// described by bbox, and may repeat regularly by shifting the cell by
|
||||||
// xstep and ystep.
|
// xstep and ystep.
|
||||||
// Because the HTML5 canvas API does not support pattern repetition with
|
// 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.
|
// gaps in between, we use the xstep/ystep instead of the bbox's size.
|
||||||
//
|
//
|
||||||
// This has the following consequences (similarly for ystep):
|
// This has the following consequences (similarly for ystep):
|
||||||
//
|
//
|
||||||
// - If xstep is the same as bbox, then there is no observable difference.
|
// - 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
|
// - 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.
|
// 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
|
// - If xstep is smaller than bbox, then the pixels between xstep and the
|
||||||
// bbox boundary will be missing. This is INCORRECT behavior.
|
// bbox boundary will be missing. This is INCORRECT behavior.
|
||||||
// "Figures on adjacent tiles should not overlap" (PDF spec 8.7.3.1),
|
// "Figures on adjacent tiles should not overlap" (PDF spec 8.7.3.1),
|
||||||
// but overlapping cells without common pixels are still valid.
|
// but overlapping cells without common pixels are still valid.
|
||||||
// TODO: Fix the implementation, to allow this scenario to be painted
|
// TODO: Fix the implementation, to allow this scenario to be painted
|
||||||
// correctly.
|
// correctly.
|
||||||
|
|
||||||
const x0 = bbox[0],
|
const x0 = bbox[0],
|
||||||
y0 = bbox[1],
|
y0 = bbox[1],
|
||||||
x1 = bbox[2],
|
x1 = bbox[2],
|
||||||
y1 = bbox[3];
|
y1 = bbox[3];
|
||||||
|
|
||||||
// Obtain scale from matrix and current transformation matrix.
|
// Obtain scale from matrix and current transformation matrix.
|
||||||
const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
|
const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
|
||||||
const curMatrixScale = Util.singularValueDecompose2dScale(
|
const curMatrixScale = Util.singularValueDecompose2dScale(
|
||||||
this.baseTransform
|
this.baseTransform
|
||||||
);
|
);
|
||||||
const combinedScale = [
|
const combinedScale = [
|
||||||
matrixScale[0] * curMatrixScale[0],
|
matrixScale[0] * curMatrixScale[0],
|
||||||
matrixScale[1] * curMatrixScale[1],
|
matrixScale[1] * curMatrixScale[1],
|
||||||
];
|
];
|
||||||
|
|
||||||
// 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.
|
||||||
const dimx = this.getSizeAndScale(
|
const dimx = this.getSizeAndScale(
|
||||||
xstep,
|
xstep,
|
||||||
this.ctx.canvas.width,
|
this.ctx.canvas.width,
|
||||||
combinedScale[0]
|
combinedScale[0]
|
||||||
);
|
);
|
||||||
const dimy = this.getSizeAndScale(
|
const dimy = this.getSizeAndScale(
|
||||||
ystep,
|
ystep,
|
||||||
this.ctx.canvas.height,
|
this.ctx.canvas.height,
|
||||||
combinedScale[1]
|
combinedScale[1]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
||||||
"pattern",
|
"pattern",
|
||||||
dimx.size,
|
dimx.size,
|
||||||
dimy.size,
|
dimy.size,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const tmpCtx = tmpCanvas.context;
|
const tmpCtx = tmpCanvas.context;
|
||||||
const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
|
const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
|
||||||
graphics.groupLevel = owner.groupLevel;
|
graphics.groupLevel = owner.groupLevel;
|
||||||
|
|
||||||
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
||||||
|
|
||||||
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, bbox, x0, y0, x1, y1);
|
||||||
|
|
||||||
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
|
||||||
|
|
||||||
graphics.executeOperatorList(operatorList);
|
graphics.executeOperatorList(operatorList);
|
||||||
|
|
||||||
graphics.endDrawing();
|
graphics.endDrawing();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canvas: tmpCanvas.canvas,
|
canvas: tmpCanvas.canvas,
|
||||||
scaleX: dimx.scale,
|
scaleX: dimx.scale,
|
||||||
scaleY: dimy.scale,
|
scaleY: dimy.scale,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
getSizeAndScale: function TilingPattern_getSizeAndScale(
|
getSizeAndScale(step, realOutputSize, scale) {
|
||||||
step,
|
// xstep / ystep may be negative -- normalize.
|
||||||
realOutputSize,
|
step = Math.abs(step);
|
||||||
scale
|
// MAX_PATTERN_SIZE is used to avoid OOM situation.
|
||||||
) {
|
// Use the destination canvas's size if it is bigger than the hard-coded
|
||||||
// xstep / ystep may be negative -- normalize.
|
// limit of MAX_PATTERN_SIZE to avoid clipping patterns that cover the
|
||||||
step = Math.abs(step);
|
// whole canvas.
|
||||||
// MAX_PATTERN_SIZE is used to avoid OOM situation.
|
const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize);
|
||||||
// Use the destination canvas's size if it is bigger than the hard-coded
|
let size = Math.ceil(step * scale);
|
||||||
// limit of MAX_PATTERN_SIZE to avoid clipping patterns that cover the
|
if (size >= maxSize) {
|
||||||
// whole canvas.
|
size = maxSize;
|
||||||
const maxSize = Math.max(MAX_PATTERN_SIZE, realOutputSize);
|
} else {
|
||||||
let size = Math.ceil(step * scale);
|
scale = size / step;
|
||||||
if (size >= maxSize) {
|
}
|
||||||
size = maxSize;
|
return { scale, size };
|
||||||
} else {
|
}
|
||||||
scale = size / step;
|
|
||||||
|
clipBbox(graphics, bbox, x0, y0, x1, y1) {
|
||||||
|
if (Array.isArray(bbox) && bbox.length === 4) {
|
||||||
|
const bboxWidth = x1 - x0;
|
||||||
|
const bboxHeight = y1 - y0;
|
||||||
|
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
||||||
|
graphics.clip();
|
||||||
|
graphics.endPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFillAndStrokeStyleToContext(graphics, paintType, color) {
|
||||||
|
const context = graphics.ctx,
|
||||||
|
current = graphics.current;
|
||||||
|
switch (paintType) {
|
||||||
|
case PaintType.COLORED:
|
||||||
|
const ctx = this.ctx;
|
||||||
|
context.fillStyle = ctx.fillStyle;
|
||||||
|
context.strokeStyle = ctx.strokeStyle;
|
||||||
|
current.fillColor = ctx.fillStyle;
|
||||||
|
current.strokeColor = ctx.strokeStyle;
|
||||||
|
break;
|
||||||
|
case PaintType.UNCOLORED:
|
||||||
|
const cssColor = Util.makeHexColor(color[0], color[1], color[2]);
|
||||||
|
context.fillStyle = cssColor;
|
||||||
|
context.strokeStyle = cssColor;
|
||||||
|
// Set color needed by image masks (fixes issues 3226 and 8741).
|
||||||
|
current.fillColor = cssColor;
|
||||||
|
current.strokeColor = cssColor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new FormatError(`Unsupported paint type: ${paintType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPattern(ctx, owner, shadingFill) {
|
||||||
|
ctx = this.ctx;
|
||||||
|
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix.
|
||||||
|
let matrix = ctx.mozCurrentTransformInverse;
|
||||||
|
if (!shadingFill) {
|
||||||
|
matrix = Util.transform(matrix, owner.baseTransform);
|
||||||
|
if (this.matrix) {
|
||||||
|
matrix = Util.transform(matrix, this.matrix);
|
||||||
}
|
}
|
||||||
return { scale, size };
|
}
|
||||||
},
|
|
||||||
|
|
||||||
clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
|
const temporaryPatternCanvas = this.createPatternCanvas(owner);
|
||||||
if (Array.isArray(bbox) && bbox.length === 4) {
|
|
||||||
const bboxWidth = x1 - x0;
|
|
||||||
const bboxHeight = y1 - y0;
|
|
||||||
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
|
||||||
graphics.clip();
|
|
||||||
graphics.endPath();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setFillAndStrokeStyleToContext: function setFillAndStrokeStyleToContext(
|
let domMatrix = createMatrix(matrix);
|
||||||
graphics,
|
// Rescale and so that the ctx.createPattern call generates a pattern with
|
||||||
paintType,
|
// the desired size.
|
||||||
color
|
domMatrix = domMatrix.scale(
|
||||||
) {
|
1 / temporaryPatternCanvas.scaleX,
|
||||||
const context = graphics.ctx,
|
1 / temporaryPatternCanvas.scaleY
|
||||||
current = graphics.current;
|
);
|
||||||
switch (paintType) {
|
|
||||||
case PaintType.COLORED:
|
|
||||||
const ctx = this.ctx;
|
|
||||||
context.fillStyle = ctx.fillStyle;
|
|
||||||
context.strokeStyle = ctx.strokeStyle;
|
|
||||||
current.fillColor = ctx.fillStyle;
|
|
||||||
current.strokeColor = ctx.strokeStyle;
|
|
||||||
break;
|
|
||||||
case PaintType.UNCOLORED:
|
|
||||||
const cssColor = Util.makeHexColor(color[0], color[1], color[2]);
|
|
||||||
context.fillStyle = cssColor;
|
|
||||||
context.strokeStyle = cssColor;
|
|
||||||
// Set color needed by image masks (fixes issues 3226 and 8741).
|
|
||||||
current.fillColor = cssColor;
|
|
||||||
current.strokeColor = cssColor;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new FormatError(`Unsupported paint type: ${paintType}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getPattern: function TilingPattern_getPattern(ctx, owner, shadingFill) {
|
const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
|
||||||
ctx = this.ctx;
|
pattern.setTransform(domMatrix);
|
||||||
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial 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 pattern;
|
||||||
|
}
|
||||||
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;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return TilingPattern;
|
|
||||||
})();
|
|
||||||
|
|
||||||
export { getShadingPatternFromIR, TilingPattern };
|
export { getShadingPatternFromIR, TilingPattern };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user