2012-09-01 07:48:21 +09:00
|
|
|
/* Copyright 2012 Mozilla Foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2011-10-26 10:18:22 +09:00
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2015-11-22 01:32:47 +09:00
|
|
|
(function (root, factory) {
|
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
|
|
define('pdfjs/display/canvas', ['exports', 'pdfjs/shared/util',
|
2016-03-03 09:48:21 +09:00
|
|
|
'pdfjs/display/dom_utils', 'pdfjs/display/pattern_helper',
|
|
|
|
'pdfjs/display/webgl'], factory);
|
2015-11-22 01:32:47 +09:00
|
|
|
} else if (typeof exports !== 'undefined') {
|
2016-03-03 09:48:21 +09:00
|
|
|
factory(exports, require('../shared/util.js'), require('./dom_utils.js'),
|
2015-11-22 01:32:47 +09:00
|
|
|
require('./pattern_helper.js'), require('./webgl.js'));
|
|
|
|
} else {
|
|
|
|
factory((root.pdfjsDisplayCanvas = {}), root.pdfjsSharedUtil,
|
2016-03-03 09:48:21 +09:00
|
|
|
root.pdfjsDisplayDOMUtils, root.pdfjsDisplayPatternHelper,
|
|
|
|
root.pdfjsDisplayWebGL);
|
2015-11-22 01:32:47 +09:00
|
|
|
}
|
2016-03-03 09:48:21 +09:00
|
|
|
}(this, function (exports, sharedUtil, displayDOMUtils, displayPatternHelper,
|
|
|
|
displayWebGL) {
|
2015-11-22 01:32:47 +09:00
|
|
|
|
|
|
|
var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
|
|
|
|
var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
|
|
|
|
var ImageKind = sharedUtil.ImageKind;
|
|
|
|
var OPS = sharedUtil.OPS;
|
|
|
|
var TextRenderingMode = sharedUtil.TextRenderingMode;
|
|
|
|
var Util = sharedUtil.Util;
|
|
|
|
var assert = sharedUtil.assert;
|
|
|
|
var info = sharedUtil.info;
|
|
|
|
var isNum = sharedUtil.isNum;
|
|
|
|
var isArray = sharedUtil.isArray;
|
2016-03-29 04:49:22 +09:00
|
|
|
var isLittleEndian = sharedUtil.isLittleEndian;
|
2015-11-22 01:32:47 +09:00
|
|
|
var error = sharedUtil.error;
|
|
|
|
var shadow = sharedUtil.shadow;
|
|
|
|
var warn = sharedUtil.warn;
|
|
|
|
var TilingPattern = displayPatternHelper.TilingPattern;
|
|
|
|
var getShadingPatternFromIR = displayPatternHelper.getShadingPatternFromIR;
|
|
|
|
var WebGLUtils = displayWebGL.WebGLUtils;
|
|
|
|
|
2011-10-28 03:51:10 +09:00
|
|
|
// <canvas> contexts store most of the state we need natively.
|
|
|
|
// However, PDF needs a bit more state, which we store here.
|
|
|
|
|
2012-02-05 05:42:07 +09:00
|
|
|
// Minimal font size that would be used during canvas fillText operations.
|
2013-04-17 00:18:07 +09:00
|
|
|
var MIN_FONT_SIZE = 16;
|
2014-10-25 10:48:31 +09:00
|
|
|
// Maximum font size that would be used during canvas fillText operations.
|
|
|
|
var MAX_FONT_SIZE = 100;
|
2014-02-13 22:01:45 +09:00
|
|
|
var MAX_GROUP_SIZE = 4096;
|
2012-02-05 03:45:18 +09:00
|
|
|
|
2014-04-12 03:19:39 +09:00
|
|
|
// Heuristic value used when enforcing minimum line widths.
|
|
|
|
var MIN_WIDTH_FACTOR = 0.65;
|
|
|
|
|
2013-05-11 12:50:14 +09:00
|
|
|
var COMPILE_TYPE3_GLYPHS = true;
|
2014-10-27 02:20:04 +09:00
|
|
|
var MAX_SIZE_TO_COMPILE = 1000;
|
|
|
|
|
|
|
|
var FULL_CHUNK_HEIGHT = 16;
|
2013-05-11 12:50:14 +09:00
|
|
|
|
2016-03-29 04:49:22 +09:00
|
|
|
var IsLittleEndianCached = {
|
|
|
|
get value() {
|
|
|
|
return shadow(IsLittleEndianCached, 'value', isLittleEndian());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-11-27 17:22:08 +09:00
|
|
|
function addContextCurrentTransform(ctx) {
|
2015-03-12 04:55:26 +09:00
|
|
|
// If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
|
2011-11-23 05:00:04 +09:00
|
|
|
if (!ctx.mozCurrentTransform) {
|
|
|
|
ctx._originalSave = ctx.save;
|
|
|
|
ctx._originalRestore = ctx.restore;
|
|
|
|
ctx._originalRotate = ctx.rotate;
|
|
|
|
ctx._originalScale = ctx.scale;
|
|
|
|
ctx._originalTranslate = ctx.translate;
|
|
|
|
ctx._originalTransform = ctx.transform;
|
2013-05-01 02:01:01 +09:00
|
|
|
ctx._originalSetTransform = ctx.setTransform;
|
2011-11-23 05:00:04 +09:00
|
|
|
|
2015-03-12 04:55:26 +09:00
|
|
|
ctx._transformMatrix = ctx._transformMatrix || [1, 0, 0, 1, 0, 0];
|
2011-11-23 05:00:04 +09:00
|
|
|
ctx._transformStack = [];
|
|
|
|
|
2011-11-27 17:22:08 +09:00
|
|
|
Object.defineProperty(ctx, 'mozCurrentTransform', {
|
|
|
|
get: function getCurrentTransform() {
|
|
|
|
return this._transformMatrix;
|
|
|
|
}
|
2011-11-23 05:00:04 +09:00
|
|
|
});
|
|
|
|
|
2011-11-27 17:22:08 +09:00
|
|
|
Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
|
|
|
|
get: function getCurrentTransformInverse() {
|
2011-11-23 05:00:04 +09:00
|
|
|
// Calculation done using WolframAlpha:
|
|
|
|
// http://www.wolframalpha.com/input/?
|
|
|
|
// i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
|
|
|
|
|
|
|
|
var m = this._transformMatrix;
|
|
|
|
var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
|
|
|
|
2011-11-27 17:22:08 +09:00
|
|
|
var ad_bc = a * d - b * c;
|
|
|
|
var bc_ad = b * c - a * d;
|
|
|
|
|
2011-11-23 05:00:04 +09:00
|
|
|
return [
|
2011-11-27 17:22:08 +09:00
|
|
|
d / ad_bc,
|
|
|
|
b / bc_ad,
|
|
|
|
c / bc_ad,
|
|
|
|
a / ad_bc,
|
|
|
|
(d * e - c * f) / bc_ad,
|
|
|
|
(b * e - a * f) / ad_bc
|
2011-11-23 05:00:04 +09:00
|
|
|
];
|
|
|
|
}
|
2011-11-27 17:22:08 +09:00
|
|
|
});
|
2011-11-23 05:00:04 +09:00
|
|
|
|
|
|
|
ctx.save = function ctxSave() {
|
|
|
|
var old = this._transformMatrix;
|
|
|
|
this._transformStack.push(old);
|
|
|
|
this._transformMatrix = old.slice(0, 6);
|
|
|
|
|
|
|
|
this._originalSave();
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx.restore = function ctxRestore() {
|
|
|
|
var prev = this._transformStack.pop();
|
|
|
|
if (prev) {
|
|
|
|
this._transformMatrix = prev;
|
|
|
|
this._originalRestore();
|
|
|
|
}
|
2011-11-27 17:22:08 +09:00
|
|
|
};
|
2011-11-23 05:00:04 +09:00
|
|
|
|
|
|
|
ctx.translate = function ctxTranslate(x, y) {
|
|
|
|
var m = this._transformMatrix;
|
|
|
|
m[4] = m[0] * x + m[2] * y + m[4];
|
|
|
|
m[5] = m[1] * x + m[3] * y + m[5];
|
|
|
|
|
|
|
|
this._originalTranslate(x, y);
|
2011-11-27 17:22:08 +09:00
|
|
|
};
|
2011-11-23 05:00:04 +09:00
|
|
|
|
|
|
|
ctx.scale = function ctxScale(x, y) {
|
|
|
|
var m = this._transformMatrix;
|
|
|
|
m[0] = m[0] * x;
|
|
|
|
m[1] = m[1] * x;
|
|
|
|
m[2] = m[2] * y;
|
|
|
|
m[3] = m[3] * y;
|
|
|
|
|
|
|
|
this._originalScale(x, y);
|
2011-11-27 17:22:08 +09:00
|
|
|
};
|
2011-11-23 05:00:04 +09:00
|
|
|
|
|
|
|
ctx.transform = function ctxTransform(a, b, c, d, e, f) {
|
|
|
|
var m = this._transformMatrix;
|
|
|
|
this._transformMatrix = [
|
|
|
|
m[0] * a + m[2] * b,
|
|
|
|
m[1] * a + m[3] * b,
|
|
|
|
m[0] * c + m[2] * d,
|
|
|
|
m[1] * c + m[3] * d,
|
|
|
|
m[0] * e + m[2] * f + m[4],
|
|
|
|
m[1] * e + m[3] * f + m[5]
|
|
|
|
];
|
|
|
|
|
|
|
|
ctx._originalTransform(a, b, c, d, e, f);
|
2011-11-27 17:22:08 +09:00
|
|
|
};
|
2011-11-23 05:00:04 +09:00
|
|
|
|
2013-05-01 02:01:01 +09:00
|
|
|
ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
|
|
|
|
this._transformMatrix = [a, b, c, d, e, f];
|
|
|
|
|
|
|
|
ctx._originalSetTransform(a, b, c, d, e, f);
|
|
|
|
};
|
|
|
|
|
2011-11-23 05:00:04 +09:00
|
|
|
ctx.rotate = function ctxRotate(angle) {
|
|
|
|
var cosValue = Math.cos(angle);
|
|
|
|
var sinValue = Math.sin(angle);
|
|
|
|
|
|
|
|
var m = this._transformMatrix;
|
|
|
|
this._transformMatrix = [
|
|
|
|
m[0] * cosValue + m[2] * sinValue,
|
|
|
|
m[1] * cosValue + m[3] * sinValue,
|
|
|
|
m[0] * (-sinValue) + m[2] * cosValue,
|
|
|
|
m[1] * (-sinValue) + m[3] * cosValue,
|
|
|
|
m[4],
|
|
|
|
m[5]
|
|
|
|
];
|
|
|
|
|
|
|
|
this._originalRotate(angle);
|
2011-11-27 17:22:08 +09:00
|
|
|
};
|
2011-11-23 05:00:04 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
var CachedCanvases = (function CachedCanvasesClosure() {
|
2017-01-28 02:58:39 +09:00
|
|
|
function CachedCanvases(canvasFactory) {
|
|
|
|
this.canvasFactory = canvasFactory;
|
2015-11-17 01:50:02 +09:00
|
|
|
this.cache = Object.create(null);
|
|
|
|
}
|
|
|
|
CachedCanvases.prototype = {
|
2013-08-16 23:50:48 +09:00
|
|
|
getCanvas: function CachedCanvases_getCanvas(id, width, height,
|
|
|
|
trackTransform) {
|
|
|
|
var canvasEntry;
|
2015-11-17 01:50:02 +09:00
|
|
|
if (this.cache[id] !== undefined) {
|
|
|
|
canvasEntry = this.cache[id];
|
2017-02-07 06:19:56 +09:00
|
|
|
this.canvasFactory.reset(canvasEntry, width, height);
|
2013-05-31 09:42:26 +09:00
|
|
|
// reset canvas transform for emulated mozCurrentTransform, if needed
|
2013-08-16 23:50:48 +09:00
|
|
|
canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
|
2013-05-31 09:42:26 +09:00
|
|
|
} else {
|
2017-02-07 06:19:56 +09:00
|
|
|
canvasEntry = this.canvasFactory.create(width, height);
|
|
|
|
this.cache[id] = canvasEntry;
|
|
|
|
}
|
|
|
|
if (trackTransform) {
|
|
|
|
addContextCurrentTransform(canvasEntry.context);
|
2013-05-31 09:42:26 +09:00
|
|
|
}
|
2013-08-16 23:50:48 +09:00
|
|
|
return canvasEntry;
|
2013-05-31 09:42:26 +09:00
|
|
|
},
|
|
|
|
clear: function () {
|
2015-11-17 01:50:02 +09:00
|
|
|
for (var id in this.cache) {
|
|
|
|
var canvasEntry = this.cache[id];
|
2017-02-07 06:19:56 +09:00
|
|
|
this.canvasFactory.destroy(canvasEntry);
|
2015-11-17 01:50:02 +09:00
|
|
|
delete this.cache[id];
|
2014-06-17 14:58:11 +09:00
|
|
|
}
|
2013-05-31 09:42:26 +09:00
|
|
|
}
|
|
|
|
};
|
2015-11-17 01:50:02 +09:00
|
|
|
return CachedCanvases;
|
2013-05-31 09:42:26 +09:00
|
|
|
})();
|
|
|
|
|
2013-05-11 12:50:14 +09:00
|
|
|
function compileType3Glyph(imgData) {
|
|
|
|
var POINT_TO_PROCESS_LIMIT = 1000;
|
|
|
|
|
|
|
|
var width = imgData.width, height = imgData.height;
|
2013-06-12 04:01:10 +09:00
|
|
|
var i, j, j0, width1 = width + 1;
|
|
|
|
var points = new Uint8Array(width1 * (height + 1));
|
2013-07-02 01:25:46 +09:00
|
|
|
var POINT_TYPES =
|
2013-06-12 04:01:10 +09:00
|
|
|
new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
|
2014-01-14 12:21:03 +09:00
|
|
|
|
|
|
|
// decodes bit-packed mask data
|
|
|
|
var lineSize = (width + 7) & ~7, data0 = imgData.data;
|
|
|
|
var data = new Uint8Array(lineSize * height), pos = 0, ii;
|
|
|
|
for (i = 0, ii = data0.length; i < ii; i++) {
|
|
|
|
var mask = 128, elem = data0[i];
|
|
|
|
while (mask > 0) {
|
|
|
|
data[pos++] = (elem & mask) ? 0 : 255;
|
|
|
|
mask >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-11 12:50:14 +09:00
|
|
|
// finding iteresting points: every point is located between mask pixels,
|
|
|
|
// so there will be points of the (width + 1)x(height + 1) grid. Every point
|
|
|
|
// will have flags assigned based on neighboring mask pixels:
|
|
|
|
// 4 | 8
|
|
|
|
// --P--
|
|
|
|
// 2 | 1
|
|
|
|
// We are interested only in points with the flags:
|
|
|
|
// - outside corners: 1, 2, 4, 8;
|
|
|
|
// - inside corners: 7, 11, 13, 14;
|
|
|
|
// - and, intersections: 5, 10.
|
2014-01-14 12:21:03 +09:00
|
|
|
var count = 0;
|
|
|
|
pos = 0;
|
|
|
|
if (data[pos] !== 0) {
|
2013-06-11 22:40:26 +09:00
|
|
|
points[0] = 1;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
for (j = 1; j < width; j++) {
|
2014-01-14 12:21:03 +09:00
|
|
|
if (data[pos] !== data[pos + 1]) {
|
2013-06-11 22:40:26 +09:00
|
|
|
points[j] = data[pos] ? 2 : 1;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
2014-01-14 12:21:03 +09:00
|
|
|
pos++;
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
|
|
|
if (data[pos] !== 0) {
|
2013-06-11 22:40:26 +09:00
|
|
|
points[j] = 2;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
for (i = 1; i < height; i++) {
|
2014-01-14 12:21:03 +09:00
|
|
|
pos = i * lineSize;
|
2013-06-12 04:01:10 +09:00
|
|
|
j0 = i * width1;
|
2013-05-11 12:50:14 +09:00
|
|
|
if (data[pos - lineSize] !== data[pos]) {
|
2013-06-11 22:40:26 +09:00
|
|
|
points[j0] = data[pos] ? 1 : 8;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
2013-06-11 22:40:26 +09:00
|
|
|
// 'sum' is the position of the current pixel configuration in the 'TYPES'
|
|
|
|
// array (in order 8-1-2-4, so we can use '>>2' to shift the column).
|
|
|
|
var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
|
2013-05-11 12:50:14 +09:00
|
|
|
for (j = 1; j < width; j++) {
|
2014-01-14 12:21:03 +09:00
|
|
|
sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) +
|
|
|
|
(data[pos - lineSize + 1] ? 8 : 0);
|
2013-07-02 01:25:46 +09:00
|
|
|
if (POINT_TYPES[sum]) {
|
2013-06-12 04:01:10 +09:00
|
|
|
points[j0 + j] = POINT_TYPES[sum];
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
2014-01-14 12:21:03 +09:00
|
|
|
pos++;
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
|
|
|
if (data[pos - lineSize] !== data[pos]) {
|
2013-06-11 22:40:26 +09:00
|
|
|
points[j0 + j] = data[pos] ? 2 : 4;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > POINT_TO_PROCESS_LIMIT) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2013-06-12 04:01:10 +09:00
|
|
|
|
2014-01-14 12:21:03 +09:00
|
|
|
pos = lineSize * (height - 1);
|
2013-06-12 04:01:10 +09:00
|
|
|
j0 = i * width1;
|
2013-05-11 12:50:14 +09:00
|
|
|
if (data[pos] !== 0) {
|
2013-06-12 04:01:10 +09:00
|
|
|
points[j0] = 8;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
for (j = 1; j < width; j++) {
|
2014-01-14 12:21:03 +09:00
|
|
|
if (data[pos] !== data[pos + 1]) {
|
2013-06-12 04:01:10 +09:00
|
|
|
points[j0 + j] = data[pos] ? 4 : 8;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
2014-01-14 12:21:03 +09:00
|
|
|
pos++;
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
|
|
|
if (data[pos] !== 0) {
|
2013-06-12 04:01:10 +09:00
|
|
|
points[j0 + j] = 4;
|
2013-05-11 12:50:14 +09:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
if (count > POINT_TO_PROCESS_LIMIT) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// building outlines
|
2013-06-12 04:01:10 +09:00
|
|
|
var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
|
2013-06-11 22:40:26 +09:00
|
|
|
var outlines = [];
|
|
|
|
for (i = 0; count && i <= height; i++) {
|
|
|
|
var p = i * width1;
|
|
|
|
var end = p + width;
|
|
|
|
while (p < end && !points[p]) {
|
|
|
|
p++;
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
2013-06-11 22:40:26 +09:00
|
|
|
if (p === end) {
|
2013-05-11 12:50:14 +09:00
|
|
|
continue;
|
|
|
|
}
|
2013-06-12 04:01:10 +09:00
|
|
|
var coords = [p % width1, i];
|
2013-05-11 12:50:14 +09:00
|
|
|
|
2013-06-12 04:01:10 +09:00
|
|
|
var type = points[p], p0 = p, pp;
|
2013-05-11 12:50:14 +09:00
|
|
|
do {
|
2013-06-11 22:40:26 +09:00
|
|
|
var step = steps[type];
|
2014-03-14 21:11:11 +09:00
|
|
|
do {
|
|
|
|
p += step;
|
|
|
|
} while (!points[p]);
|
2013-07-02 01:25:46 +09:00
|
|
|
|
2013-06-12 04:01:10 +09:00
|
|
|
pp = points[p];
|
|
|
|
if (pp !== 5 && pp !== 10) {
|
2013-06-11 22:40:26 +09:00
|
|
|
// set new direction
|
2013-07-02 01:25:46 +09:00
|
|
|
type = pp;
|
2013-06-11 22:40:26 +09:00
|
|
|
// delete mark
|
2013-07-02 01:25:46 +09:00
|
|
|
points[p] = 0;
|
2013-06-11 22:40:26 +09:00
|
|
|
} else { // type is 5 or 10, ie, a crossing
|
|
|
|
// set new direction
|
2013-07-02 01:25:46 +09:00
|
|
|
type = pp & ((0x33 * type) >> 4);
|
2013-06-11 22:40:26 +09:00
|
|
|
// set new type for "future hit"
|
|
|
|
points[p] &= (type >> 2 | type << 2);
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
|
|
|
|
2013-06-12 04:01:10 +09:00
|
|
|
coords.push(p % width1);
|
|
|
|
coords.push((p / width1) | 0);
|
2013-06-11 22:40:26 +09:00
|
|
|
--count;
|
|
|
|
} while (p0 !== p);
|
2013-06-12 04:01:10 +09:00
|
|
|
outlines.push(coords);
|
2013-05-11 12:50:14 +09:00
|
|
|
--i;
|
|
|
|
}
|
|
|
|
|
2013-06-11 22:40:26 +09:00
|
|
|
var drawOutline = function(c) {
|
|
|
|
c.save();
|
|
|
|
// the path shall be painted in [0..1]x[0..1] space
|
|
|
|
c.scale(1 / width, -1 / height);
|
|
|
|
c.translate(0, -height);
|
|
|
|
c.beginPath();
|
2013-06-12 04:01:10 +09:00
|
|
|
for (var i = 0, ii = outlines.length; i < ii; i++) {
|
|
|
|
var o = outlines[i];
|
|
|
|
c.moveTo(o[0], o[1]);
|
|
|
|
for (var j = 2, jj = o.length; j < jj; j += 2) {
|
2016-11-01 23:04:21 +09:00
|
|
|
c.lineTo(o[j], o[j + 1]);
|
2013-06-11 22:40:26 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
c.fill();
|
|
|
|
c.beginPath();
|
|
|
|
c.restore();
|
|
|
|
};
|
2013-07-02 01:25:46 +09:00
|
|
|
|
2013-06-11 22:40:26 +09:00
|
|
|
return drawOutline;
|
2013-05-11 12:50:14 +09:00
|
|
|
}
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
|
|
|
function CanvasExtraState(old) {
|
|
|
|
// Are soft masks and alpha values shapes or opacities?
|
|
|
|
this.alphaIsShape = false;
|
|
|
|
this.fontSize = 0;
|
|
|
|
this.fontSizeScale = 1;
|
|
|
|
this.textMatrix = IDENTITY_MATRIX;
|
2014-05-24 03:36:54 +09:00
|
|
|
this.textMatrixScale = 1;
|
2013-01-04 09:39:06 +09:00
|
|
|
this.fontMatrix = FONT_IDENTITY_MATRIX;
|
2012-04-05 05:43:26 +09:00
|
|
|
this.leading = 0;
|
|
|
|
// Current point (in user coordinates)
|
|
|
|
this.x = 0;
|
|
|
|
this.y = 0;
|
|
|
|
// Start of text line (in text coordinates)
|
|
|
|
this.lineX = 0;
|
|
|
|
this.lineY = 0;
|
|
|
|
// Character and word spacing
|
|
|
|
this.charSpacing = 0;
|
|
|
|
this.wordSpacing = 0;
|
|
|
|
this.textHScale = 1;
|
|
|
|
this.textRenderingMode = TextRenderingMode.FILL;
|
2012-08-02 05:10:48 +09:00
|
|
|
this.textRise = 0;
|
2012-04-05 05:43:26 +09:00
|
|
|
// Default fore and background colors
|
|
|
|
this.fillColor = '#000000';
|
|
|
|
this.strokeColor = '#000000';
|
2014-10-27 02:20:04 +09:00
|
|
|
this.patternFill = false;
|
2012-04-05 05:43:26 +09:00
|
|
|
// Note: fill alpha applies to all non-stroking operations
|
|
|
|
this.fillAlpha = 1;
|
|
|
|
this.strokeAlpha = 1;
|
|
|
|
this.lineWidth = 1;
|
2016-04-10 08:46:15 +09:00
|
|
|
this.activeSMask = null;
|
|
|
|
this.resumeSMaskCtx = null; // nonclonable field (see the save method below)
|
2012-04-05 05:43:26 +09:00
|
|
|
|
|
|
|
this.old = old;
|
|
|
|
}
|
|
|
|
|
|
|
|
CanvasExtraState.prototype = {
|
|
|
|
clone: function CanvasExtraState_clone() {
|
|
|
|
return Object.create(this);
|
|
|
|
},
|
|
|
|
setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return CanvasExtraState;
|
|
|
|
})();
|
|
|
|
|
2011-12-09 07:18:43 +09:00
|
|
|
var CanvasGraphics = (function CanvasGraphicsClosure() {
|
2012-03-13 02:41:40 +09:00
|
|
|
// Defines the time the executeOperatorList is going to be executing
|
2011-10-25 08:55:23 +09:00
|
|
|
// before it stops and shedules a continue of execution.
|
2012-11-10 06:34:11 +09:00
|
|
|
var EXECUTION_TIME = 15;
|
2014-06-02 21:40:33 +09:00
|
|
|
// Defines the number of steps before checking the execution time
|
|
|
|
var EXECUTION_STEPS = 10;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2017-01-28 02:58:39 +09:00
|
|
|
function CanvasGraphics(canvasCtx, commonObjs, objs, canvasFactory,
|
|
|
|
imageLayer) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx = canvasCtx;
|
|
|
|
this.current = new CanvasExtraState();
|
|
|
|
this.stateStack = [];
|
|
|
|
this.pendingClip = null;
|
2013-05-04 08:47:40 +09:00
|
|
|
this.pendingEOFill = false;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.res = null;
|
|
|
|
this.xobjs = null;
|
2012-10-29 05:10:34 +09:00
|
|
|
this.commonObjs = commonObjs;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.objs = objs;
|
2017-01-28 02:58:39 +09:00
|
|
|
this.canvasFactory = canvasFactory;
|
2013-02-11 03:19:36 +09:00
|
|
|
this.imageLayer = imageLayer;
|
2013-03-13 09:20:38 +09:00
|
|
|
this.groupStack = [];
|
2013-05-11 12:50:14 +09:00
|
|
|
this.processingType3 = null;
|
2013-07-23 06:52:44 +09:00
|
|
|
// Patterns are painted relative to the initial page/form transform, see pdf
|
|
|
|
// spec 8.7.2 NOTE 1.
|
|
|
|
this.baseTransform = null;
|
|
|
|
this.baseTransformStack = [];
|
2013-08-16 23:50:48 +09:00
|
|
|
this.groupLevel = 0;
|
2014-01-24 02:13:32 +09:00
|
|
|
this.smaskStack = [];
|
|
|
|
this.smaskCounter = 0;
|
|
|
|
this.tempSMask = null;
|
2017-01-28 02:58:39 +09:00
|
|
|
this.cachedCanvases = new CachedCanvases(this.canvasFactory);
|
2011-11-23 05:00:04 +09:00
|
|
|
if (canvasCtx) {
|
2015-03-12 04:55:26 +09:00
|
|
|
// NOTE: if mozCurrentTransform is polyfilled, then the current state of
|
|
|
|
// the transformation must already be set in canvasCtx._transformMatrix.
|
2011-11-27 17:22:08 +09:00
|
|
|
addContextCurrentTransform(canvasCtx);
|
2011-11-23 05:00:04 +09:00
|
|
|
}
|
2014-04-12 03:19:39 +09:00
|
|
|
this.cachedGetSinglePixelWidth = null;
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
function putBinaryImageData(ctx, imgData) {
|
|
|
|
if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
|
|
|
|
ctx.putImageData(imgData, 0, 0);
|
|
|
|
return;
|
2012-12-08 03:19:43 +09:00
|
|
|
}
|
|
|
|
|
2014-01-17 09:59:22 +09:00
|
|
|
// Put the image data to the canvas in chunks, rather than putting the
|
|
|
|
// whole image at once. This saves JS memory, because the ImageData object
|
|
|
|
// is smaller. It also possibly saves C++ memory within the implementation
|
|
|
|
// of putImageData(). (E.g. in Firefox we make two short-lived copies of
|
|
|
|
// the data passed to putImageData()). |n| shouldn't be too small, however,
|
|
|
|
// because too many putImageData() calls will slow things down.
|
2014-01-23 09:47:51 +09:00
|
|
|
//
|
|
|
|
// Note: as written, if the last chunk is partial, the putImageData() call
|
|
|
|
// will (conceptually) put pixels past the bounds of the canvas. But
|
|
|
|
// that's ok; any such pixels are ignored.
|
|
|
|
|
2014-01-30 04:10:34 +09:00
|
|
|
var height = imgData.height, width = imgData.width;
|
2014-10-27 02:20:04 +09:00
|
|
|
var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
|
|
|
|
var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
|
|
|
|
var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
|
2014-01-23 09:47:51 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
|
2014-04-09 04:48:16 +09:00
|
|
|
var srcPos = 0, destPos;
|
2014-01-17 09:59:22 +09:00
|
|
|
var src = imgData.data;
|
2014-02-25 12:41:04 +09:00
|
|
|
var dest = chunkImgData.data;
|
2014-04-09 04:48:16 +09:00
|
|
|
var i, j, thisChunkHeight, elemsInThisChunk;
|
2014-01-17 09:59:22 +09:00
|
|
|
|
2014-01-23 09:47:51 +09:00
|
|
|
// There are multiple forms in which the pixel data can be passed, and
|
|
|
|
// imgData.kind tells us which one this is.
|
2014-02-26 08:11:15 +09:00
|
|
|
if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
|
2014-01-23 09:47:51 +09:00
|
|
|
// Grayscale, 1 bit per pixel (i.e. black-and-white).
|
2014-02-25 12:41:04 +09:00
|
|
|
var srcLength = src.byteLength;
|
2017-04-14 03:16:35 +09:00
|
|
|
var dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
|
2014-03-21 00:57:11 +09:00
|
|
|
var dest32DataLength = dest32.length;
|
|
|
|
var fullSrcDiff = (width + 7) >> 3;
|
|
|
|
var white = 0xFFFFFFFF;
|
2017-04-14 03:16:35 +09:00
|
|
|
var black = IsLittleEndianCached.value ? 0xFF000000 : 0x000000FF;
|
2014-04-09 04:48:16 +09:00
|
|
|
for (i = 0; i < totalChunks; i++) {
|
|
|
|
thisChunkHeight =
|
2014-10-27 02:20:04 +09:00
|
|
|
(i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight;
|
2014-04-09 04:48:16 +09:00
|
|
|
destPos = 0;
|
|
|
|
for (j = 0; j < thisChunkHeight; j++) {
|
2014-03-21 00:57:11 +09:00
|
|
|
var srcDiff = srcLength - srcPos;
|
|
|
|
var k = 0;
|
|
|
|
var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7;
|
|
|
|
var kEndUnrolled = kEnd & ~7;
|
2014-01-23 09:47:51 +09:00
|
|
|
var mask = 0;
|
|
|
|
var srcByte = 0;
|
2014-03-21 00:57:11 +09:00
|
|
|
for (; k < kEndUnrolled; k += 8) {
|
|
|
|
srcByte = src[srcPos++];
|
|
|
|
dest32[destPos++] = (srcByte & 128) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 64) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 32) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 16) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 8) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 4) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 2) ? white : black;
|
|
|
|
dest32[destPos++] = (srcByte & 1) ? white : black;
|
|
|
|
}
|
|
|
|
for (; k < kEnd; k++) {
|
|
|
|
if (mask === 0) {
|
|
|
|
srcByte = src[srcPos++];
|
|
|
|
mask = 128;
|
|
|
|
}
|
2014-01-23 09:47:51 +09:00
|
|
|
|
2014-03-21 00:57:11 +09:00
|
|
|
dest32[destPos++] = (srcByte & mask) ? white : black;
|
2014-01-23 09:47:51 +09:00
|
|
|
mask >>= 1;
|
|
|
|
}
|
2014-01-17 09:59:22 +09:00
|
|
|
}
|
2014-03-21 00:57:11 +09:00
|
|
|
// We ran out of input. Make all remaining pixels transparent.
|
|
|
|
while (destPos < dest32DataLength) {
|
|
|
|
dest32[destPos++] = 0;
|
2014-01-30 04:10:34 +09:00
|
|
|
}
|
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
|
2014-01-17 09:59:22 +09:00
|
|
|
}
|
2014-02-26 08:11:15 +09:00
|
|
|
} else if (imgData.kind === ImageKind.RGBA_32BPP) {
|
2014-01-23 09:47:51 +09:00
|
|
|
// RGBA, 32-bits per pixel.
|
|
|
|
|
2014-04-29 00:19:56 +09:00
|
|
|
j = 0;
|
2014-10-27 02:20:04 +09:00
|
|
|
elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
|
2014-04-29 00:19:56 +09:00
|
|
|
for (i = 0; i < fullChunks; i++) {
|
2014-03-11 12:18:37 +09:00
|
|
|
dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
|
|
|
|
srcPos += elemsInThisChunk;
|
|
|
|
|
2014-04-29 00:19:56 +09:00
|
|
|
ctx.putImageData(chunkImgData, 0, j);
|
2014-10-27 02:20:04 +09:00
|
|
|
j += FULL_CHUNK_HEIGHT;
|
2014-04-29 00:19:56 +09:00
|
|
|
}
|
|
|
|
if (i < totalChunks) {
|
|
|
|
elemsInThisChunk = width * partialChunkHeight * 4;
|
|
|
|
dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
|
|
|
|
ctx.putImageData(chunkImgData, 0, j);
|
2014-01-17 09:59:22 +09:00
|
|
|
}
|
2014-04-29 00:19:56 +09:00
|
|
|
|
2014-02-26 08:11:15 +09:00
|
|
|
} else if (imgData.kind === ImageKind.RGB_24BPP) {
|
2014-02-25 12:37:19 +09:00
|
|
|
// RGB, 24-bits per pixel.
|
2014-10-27 02:20:04 +09:00
|
|
|
thisChunkHeight = FULL_CHUNK_HEIGHT;
|
2014-04-29 00:19:56 +09:00
|
|
|
elemsInThisChunk = width * thisChunkHeight;
|
2014-04-09 04:48:16 +09:00
|
|
|
for (i = 0; i < totalChunks; i++) {
|
2014-04-29 00:19:56 +09:00
|
|
|
if (i >= fullChunks) {
|
2014-10-27 02:20:04 +09:00
|
|
|
thisChunkHeight = partialChunkHeight;
|
2014-04-29 00:19:56 +09:00
|
|
|
elemsInThisChunk = width * thisChunkHeight;
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:48:16 +09:00
|
|
|
destPos = 0;
|
2014-04-29 00:19:56 +09:00
|
|
|
for (j = elemsInThisChunk; j--;) {
|
2014-02-25 12:41:04 +09:00
|
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = src[srcPos++];
|
|
|
|
dest[destPos++] = 255;
|
2014-02-25 12:37:19 +09:00
|
|
|
}
|
2014-10-27 02:20:04 +09:00
|
|
|
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
|
2014-02-25 12:37:19 +09:00
|
|
|
}
|
2014-01-23 09:47:51 +09:00
|
|
|
} else {
|
2014-04-09 04:48:16 +09:00
|
|
|
error('bad image kind: ' + imgData.kind);
|
2014-01-17 09:59:22 +09:00
|
|
|
}
|
2012-12-22 07:31:57 +09:00
|
|
|
}
|
|
|
|
|
2014-01-14 11:08:39 +09:00
|
|
|
function putBinaryImageMask(ctx, imgData) {
|
2014-02-28 14:15:38 +09:00
|
|
|
var height = imgData.height, width = imgData.width;
|
2014-10-27 02:20:04 +09:00
|
|
|
var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
|
|
|
|
var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
|
|
|
|
var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
|
2014-02-28 14:15:38 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
|
2014-02-28 14:15:38 +09:00
|
|
|
var srcPos = 0;
|
|
|
|
var src = imgData.data;
|
|
|
|
var dest = chunkImgData.data;
|
|
|
|
|
|
|
|
for (var i = 0; i < totalChunks; i++) {
|
|
|
|
var thisChunkHeight =
|
2014-10-27 02:20:04 +09:00
|
|
|
(i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight;
|
2014-02-28 14:15:38 +09:00
|
|
|
|
|
|
|
// Expand the mask so it can be used by the canvas. Any required
|
|
|
|
// inversion has already been handled.
|
|
|
|
var destPos = 3; // alpha component offset
|
|
|
|
for (var j = 0; j < thisChunkHeight; j++) {
|
|
|
|
var mask = 0;
|
|
|
|
for (var k = 0; k < width; k++) {
|
|
|
|
if (!mask) {
|
|
|
|
var elem = src[srcPos++];
|
|
|
|
mask = 128;
|
|
|
|
}
|
|
|
|
dest[destPos] = (elem & mask) ? 0 : 255;
|
|
|
|
destPos += 4;
|
|
|
|
mask >>= 1;
|
2014-01-14 11:08:39 +09:00
|
|
|
}
|
|
|
|
}
|
2014-10-27 02:20:04 +09:00
|
|
|
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
|
2014-01-14 11:08:39 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-13 09:20:38 +09:00
|
|
|
function copyCtxState(sourceCtx, destCtx) {
|
|
|
|
var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
|
|
|
|
'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
|
|
|
|
'globalCompositeOperation', 'font'];
|
|
|
|
for (var i = 0, ii = properties.length; i < ii; i++) {
|
|
|
|
var property = properties[i];
|
2014-10-27 02:20:04 +09:00
|
|
|
if (sourceCtx[property] !== undefined) {
|
2013-03-13 09:20:38 +09:00
|
|
|
destCtx[property] = sourceCtx[property];
|
|
|
|
}
|
|
|
|
}
|
2014-10-27 02:20:04 +09:00
|
|
|
if (sourceCtx.setLineDash !== undefined) {
|
2013-03-13 09:20:38 +09:00
|
|
|
destCtx.setLineDash(sourceCtx.getLineDash());
|
2016-12-10 22:28:27 +09:00
|
|
|
destCtx.lineDashOffset = sourceCtx.lineDashOffset;
|
2013-03-13 09:20:38 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 21:54:40 +09:00
|
|
|
function composeSMaskBackdrop(bytes, r0, g0, b0) {
|
|
|
|
var length = bytes.length;
|
|
|
|
for (var i = 3; i < length; i += 4) {
|
|
|
|
var alpha = bytes[i];
|
|
|
|
if (alpha === 0) {
|
|
|
|
bytes[i - 3] = r0;
|
|
|
|
bytes[i - 2] = g0;
|
|
|
|
bytes[i - 1] = b0;
|
|
|
|
} else if (alpha < 255) {
|
|
|
|
var alpha_ = 255 - alpha;
|
|
|
|
bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8;
|
|
|
|
bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8;
|
|
|
|
bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-05 03:52:45 +09:00
|
|
|
function composeSMaskAlpha(maskData, layerData, transferMap) {
|
2014-06-02 21:54:40 +09:00
|
|
|
var length = maskData.length;
|
|
|
|
var scale = 1 / 255;
|
|
|
|
for (var i = 3; i < length; i += 4) {
|
2015-12-05 03:52:45 +09:00
|
|
|
var alpha = transferMap ? transferMap[maskData[i]] : maskData[i];
|
2014-06-02 21:54:40 +09:00
|
|
|
layerData[i] = (layerData[i] * alpha * scale) | 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-05 03:52:45 +09:00
|
|
|
function composeSMaskLuminosity(maskData, layerData, transferMap) {
|
2014-06-02 21:54:40 +09:00
|
|
|
var length = maskData.length;
|
|
|
|
for (var i = 3; i < length; i += 4) {
|
2014-12-03 06:13:03 +09:00
|
|
|
var y = (maskData[i - 3] * 77) + // * 0.3 / 255 * 0x10000
|
|
|
|
(maskData[i - 2] * 152) + // * 0.59 ....
|
|
|
|
(maskData[i - 1] * 28); // * 0.11 ....
|
2015-12-05 03:52:45 +09:00
|
|
|
layerData[i] = transferMap ?
|
|
|
|
(layerData[i] * transferMap[y >> 8]) >> 8 :
|
|
|
|
(layerData[i] * y) >> 16;
|
2014-06-02 21:54:40 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-13 23:44:58 +09:00
|
|
|
function genericComposeSMask(maskCtx, layerCtx, width, height,
|
2015-12-05 03:52:45 +09:00
|
|
|
subtype, backdrop, transferMap) {
|
2014-06-11 05:47:25 +09:00
|
|
|
var hasBackdrop = !!backdrop;
|
2014-06-02 21:54:40 +09:00
|
|
|
var r0 = hasBackdrop ? backdrop[0] : 0;
|
|
|
|
var g0 = hasBackdrop ? backdrop[1] : 0;
|
|
|
|
var b0 = hasBackdrop ? backdrop[2] : 0;
|
2014-01-24 02:13:32 +09:00
|
|
|
|
|
|
|
var composeFn;
|
2014-02-13 23:44:58 +09:00
|
|
|
if (subtype === 'Luminosity') {
|
2014-06-02 21:54:40 +09:00
|
|
|
composeFn = composeSMaskLuminosity;
|
2014-01-24 02:13:32 +09:00
|
|
|
} else {
|
2014-06-02 21:54:40 +09:00
|
|
|
composeFn = composeSMaskAlpha;
|
2014-01-24 02:13:32 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
// processing image in chunks to save memory
|
2014-12-03 18:25:53 +09:00
|
|
|
var PIXELS_TO_PROCESS = 1048576;
|
2014-02-13 23:37:19 +09:00
|
|
|
var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));
|
2014-01-24 02:13:32 +09:00
|
|
|
for (var row = 0; row < height; row += chunkSize) {
|
|
|
|
var chunkHeight = Math.min(chunkSize, height - row);
|
|
|
|
var maskData = maskCtx.getImageData(0, row, width, chunkHeight);
|
|
|
|
var layerData = layerCtx.getImageData(0, row, width, chunkHeight);
|
|
|
|
|
2014-06-02 21:54:40 +09:00
|
|
|
if (hasBackdrop) {
|
|
|
|
composeSMaskBackdrop(maskData.data, r0, g0, b0);
|
|
|
|
}
|
2015-12-05 03:52:45 +09:00
|
|
|
composeFn(maskData.data, layerData.data, transferMap);
|
2014-01-24 02:13:32 +09:00
|
|
|
|
|
|
|
maskCtx.putImageData(layerData, 0, row);
|
|
|
|
}
|
2014-02-13 23:44:58 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
function composeSMask(ctx, smask, layerCtx) {
|
|
|
|
var mask = smask.canvas;
|
|
|
|
var maskCtx = smask.context;
|
2014-01-24 02:13:32 +09:00
|
|
|
|
2014-02-13 22:01:45 +09:00
|
|
|
ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY,
|
|
|
|
smask.offsetX, smask.offsetY);
|
2014-02-13 23:44:58 +09:00
|
|
|
|
2014-05-22 02:47:42 +09:00
|
|
|
var backdrop = smask.backdrop || null;
|
2015-12-05 03:52:45 +09:00
|
|
|
if (!smask.transferMap && WebGLUtils.isEnabled) {
|
2014-02-13 23:44:58 +09:00
|
|
|
var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask,
|
|
|
|
{subtype: smask.subtype, backdrop: backdrop});
|
|
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
ctx.drawImage(composed, smask.offsetX, smask.offsetY);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height,
|
2015-12-05 03:52:45 +09:00
|
|
|
smask.subtype, backdrop, smask.transferMap);
|
2014-02-13 22:01:45 +09:00
|
|
|
ctx.drawImage(mask, 0, 0);
|
2014-01-24 02:13:32 +09:00
|
|
|
}
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
|
|
|
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
|
|
|
|
var NORMAL_CLIP = {};
|
|
|
|
var EO_CLIP = {};
|
|
|
|
|
2011-12-09 07:18:43 +09:00
|
|
|
CanvasGraphics.prototype = {
|
2011-12-01 08:02:30 +09:00
|
|
|
|
2015-11-17 01:50:02 +09:00
|
|
|
beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport,
|
|
|
|
transparency) {
|
2013-03-30 05:26:25 +09:00
|
|
|
// For pdfs that use blend modes we have to clear the canvas else certain
|
|
|
|
// blend modes can look wrong since we'd be blending with a white
|
|
|
|
// backdrop. The problem with a transparent backdrop though is we then
|
2015-11-17 01:50:02 +09:00
|
|
|
// don't get sub pixel anti aliasing on text, creating temporary
|
|
|
|
// transparent canvas when we have blend modes.
|
2013-03-30 05:26:25 +09:00
|
|
|
var width = this.ctx.canvas.width;
|
|
|
|
var height = this.ctx.canvas.height;
|
2015-11-17 01:50:02 +09:00
|
|
|
|
|
|
|
this.ctx.save();
|
|
|
|
this.ctx.fillStyle = 'rgb(255, 255, 255)';
|
|
|
|
this.ctx.fillRect(0, 0, width, height);
|
|
|
|
this.ctx.restore();
|
|
|
|
|
2013-03-30 05:26:25 +09:00
|
|
|
if (transparency) {
|
2015-11-17 01:50:02 +09:00
|
|
|
var transparentCanvas = this.cachedCanvases.getCanvas(
|
|
|
|
'transparent', width, height, true);
|
|
|
|
this.compositeCtx = this.ctx;
|
|
|
|
this.transparentCanvas = transparentCanvas.canvas;
|
|
|
|
this.ctx = transparentCanvas.context;
|
2013-03-30 05:26:25 +09:00
|
|
|
this.ctx.save();
|
2015-11-17 01:50:02 +09:00
|
|
|
// The transform can be applied before rendering, transferring it to
|
|
|
|
// the new canvas.
|
|
|
|
this.ctx.transform.apply(this.ctx,
|
|
|
|
this.compositeCtx.mozCurrentTransform);
|
2013-03-30 05:26:25 +09:00
|
|
|
}
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.save();
|
2015-11-17 01:50:02 +09:00
|
|
|
if (transform) {
|
|
|
|
this.ctx.transform.apply(this.ctx, transform);
|
|
|
|
}
|
|
|
|
this.ctx.transform.apply(this.ctx, viewport.transform);
|
2011-12-19 03:53:30 +09:00
|
|
|
|
2014-01-27 22:17:14 +09:00
|
|
|
this.baseTransform = this.ctx.mozCurrentTransform.slice();
|
|
|
|
|
2013-02-11 03:19:36 +09:00
|
|
|
if (this.imageLayer) {
|
|
|
|
this.imageLayer.beginLayout();
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
executeOperatorList: function CanvasGraphics_executeOperatorList(
|
2012-03-13 02:41:40 +09:00
|
|
|
operatorList,
|
|
|
|
executionStartIdx, continueCallback,
|
|
|
|
stepper) {
|
|
|
|
var argsArray = operatorList.argsArray;
|
|
|
|
var fnArray = operatorList.fnArray;
|
2011-10-25 08:55:23 +09:00
|
|
|
var i = executionStartIdx || 0;
|
|
|
|
var argsArrayLen = argsArray.length;
|
|
|
|
|
2012-03-13 02:41:40 +09:00
|
|
|
// Sometimes the OperatorList to execute is empty.
|
2014-06-02 21:40:33 +09:00
|
|
|
if (argsArrayLen === i) {
|
2011-11-28 04:54:25 +09:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2014-06-02 21:40:33 +09:00
|
|
|
var chunkOperations = (argsArrayLen - i > EXECUTION_STEPS &&
|
|
|
|
typeof continueCallback === 'function');
|
|
|
|
var endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
|
|
|
|
var steps = 0;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-10-29 05:10:34 +09:00
|
|
|
var commonObjs = this.commonObjs;
|
2011-10-25 08:55:23 +09:00
|
|
|
var objs = this.objs;
|
2013-11-14 04:43:38 +09:00
|
|
|
var fnId;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2011-11-28 04:54:25 +09:00
|
|
|
while (true) {
|
2014-06-02 21:40:33 +09:00
|
|
|
if (stepper !== undefined && i === stepper.nextBreakPoint) {
|
2012-02-14 10:35:58 +09:00
|
|
|
stepper.breakIt(i, continueCallback);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2013-11-14 04:43:38 +09:00
|
|
|
fnId = fnArray[i];
|
2011-12-01 08:02:30 +09:00
|
|
|
|
2013-11-14 04:43:38 +09:00
|
|
|
if (fnId !== OPS.dependency) {
|
|
|
|
this[fnId].apply(this, argsArray[i]);
|
2011-11-28 04:54:25 +09:00
|
|
|
} else {
|
|
|
|
var deps = argsArray[i];
|
|
|
|
for (var n = 0, nn = deps.length; n < nn; n++) {
|
|
|
|
var depObjId = deps[n];
|
2014-06-02 21:40:33 +09:00
|
|
|
var common = depObjId[0] === 'g' && depObjId[1] === '_';
|
|
|
|
var objsPool = common ? commonObjs : objs;
|
2011-11-28 04:54:25 +09:00
|
|
|
|
|
|
|
// If the promise isn't resolved yet, add the continueCallback
|
|
|
|
// to the promise and bail out.
|
2014-06-02 21:40:33 +09:00
|
|
|
if (!objsPool.isResolved(depObjId)) {
|
|
|
|
objsPool.get(depObjId, continueCallback);
|
2012-10-29 05:10:34 +09:00
|
|
|
return i;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
}
|
2011-12-01 08:02:30 +09:00
|
|
|
|
2011-11-28 04:54:25 +09:00
|
|
|
i++;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-03-13 02:41:40 +09:00
|
|
|
// If the entire operatorList was executed, stop as were done.
|
2014-06-02 21:40:33 +09:00
|
|
|
if (i === argsArrayLen) {
|
2011-10-25 08:55:23 +09:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2014-05-09 21:00:47 +09:00
|
|
|
// If the execution took longer then a certain amount of time and
|
|
|
|
// `continueCallback` is specified, interrupt the execution.
|
2014-06-02 21:40:33 +09:00
|
|
|
if (chunkOperations && ++steps > EXECUTION_STEPS) {
|
|
|
|
if (Date.now() > endTime) {
|
|
|
|
continueCallback();
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
steps = 0;
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
|
2012-03-13 02:41:40 +09:00
|
|
|
// If the operatorList isn't executed completely yet OR the execution
|
|
|
|
// time was short enough, do another execution round.
|
2011-11-28 04:54:25 +09:00
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
endDrawing: function CanvasGraphics_endDrawing() {
|
2016-04-10 08:50:11 +09:00
|
|
|
// Finishing all opened operations such as SMask group painting.
|
|
|
|
if (this.current.activeSMask !== null) {
|
|
|
|
this.endSMaskGroup();
|
|
|
|
}
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.restore();
|
2015-11-17 01:50:02 +09:00
|
|
|
|
|
|
|
if (this.transparentCanvas) {
|
|
|
|
this.ctx = this.compositeCtx;
|
2016-01-21 10:25:39 +09:00
|
|
|
this.ctx.save();
|
|
|
|
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // Avoid apply transform twice
|
2015-11-17 01:50:02 +09:00
|
|
|
this.ctx.drawImage(this.transparentCanvas, 0, 0);
|
2016-01-21 10:25:39 +09:00
|
|
|
this.ctx.restore();
|
2015-11-17 01:50:02 +09:00
|
|
|
this.transparentCanvas = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cachedCanvases.clear();
|
2014-02-13 23:44:58 +09:00
|
|
|
WebGLUtils.clear();
|
2011-10-29 06:37:55 +09:00
|
|
|
|
2013-02-11 03:19:36 +09:00
|
|
|
if (this.imageLayer) {
|
|
|
|
this.imageLayer.endLayout();
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
// Graphics state
|
2012-04-05 05:43:26 +09:00
|
|
|
setLineWidth: function CanvasGraphics_setLineWidth(width) {
|
2012-01-18 13:50:49 +09:00
|
|
|
this.current.lineWidth = width;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.lineWidth = width;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setLineCap: function CanvasGraphics_setLineCap(style) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.lineCap = LINE_CAP_STYLES[style];
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setLineJoin: function CanvasGraphics_setLineJoin(style) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.lineJoin = LINE_JOIN_STYLES[style];
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.miterLimit = limit;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
|
2012-12-04 23:26:10 +09:00
|
|
|
var ctx = this.ctx;
|
2014-10-27 02:20:04 +09:00
|
|
|
if (ctx.setLineDash !== undefined) {
|
2012-12-04 23:26:10 +09:00
|
|
|
ctx.setLineDash(dashArray);
|
|
|
|
ctx.lineDashOffset = dashPhase;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
|
2012-05-15 09:19:09 +09:00
|
|
|
// Maybe if we one day fully support color spaces this will be important
|
|
|
|
// for now we can ignore.
|
|
|
|
// TODO set rendering intent?
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setFlatness: function CanvasGraphics_setFlatness(flatness) {
|
2012-05-15 09:19:09 +09:00
|
|
|
// There's no way to control this with canvas, but we can safely ignore.
|
|
|
|
// TODO set flatness?
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setGState: function CanvasGraphics_setGState(states) {
|
2011-11-03 04:21:45 +09:00
|
|
|
for (var i = 0, ii = states.length; i < ii; i++) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var state = states[i];
|
|
|
|
var key = state[0];
|
|
|
|
var value = state[1];
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case 'LW':
|
|
|
|
this.setLineWidth(value);
|
|
|
|
break;
|
|
|
|
case 'LC':
|
|
|
|
this.setLineCap(value);
|
|
|
|
break;
|
|
|
|
case 'LJ':
|
|
|
|
this.setLineJoin(value);
|
|
|
|
break;
|
|
|
|
case 'ML':
|
|
|
|
this.setMiterLimit(value);
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
this.setDash(value[0], value[1]);
|
|
|
|
break;
|
|
|
|
case 'RI':
|
|
|
|
this.setRenderingIntent(value);
|
|
|
|
break;
|
|
|
|
case 'FL':
|
|
|
|
this.setFlatness(value);
|
|
|
|
break;
|
|
|
|
case 'Font':
|
2013-08-01 03:17:36 +09:00
|
|
|
this.setFont(value[0], value[1]);
|
2011-10-25 08:55:23 +09:00
|
|
|
break;
|
2011-10-29 06:10:10 +09:00
|
|
|
case 'CA':
|
|
|
|
this.current.strokeAlpha = state[1];
|
|
|
|
break;
|
|
|
|
case 'ca':
|
|
|
|
this.current.fillAlpha = state[1];
|
|
|
|
this.ctx.globalAlpha = state[1];
|
|
|
|
break;
|
2013-03-12 02:23:47 +09:00
|
|
|
case 'BM':
|
2017-04-11 03:58:02 +09:00
|
|
|
this.ctx.globalCompositeOperation = value;
|
2013-03-12 02:23:47 +09:00
|
|
|
break;
|
2014-01-24 02:13:32 +09:00
|
|
|
case 'SMask':
|
|
|
|
if (this.current.activeSMask) {
|
2016-04-10 08:46:15 +09:00
|
|
|
// If SMask is currrenly used, it needs to be suspended or
|
|
|
|
// finished. Suspend only makes sense when at least one save()
|
|
|
|
// was performed and state needs to be reverted on restore().
|
|
|
|
if (this.stateStack.length > 0 &&
|
|
|
|
(this.stateStack[this.stateStack.length - 1].activeSMask ===
|
|
|
|
this.current.activeSMask)) {
|
|
|
|
this.suspendSMaskGroup();
|
|
|
|
} else {
|
|
|
|
this.endSMaskGroup();
|
|
|
|
}
|
2014-01-24 02:13:32 +09:00
|
|
|
}
|
|
|
|
this.current.activeSMask = value ? this.tempSMask : null;
|
|
|
|
if (this.current.activeSMask) {
|
|
|
|
this.beginSMaskGroup();
|
|
|
|
}
|
|
|
|
this.tempSMask = null;
|
|
|
|
break;
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2014-01-24 02:13:32 +09:00
|
|
|
beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() {
|
|
|
|
|
|
|
|
var activeSMask = this.current.activeSMask;
|
|
|
|
var drawnWidth = activeSMask.canvas.width;
|
|
|
|
var drawnHeight = activeSMask.canvas.height;
|
|
|
|
var cacheId = 'smaskGroupAt' + this.groupLevel;
|
2015-11-17 01:50:02 +09:00
|
|
|
var scratchCanvas = this.cachedCanvases.getCanvas(
|
2014-01-24 02:13:32 +09:00
|
|
|
cacheId, drawnWidth, drawnHeight, true);
|
|
|
|
|
|
|
|
var currentCtx = this.ctx;
|
|
|
|
var currentTransform = currentCtx.mozCurrentTransform;
|
|
|
|
this.ctx.save();
|
|
|
|
|
|
|
|
var groupCtx = scratchCanvas.context;
|
2014-02-13 22:01:45 +09:00
|
|
|
groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
|
2014-01-24 02:13:32 +09:00
|
|
|
groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
|
|
|
|
groupCtx.transform.apply(groupCtx, currentTransform);
|
|
|
|
|
2016-04-10 08:46:15 +09:00
|
|
|
activeSMask.startTransformInverse = groupCtx.mozCurrentTransformInverse;
|
|
|
|
|
2014-01-24 02:13:32 +09:00
|
|
|
copyCtxState(currentCtx, groupCtx);
|
|
|
|
this.ctx = groupCtx;
|
|
|
|
this.setGState([
|
2017-04-11 03:58:02 +09:00
|
|
|
['BM', 'source-over'],
|
2014-01-24 02:13:32 +09:00
|
|
|
['ca', 1],
|
|
|
|
['CA', 1]
|
|
|
|
]);
|
|
|
|
this.groupStack.push(currentCtx);
|
|
|
|
this.groupLevel++;
|
|
|
|
},
|
2016-04-10 08:46:15 +09:00
|
|
|
suspendSMaskGroup: function CanvasGraphics_endSMaskGroup() {
|
|
|
|
// Similar to endSMaskGroup, the intermediate canvas has to be composed
|
|
|
|
// and future ctx state restored.
|
|
|
|
var groupCtx = this.ctx;
|
|
|
|
this.groupLevel--;
|
|
|
|
this.ctx = this.groupStack.pop();
|
|
|
|
|
|
|
|
composeSMask(this.ctx, this.current.activeSMask, groupCtx);
|
|
|
|
this.ctx.restore();
|
|
|
|
this.ctx.save(); // save is needed since SMask will be resumed.
|
|
|
|
copyCtxState(groupCtx, this.ctx);
|
|
|
|
|
|
|
|
// Saving state for resuming.
|
|
|
|
this.current.resumeSMaskCtx = groupCtx;
|
|
|
|
// Transform was changed in the SMask canvas, reflecting this change on
|
|
|
|
// this.ctx.
|
|
|
|
var deltaTransform = Util.transform(
|
|
|
|
this.current.activeSMask.startTransformInverse,
|
|
|
|
groupCtx.mozCurrentTransform);
|
|
|
|
this.ctx.transform.apply(this.ctx, deltaTransform);
|
|
|
|
|
|
|
|
// SMask was composed, the results at the groupCtx can be cleared.
|
|
|
|
groupCtx.save();
|
|
|
|
groupCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
groupCtx.clearRect(0, 0, groupCtx.canvas.width, groupCtx.canvas.height);
|
|
|
|
groupCtx.restore();
|
|
|
|
},
|
|
|
|
resumeSMaskGroup: function CanvasGraphics_endSMaskGroup() {
|
|
|
|
// Resuming state saved by suspendSMaskGroup. We don't need to restore
|
|
|
|
// any groupCtx state since restore() command (the only caller) will do
|
|
|
|
// that for us. See also beginSMaskGroup.
|
|
|
|
var groupCtx = this.current.resumeSMaskCtx;
|
|
|
|
var currentCtx = this.ctx;
|
|
|
|
this.ctx = groupCtx;
|
|
|
|
this.groupStack.push(currentCtx);
|
|
|
|
this.groupLevel++;
|
|
|
|
},
|
2014-01-24 02:13:32 +09:00
|
|
|
endSMaskGroup: function CanvasGraphics_endSMaskGroup() {
|
|
|
|
var groupCtx = this.ctx;
|
|
|
|
this.groupLevel--;
|
|
|
|
this.ctx = this.groupStack.pop();
|
|
|
|
|
|
|
|
composeSMask(this.ctx, this.current.activeSMask, groupCtx);
|
|
|
|
this.ctx.restore();
|
2015-12-04 05:34:12 +09:00
|
|
|
copyCtxState(groupCtx, this.ctx);
|
2016-04-10 08:46:15 +09:00
|
|
|
// Transform was changed in the SMask canvas, reflecting this change on
|
|
|
|
// this.ctx.
|
|
|
|
var deltaTransform = Util.transform(
|
|
|
|
this.current.activeSMask.startTransformInverse,
|
|
|
|
groupCtx.mozCurrentTransform);
|
|
|
|
this.ctx.transform.apply(this.ctx, deltaTransform);
|
2014-01-24 02:13:32 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
save: function CanvasGraphics_save() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.save();
|
|
|
|
var old = this.current;
|
|
|
|
this.stateStack.push(old);
|
|
|
|
this.current = old.clone();
|
2016-04-10 08:46:15 +09:00
|
|
|
this.current.resumeSMaskCtx = null;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
restore: function CanvasGraphics_restore() {
|
2016-04-10 08:46:15 +09:00
|
|
|
// SMask was suspended, we just need to resume it.
|
|
|
|
if (this.current.resumeSMaskCtx) {
|
|
|
|
this.resumeSMaskGroup();
|
|
|
|
}
|
|
|
|
// SMask has to be finished once there is no states that are using the
|
|
|
|
// same SMask.
|
|
|
|
if (this.current.activeSMask !== null && (this.stateStack.length === 0 ||
|
|
|
|
this.stateStack[this.stateStack.length - 1].activeSMask !==
|
|
|
|
this.current.activeSMask)) {
|
|
|
|
this.endSMaskGroup();
|
|
|
|
}
|
2014-01-24 02:13:32 +09:00
|
|
|
|
2016-04-10 08:46:15 +09:00
|
|
|
if (this.stateStack.length !== 0) {
|
2014-06-02 21:46:59 +09:00
|
|
|
this.current = this.stateStack.pop();
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.restore();
|
2014-04-12 03:19:39 +09:00
|
|
|
|
2015-09-04 00:35:32 +09:00
|
|
|
// Ensure that the clipping path is reset (fixes issue6413.pdf).
|
|
|
|
this.pendingClip = null;
|
|
|
|
|
2014-04-12 03:19:39 +09:00
|
|
|
this.cachedGetSinglePixelWidth = null;
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.transform(a, b, c, d, e, f);
|
2014-04-12 03:19:39 +09:00
|
|
|
|
|
|
|
this.cachedGetSinglePixelWidth = null;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
// Path
|
2014-04-30 23:09:04 +09:00
|
|
|
constructPath: function CanvasGraphics_constructPath(ops, args) {
|
|
|
|
var ctx = this.ctx;
|
2011-10-25 08:55:23 +09:00
|
|
|
var current = this.current;
|
2014-04-30 23:09:04 +09:00
|
|
|
var x = current.x, y = current.y;
|
|
|
|
for (var i = 0, j = 0, ii = ops.length; i < ii; i++) {
|
|
|
|
switch (ops[i] | 0) {
|
2014-06-24 05:07:31 +09:00
|
|
|
case OPS.rectangle:
|
|
|
|
x = args[j++];
|
|
|
|
y = args[j++];
|
|
|
|
var width = args[j++];
|
|
|
|
var height = args[j++];
|
|
|
|
if (width === 0) {
|
|
|
|
width = this.getSinglePixelWidth();
|
|
|
|
}
|
|
|
|
if (height === 0) {
|
|
|
|
height = this.getSinglePixelWidth();
|
|
|
|
}
|
|
|
|
var xw = x + width;
|
|
|
|
var yh = y + height;
|
|
|
|
this.ctx.moveTo(x, y);
|
|
|
|
this.ctx.lineTo(xw, y);
|
|
|
|
this.ctx.lineTo(xw, yh);
|
|
|
|
this.ctx.lineTo(x, yh);
|
|
|
|
this.ctx.lineTo(x, y);
|
|
|
|
this.ctx.closePath();
|
|
|
|
break;
|
2014-04-30 23:09:04 +09:00
|
|
|
case OPS.moveTo:
|
|
|
|
x = args[j++];
|
|
|
|
y = args[j++];
|
|
|
|
ctx.moveTo(x, y);
|
|
|
|
break;
|
|
|
|
case OPS.lineTo:
|
|
|
|
x = args[j++];
|
|
|
|
y = args[j++];
|
|
|
|
ctx.lineTo(x, y);
|
|
|
|
break;
|
|
|
|
case OPS.curveTo:
|
|
|
|
x = args[j + 4];
|
|
|
|
y = args[j + 5];
|
2014-05-15 23:10:32 +09:00
|
|
|
ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3],
|
|
|
|
x, y);
|
2014-04-30 23:09:04 +09:00
|
|
|
j += 6;
|
|
|
|
break;
|
|
|
|
case OPS.curveTo2:
|
|
|
|
ctx.bezierCurveTo(x, y, args[j], args[j + 1],
|
|
|
|
args[j + 2], args[j + 3]);
|
|
|
|
x = args[j + 2];
|
|
|
|
y = args[j + 3];
|
|
|
|
j += 4;
|
|
|
|
break;
|
|
|
|
case OPS.curveTo3:
|
|
|
|
x = args[j + 2];
|
|
|
|
y = args[j + 3];
|
2014-05-15 23:10:32 +09:00
|
|
|
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
|
2014-04-30 23:09:04 +09:00
|
|
|
j += 4;
|
|
|
|
break;
|
|
|
|
case OPS.closePath:
|
|
|
|
ctx.closePath();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current.setCurrentPoint(x, y);
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
closePath: function CanvasGraphics_closePath() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.closePath();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
stroke: function CanvasGraphics_stroke(consumePath) {
|
2011-10-29 06:10:10 +09:00
|
|
|
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
2011-10-25 08:55:23 +09:00
|
|
|
var ctx = this.ctx;
|
|
|
|
var strokeColor = this.current.strokeColor;
|
2014-04-12 03:19:39 +09:00
|
|
|
// Prevent drawing too thin lines by enforcing a minimum line width.
|
|
|
|
ctx.lineWidth = Math.max(this.getSinglePixelWidth() * MIN_WIDTH_FACTOR,
|
|
|
|
this.current.lineWidth);
|
2011-10-29 06:10:10 +09:00
|
|
|
// For stroke we want to temporarily change the global alpha to the
|
|
|
|
// stroking alpha.
|
|
|
|
ctx.globalAlpha = this.current.strokeAlpha;
|
2011-10-25 08:55:23 +09:00
|
|
|
if (strokeColor && strokeColor.hasOwnProperty('type') &&
|
|
|
|
strokeColor.type === 'Pattern') {
|
|
|
|
// for patterns, we transform to pattern space, calculate
|
|
|
|
// the pattern, call stroke, and restore to user space
|
|
|
|
ctx.save();
|
2013-08-16 23:50:48 +09:00
|
|
|
ctx.strokeStyle = strokeColor.getPattern(ctx, this);
|
2011-10-25 08:55:23 +09:00
|
|
|
ctx.stroke();
|
|
|
|
ctx.restore();
|
|
|
|
} else {
|
|
|
|
ctx.stroke();
|
|
|
|
}
|
2014-03-14 21:11:11 +09:00
|
|
|
if (consumePath) {
|
2011-10-29 06:10:10 +09:00
|
|
|
this.consumePath();
|
2014-03-14 21:11:11 +09:00
|
|
|
}
|
2011-10-29 06:10:10 +09:00
|
|
|
// Restore the global alpha to the fill alpha
|
|
|
|
ctx.globalAlpha = this.current.fillAlpha;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
closeStroke: function CanvasGraphics_closeStroke() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.closePath();
|
|
|
|
this.stroke();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
fill: function CanvasGraphics_fill(consumePath) {
|
2011-10-29 06:10:10 +09:00
|
|
|
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
2011-10-25 08:55:23 +09:00
|
|
|
var ctx = this.ctx;
|
|
|
|
var fillColor = this.current.fillColor;
|
2014-10-27 02:20:04 +09:00
|
|
|
var isPatternFill = this.current.patternFill;
|
2013-05-04 08:47:40 +09:00
|
|
|
var needRestore = false;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
if (isPatternFill) {
|
2011-10-25 08:55:23 +09:00
|
|
|
ctx.save();
|
2015-11-25 16:44:06 +09:00
|
|
|
if (this.baseTransform) {
|
|
|
|
ctx.setTransform.apply(ctx, this.baseTransform);
|
|
|
|
}
|
2013-08-16 23:50:48 +09:00
|
|
|
ctx.fillStyle = fillColor.getPattern(ctx, this);
|
2013-05-04 08:47:40 +09:00
|
|
|
needRestore = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.pendingEOFill) {
|
2017-01-30 07:24:44 +09:00
|
|
|
ctx.fill('evenodd');
|
2013-05-04 08:47:40 +09:00
|
|
|
this.pendingEOFill = false;
|
2011-10-25 08:55:23 +09:00
|
|
|
} else {
|
2014-05-15 23:25:12 +09:00
|
|
|
ctx.fill();
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
2013-05-04 08:47:40 +09:00
|
|
|
|
|
|
|
if (needRestore) {
|
|
|
|
ctx.restore();
|
|
|
|
}
|
|
|
|
if (consumePath) {
|
2011-10-29 06:10:10 +09:00
|
|
|
this.consumePath();
|
2013-05-04 08:47:40 +09:00
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
eoFill: function CanvasGraphics_eoFill() {
|
2013-05-04 08:47:40 +09:00
|
|
|
this.pendingEOFill = true;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.fill();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
fillStroke: function CanvasGraphics_fillStroke() {
|
2011-10-29 06:10:10 +09:00
|
|
|
this.fill(false);
|
|
|
|
this.stroke(false);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
this.consumePath();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
eoFillStroke: function CanvasGraphics_eoFillStroke() {
|
2013-05-04 08:47:40 +09:00
|
|
|
this.pendingEOFill = true;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.fillStroke();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
closeFillStroke: function CanvasGraphics_closeFillStroke() {
|
2011-10-28 20:34:56 +09:00
|
|
|
this.closePath();
|
|
|
|
this.fillStroke();
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
|
2013-05-04 08:47:40 +09:00
|
|
|
this.pendingEOFill = true;
|
2011-10-28 20:34:56 +09:00
|
|
|
this.closePath();
|
2011-10-25 08:55:23 +09:00
|
|
|
this.fillStroke();
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
endPath: function CanvasGraphics_endPath() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.consumePath();
|
|
|
|
},
|
|
|
|
|
|
|
|
// Clipping
|
2012-04-05 05:43:26 +09:00
|
|
|
clip: function CanvasGraphics_clip() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.pendingClip = NORMAL_CLIP;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
eoClip: function CanvasGraphics_eoClip() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.pendingClip = EO_CLIP;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Text
|
2012-04-05 05:43:26 +09:00
|
|
|
beginText: function CanvasGraphics_beginText() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.textMatrix = IDENTITY_MATRIX;
|
2014-05-24 03:36:54 +09:00
|
|
|
this.current.textMatrixScale = 1;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.x = this.current.lineX = 0;
|
|
|
|
this.current.y = this.current.lineY = 0;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
endText: function CanvasGraphics_endText() {
|
2013-05-16 05:57:27 +09:00
|
|
|
var paths = this.pendingTextPaths;
|
2012-10-13 12:33:56 +09:00
|
|
|
var ctx = this.ctx;
|
2014-05-15 23:25:12 +09:00
|
|
|
if (paths === undefined) {
|
|
|
|
ctx.beginPath();
|
|
|
|
return;
|
|
|
|
}
|
2012-10-13 12:33:56 +09:00
|
|
|
|
|
|
|
ctx.save();
|
2013-05-16 05:57:27 +09:00
|
|
|
ctx.beginPath();
|
|
|
|
for (var i = 0; i < paths.length; i++) {
|
|
|
|
var path = paths[i];
|
|
|
|
ctx.setTransform.apply(ctx, path.transform);
|
|
|
|
ctx.translate(path.x, path.y);
|
|
|
|
path.addToPath(ctx, path.fontSize);
|
|
|
|
}
|
2012-10-13 12:33:56 +09:00
|
|
|
ctx.restore();
|
2013-05-16 05:57:27 +09:00
|
|
|
ctx.clip();
|
|
|
|
ctx.beginPath();
|
|
|
|
delete this.pendingTextPaths;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.charSpacing = spacing;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.wordSpacing = spacing;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setHScale: function CanvasGraphics_setHScale(scale) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.textHScale = scale / 100;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setLeading: function CanvasGraphics_setLeading(leading) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.leading = -leading;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setFont: function CanvasGraphics_setFont(fontRefName, size) {
|
2012-10-29 05:10:34 +09:00
|
|
|
var fontObj = this.commonObjs.get(fontRefName);
|
2012-01-24 05:23:09 +09:00
|
|
|
var current = this.current;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-03-14 21:11:11 +09:00
|
|
|
if (!fontObj) {
|
2012-01-25 05:04:59 +09:00
|
|
|
error('Can\'t find font for ' + fontRefName);
|
2014-03-14 21:11:11 +09:00
|
|
|
}
|
2012-01-24 05:23:09 +09:00
|
|
|
|
2014-03-14 21:11:11 +09:00
|
|
|
current.fontMatrix = (fontObj.fontMatrix ?
|
|
|
|
fontObj.fontMatrix : FONT_IDENTITY_MATRIX);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-01-21 08:41:01 +09:00
|
|
|
// A valid matrix needs all main diagonal elements to be non-zero
|
|
|
|
// This also ensures we bypass FF bugzilla bug #719844.
|
2012-01-24 05:23:09 +09:00
|
|
|
if (current.fontMatrix[0] === 0 ||
|
|
|
|
current.fontMatrix[3] === 0) {
|
2012-01-21 04:55:52 +09:00
|
|
|
warn('Invalid font matrix for font ' + fontRefName);
|
|
|
|
}
|
|
|
|
|
2012-01-21 08:41:01 +09:00
|
|
|
// The spec for Tf (setFont) says that 'size' specifies the font 'scale',
|
2012-01-24 05:23:09 +09:00
|
|
|
// and in some docs this can be negative (inverted x-y axes).
|
2012-01-21 08:41:01 +09:00
|
|
|
if (size < 0) {
|
|
|
|
size = -size;
|
2013-01-04 09:39:06 +09:00
|
|
|
current.fontDirection = -1;
|
|
|
|
} else {
|
|
|
|
current.fontDirection = 1;
|
2012-01-21 08:41:01 +09:00
|
|
|
}
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.font = fontObj;
|
|
|
|
this.current.fontSize = size;
|
|
|
|
|
2014-05-20 06:27:54 +09:00
|
|
|
if (fontObj.isType3Font) {
|
2012-02-05 03:45:18 +09:00
|
|
|
return; // we don't need ctx.font for Type3 fonts
|
2014-03-14 21:11:11 +09:00
|
|
|
}
|
2012-02-05 03:45:18 +09:00
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
var name = fontObj.loadedName || 'sans-serif';
|
2016-11-22 21:48:06 +09:00
|
|
|
var bold = fontObj.black ? '900' : (fontObj.bold ? 'bold' : 'normal');
|
2011-10-25 08:55:23 +09:00
|
|
|
var italic = fontObj.italic ? 'italic' : 'normal';
|
2012-09-15 02:58:33 +09:00
|
|
|
var typeface = '"' + name + '", ' + fontObj.fallbackName;
|
2012-02-05 03:45:18 +09:00
|
|
|
|
2012-02-14 13:35:42 +09:00
|
|
|
// Some font backends cannot handle fonts below certain size.
|
|
|
|
// Keeping the font at minimal size and using the fontSizeScale to change
|
|
|
|
// the current transformation matrix before the fillText/strokeText.
|
|
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
|
2014-10-25 10:48:31 +09:00
|
|
|
var browserFontSize = size < MIN_FONT_SIZE ? MIN_FONT_SIZE :
|
|
|
|
size > MAX_FONT_SIZE ? MAX_FONT_SIZE : size;
|
|
|
|
this.current.fontSizeScale = size / browserFontSize;
|
2012-02-05 03:45:18 +09:00
|
|
|
|
|
|
|
var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.font = rule;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
|
2011-12-03 07:52:31 +09:00
|
|
|
this.current.textRenderingMode = mode;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setTextRise: function CanvasGraphics_setTextRise(rise) {
|
2012-08-02 05:10:48 +09:00
|
|
|
this.current.textRise = rise;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
moveText: function CanvasGraphics_moveText(x, y) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.x = this.current.lineX += x;
|
|
|
|
this.current.y = this.current.lineY += y;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.setLeading(-y);
|
|
|
|
this.moveText(x, y);
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.current.textMatrix = [a, b, c, d, e, f];
|
2014-05-24 03:36:54 +09:00
|
|
|
this.current.textMatrixScale = Math.sqrt(a * a + b * b);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
this.current.x = this.current.lineX = 0;
|
|
|
|
this.current.y = this.current.lineY = 0;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
nextLine: function CanvasGraphics_nextLine() {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.moveText(0, this.current.leading);
|
|
|
|
},
|
2011-12-02 04:11:17 +09:00
|
|
|
|
2014-05-04 00:28:30 +09:00
|
|
|
paintChar: function CanvasGraphics_paintChar(character, x, y) {
|
2013-05-16 05:57:27 +09:00
|
|
|
var ctx = this.ctx;
|
|
|
|
var current = this.current;
|
|
|
|
var font = current.font;
|
|
|
|
var textRenderingMode = current.textRenderingMode;
|
2014-05-24 03:36:54 +09:00
|
|
|
var fontSize = current.fontSize / current.fontSizeScale;
|
2013-05-16 05:57:27 +09:00
|
|
|
var fillStrokeMode = textRenderingMode &
|
|
|
|
TextRenderingMode.FILL_STROKE_MASK;
|
|
|
|
var isAddToPathSet = !!(textRenderingMode &
|
|
|
|
TextRenderingMode.ADD_TO_PATH_FLAG);
|
|
|
|
|
|
|
|
var addToPath;
|
|
|
|
if (font.disableFontFace || isAddToPathSet) {
|
2013-08-20 08:33:20 +09:00
|
|
|
addToPath = font.getPathGenerator(this.commonObjs, character);
|
2013-05-16 05:57:27 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if (font.disableFontFace) {
|
|
|
|
ctx.save();
|
|
|
|
ctx.translate(x, y);
|
|
|
|
ctx.beginPath();
|
|
|
|
addToPath(ctx, fontSize);
|
|
|
|
if (fillStrokeMode === TextRenderingMode.FILL ||
|
|
|
|
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
if (fillStrokeMode === TextRenderingMode.STROKE ||
|
|
|
|
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
|
|
|
|
ctx.stroke();
|
|
|
|
}
|
|
|
|
ctx.restore();
|
|
|
|
} else {
|
|
|
|
if (fillStrokeMode === TextRenderingMode.FILL ||
|
|
|
|
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
|
|
|
|
ctx.fillText(character, x, y);
|
|
|
|
}
|
|
|
|
if (fillStrokeMode === TextRenderingMode.STROKE ||
|
|
|
|
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
|
|
|
|
ctx.strokeText(character, x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isAddToPathSet) {
|
|
|
|
var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
|
|
|
|
paths.push({
|
|
|
|
transform: ctx.mozCurrentTransform,
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
fontSize: fontSize,
|
|
|
|
addToPath: addToPath
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-01-15 08:31:23 +09:00
|
|
|
get isFontSubpixelAAEnabled() {
|
|
|
|
// Checks if anti-aliasing is enabled when scaled text is painted.
|
|
|
|
// On Windows GDI scaled fonts looks bad.
|
2017-02-07 06:19:56 +09:00
|
|
|
var ctx = this.canvasFactory.create(10, 10).context;
|
2014-01-15 08:31:23 +09:00
|
|
|
ctx.scale(1.5, 1);
|
|
|
|
ctx.fillText('I', 0, 10);
|
|
|
|
var data = ctx.getImageData(0, 0, 10, 10).data;
|
|
|
|
var enabled = false;
|
|
|
|
for (var i = 3; i < data.length; i += 4) {
|
|
|
|
if (data[i] > 0 && data[i] < 255) {
|
|
|
|
enabled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return shadow(this, 'isFontSubpixelAAEnabled', enabled);
|
|
|
|
},
|
|
|
|
|
2014-04-10 08:44:07 +09:00
|
|
|
showText: function CanvasGraphics_showText(glyphs) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var current = this.current;
|
|
|
|
var font = current.font;
|
2014-05-24 03:36:54 +09:00
|
|
|
if (font.isType3Font) {
|
|
|
|
return this.showType3Text(glyphs);
|
|
|
|
}
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
var fontSize = current.fontSize;
|
2014-05-24 03:36:54 +09:00
|
|
|
if (fontSize === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var ctx = this.ctx;
|
2012-02-05 03:45:18 +09:00
|
|
|
var fontSizeScale = current.fontSizeScale;
|
2011-10-25 08:55:23 +09:00
|
|
|
var charSpacing = current.charSpacing;
|
|
|
|
var wordSpacing = current.wordSpacing;
|
2014-05-24 03:36:54 +09:00
|
|
|
var fontDirection = current.fontDirection;
|
|
|
|
var textHScale = current.textHScale * fontDirection;
|
2011-10-25 08:55:23 +09:00
|
|
|
var glyphsLength = glyphs.length;
|
2013-02-08 21:29:22 +09:00
|
|
|
var vertical = font.vertical;
|
2015-08-28 20:42:01 +09:00
|
|
|
var spacingDir = vertical ? 1 : -1;
|
2013-02-08 21:29:22 +09:00
|
|
|
var defaultVMetrics = font.defaultVMetrics;
|
2014-05-24 03:36:54 +09:00
|
|
|
var widthAdvanceScale = fontSize * current.fontMatrix[0];
|
2011-11-09 05:27:03 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
var simpleFillText =
|
|
|
|
current.textRenderingMode === TextRenderingMode.FILL &&
|
|
|
|
!font.disableFontFace;
|
2014-03-30 20:36:21 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
ctx.save();
|
|
|
|
ctx.transform.apply(ctx, current.textMatrix);
|
|
|
|
ctx.translate(current.x, current.y + current.textRise);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
Apply Patterns, if necessary, when rendering text
Currently we're not applying Patterns for text, but only for graphics.
This patch is unfortunately not a complete solution, but rather a step on the way, since there are still some PDF files where the Patterns look more like a solid colour, rather than the intended gradient.
I've been unable to fix these issues completely, and I've not managed to determine if the remaining issues are caused either by the pattern code, the canvas code, or perhaps both.
However, given that even this simple patch improves the current situation quite a bit, I figured that it couldn't hurt to submit it as-is.
- Fixes 5804.
- Fixes 6130.
- Improves 3988 a lot, since the text is now visible. However, it looks like the text is *one* solid colour, instead of the correct gradient.
- Improves 5432, since the text is no longer gray. (This file also suffers from the same problem as the previous one.)
2015-12-30 01:57:10 +09:00
|
|
|
if (current.patternFill) {
|
|
|
|
// TODO: Some shading patterns are not applied correctly to text,
|
|
|
|
// e.g. issues 3988 and 5432, and ShowText-ShadingPattern.pdf.
|
|
|
|
ctx.fillStyle = current.fillColor.getPattern(ctx, this);
|
|
|
|
}
|
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
if (fontDirection > 0) {
|
|
|
|
ctx.scale(textHScale, -1);
|
|
|
|
} else {
|
2011-12-02 11:56:26 +09:00
|
|
|
ctx.scale(textHScale, 1);
|
2014-05-24 03:36:54 +09:00
|
|
|
}
|
2011-12-13 12:32:20 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
var lineWidth = current.lineWidth;
|
|
|
|
var scale = current.textMatrixScale;
|
|
|
|
if (scale === 0 || lineWidth === 0) {
|
2014-04-12 03:19:39 +09:00
|
|
|
var fillStrokeMode = current.textRenderingMode &
|
|
|
|
TextRenderingMode.FILL_STROKE_MASK;
|
|
|
|
if (fillStrokeMode === TextRenderingMode.STROKE ||
|
|
|
|
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
|
|
|
|
this.cachedGetSinglePixelWidth = null;
|
|
|
|
lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR;
|
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
} else {
|
|
|
|
lineWidth /= scale;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-08-01 19:39:56 +09:00
|
|
|
if (fontSizeScale !== 1.0) {
|
2014-05-24 03:36:54 +09:00
|
|
|
ctx.scale(fontSizeScale, fontSizeScale);
|
|
|
|
lineWidth /= fontSizeScale;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
ctx.lineWidth = lineWidth;
|
|
|
|
|
|
|
|
var x = 0, i;
|
|
|
|
for (i = 0; i < glyphsLength; ++i) {
|
|
|
|
var glyph = glyphs[i];
|
2015-11-02 23:54:15 +09:00
|
|
|
if (isNum(glyph)) {
|
2015-08-28 20:42:01 +09:00
|
|
|
x += spacingDir * glyph * fontSize / 1000;
|
2014-05-24 03:36:54 +09:00
|
|
|
continue;
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
2012-01-18 13:50:49 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
var restoreNeeded = false;
|
2015-11-02 23:54:15 +09:00
|
|
|
var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
|
2014-05-24 03:36:54 +09:00
|
|
|
var character = glyph.fontChar;
|
|
|
|
var accent = glyph.accent;
|
|
|
|
var scaledX, scaledY, scaledAccentX, scaledAccentY;
|
|
|
|
var width = glyph.width;
|
|
|
|
if (vertical) {
|
|
|
|
var vmetric, vx, vy;
|
|
|
|
vmetric = glyph.vmetric || defaultVMetrics;
|
|
|
|
vx = glyph.vmetric ? vmetric[1] : width * 0.5;
|
|
|
|
vx = -vx * widthAdvanceScale;
|
|
|
|
vy = vmetric[2] * widthAdvanceScale;
|
|
|
|
|
|
|
|
width = vmetric ? -vmetric[0] : width;
|
|
|
|
scaledX = vx / fontSizeScale;
|
|
|
|
scaledY = (x + vy) / fontSizeScale;
|
2014-03-14 21:11:11 +09:00
|
|
|
} else {
|
2014-05-24 03:36:54 +09:00
|
|
|
scaledX = x / fontSizeScale;
|
|
|
|
scaledY = 0;
|
2014-03-14 21:11:11 +09:00
|
|
|
}
|
2012-01-18 13:50:49 +09:00
|
|
|
|
2015-11-18 04:21:27 +09:00
|
|
|
if (font.remeasure && width > 0) {
|
|
|
|
// Some standard fonts may not have the exact width: rescale per
|
|
|
|
// character if measured width is greater than expected glyph width
|
|
|
|
// and subpixel-aa is enabled, otherwise just center the glyph.
|
2014-05-24 03:36:54 +09:00
|
|
|
var measuredWidth = ctx.measureText(character).width * 1000 /
|
|
|
|
fontSize * fontSizeScale;
|
2015-11-18 04:21:27 +09:00
|
|
|
if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
|
|
|
|
var characterScaleX = width / measuredWidth;
|
|
|
|
restoreNeeded = true;
|
|
|
|
ctx.save();
|
|
|
|
ctx.scale(characterScaleX, 1);
|
|
|
|
scaledX /= characterScaleX;
|
|
|
|
} else if (width !== measuredWidth) {
|
|
|
|
scaledX += (width - measuredWidth) / 2000 *
|
|
|
|
fontSize / fontSizeScale;
|
|
|
|
}
|
2012-02-14 12:12:55 +09:00
|
|
|
}
|
|
|
|
|
2016-02-25 03:48:02 +09:00
|
|
|
// Only attempt to draw the glyph if it is actually in the embedded font
|
|
|
|
// file or if there isn't a font file so the fallback font is shown.
|
|
|
|
if (glyph.isInFont || font.missingFile) {
|
|
|
|
if (simpleFillText && !accent) {
|
|
|
|
// common case
|
|
|
|
ctx.fillText(character, scaledX, scaledY);
|
|
|
|
} else {
|
|
|
|
this.paintChar(character, scaledX, scaledY);
|
|
|
|
if (accent) {
|
|
|
|
scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
|
|
|
|
scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
|
|
|
|
this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY);
|
|
|
|
}
|
2014-02-12 03:27:09 +09:00
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
}
|
2014-02-12 03:27:09 +09:00
|
|
|
|
2015-11-02 23:54:15 +09:00
|
|
|
var charWidth = width * widthAdvanceScale + spacing * fontDirection;
|
2014-05-24 03:36:54 +09:00
|
|
|
x += charWidth;
|
2011-11-30 06:02:12 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
if (restoreNeeded) {
|
|
|
|
ctx.restore();
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
if (vertical) {
|
|
|
|
current.y -= x * textHScale;
|
|
|
|
} else {
|
|
|
|
current.x += x * textHScale;
|
|
|
|
}
|
|
|
|
ctx.restore();
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2014-05-24 03:36:54 +09:00
|
|
|
|
|
|
|
showType3Text: function CanvasGraphics_showType3Text(glyphs) {
|
|
|
|
// Type3 fonts - each glyph is a "mini-PDF"
|
|
|
|
var ctx = this.ctx;
|
2011-10-25 08:55:23 +09:00
|
|
|
var current = this.current;
|
2011-12-13 12:32:20 +09:00
|
|
|
var font = current.font;
|
2011-10-25 08:55:23 +09:00
|
|
|
var fontSize = current.fontSize;
|
2014-05-24 03:36:54 +09:00
|
|
|
var fontDirection = current.fontDirection;
|
2015-08-28 20:42:01 +09:00
|
|
|
var spacingDir = font.vertical ? 1 : -1;
|
2014-05-24 03:36:54 +09:00
|
|
|
var charSpacing = current.charSpacing;
|
|
|
|
var wordSpacing = current.wordSpacing;
|
|
|
|
var textHScale = current.textHScale * fontDirection;
|
|
|
|
var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
|
|
|
|
var glyphsLength = glyphs.length;
|
2014-10-19 05:29:21 +09:00
|
|
|
var isTextInvisible =
|
|
|
|
current.textRenderingMode === TextRenderingMode.INVISIBLE;
|
2015-08-28 20:42:01 +09:00
|
|
|
var i, glyph, width, spacingLength;
|
2011-11-30 06:02:12 +09:00
|
|
|
|
2014-10-19 05:29:21 +09:00
|
|
|
if (isTextInvisible || fontSize === 0) {
|
2014-05-24 03:36:54 +09:00
|
|
|
return;
|
|
|
|
}
|
2015-06-16 19:47:50 +09:00
|
|
|
this.cachedGetSinglePixelWidth = null;
|
2011-10-29 06:37:55 +09:00
|
|
|
|
2014-05-24 03:36:54 +09:00
|
|
|
ctx.save();
|
|
|
|
ctx.transform.apply(ctx, current.textMatrix);
|
|
|
|
ctx.translate(current.x, current.y);
|
|
|
|
|
2014-08-19 07:57:52 +09:00
|
|
|
ctx.scale(textHScale, fontDirection);
|
2014-05-24 03:36:54 +09:00
|
|
|
|
|
|
|
for (i = 0; i < glyphsLength; ++i) {
|
|
|
|
glyph = glyphs[i];
|
2015-11-02 23:54:15 +09:00
|
|
|
if (isNum(glyph)) {
|
2015-08-28 20:42:01 +09:00
|
|
|
spacingLength = spacingDir * glyph * fontSize / 1000;
|
2014-05-24 03:36:54 +09:00
|
|
|
this.ctx.translate(spacingLength, 0);
|
|
|
|
current.x += spacingLength * textHScale;
|
|
|
|
continue;
|
2013-02-08 21:29:22 +09:00
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
|
2015-11-02 23:54:15 +09:00
|
|
|
var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
|
2014-07-24 21:59:21 +09:00
|
|
|
var operatorList = font.charProcOperatorList[glyph.operatorListId];
|
|
|
|
if (!operatorList) {
|
|
|
|
warn('Type3 character \"' + glyph.operatorListId +
|
|
|
|
'\" is not available');
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
this.processingType3 = glyph;
|
|
|
|
this.save();
|
|
|
|
ctx.scale(fontSize, fontSize);
|
|
|
|
ctx.transform.apply(ctx, fontMatrix);
|
|
|
|
this.executeOperatorList(operatorList);
|
|
|
|
this.restore();
|
|
|
|
|
|
|
|
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
2015-11-02 23:54:15 +09:00
|
|
|
width = transformed[0] * fontSize + spacing;
|
2014-05-24 03:36:54 +09:00
|
|
|
|
|
|
|
ctx.translate(width, 0);
|
|
|
|
current.x += width * textHScale;
|
2012-09-20 05:17:01 +09:00
|
|
|
}
|
2014-05-24 03:36:54 +09:00
|
|
|
ctx.restore();
|
|
|
|
this.processingType3 = null;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
// Type3 fonts
|
2012-04-05 05:43:26 +09:00
|
|
|
setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {
|
2011-10-25 08:55:23 +09:00
|
|
|
// We can safely ignore this since the width should be the same
|
|
|
|
// as the width in the Widths array.
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth,
|
2011-10-25 08:55:23 +09:00
|
|
|
yWidth,
|
|
|
|
llx,
|
|
|
|
lly,
|
|
|
|
urx,
|
|
|
|
ury) {
|
|
|
|
// TODO According to the spec we're also suppose to ignore any operators
|
|
|
|
// that set color or include images while processing this type3 font.
|
2014-06-24 05:07:31 +09:00
|
|
|
this.ctx.rect(llx, lly, urx - llx, ury - lly);
|
2011-10-25 08:55:23 +09:00
|
|
|
this.clip();
|
|
|
|
this.endPath();
|
|
|
|
},
|
|
|
|
|
|
|
|
// Color
|
2014-05-22 02:47:42 +09:00
|
|
|
getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR) {
|
2014-04-09 04:48:16 +09:00
|
|
|
var pattern;
|
2014-06-02 21:46:59 +09:00
|
|
|
if (IR[0] === 'TilingPattern') {
|
2014-05-22 02:47:42 +09:00
|
|
|
var color = IR[1];
|
2015-09-28 05:43:23 +09:00
|
|
|
var baseTransform = this.baseTransform ||
|
|
|
|
this.ctx.mozCurrentTransform.slice();
|
2015-11-22 01:32:47 +09:00
|
|
|
var self = this;
|
|
|
|
var canvasGraphicsFactory = {
|
|
|
|
createCanvasGraphics: function (ctx) {
|
2017-01-28 02:58:39 +09:00
|
|
|
return new CanvasGraphics(ctx, self.commonObjs, self.objs,
|
|
|
|
self.canvasFactory);
|
2015-11-22 01:32:47 +09:00
|
|
|
}
|
|
|
|
};
|
|
|
|
pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory,
|
|
|
|
baseTransform);
|
2011-10-25 08:55:23 +09:00
|
|
|
} else {
|
2014-04-09 04:48:16 +09:00
|
|
|
pattern = getShadingPatternFromIR(IR);
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
return pattern;
|
|
|
|
},
|
2017-01-20 00:26:32 +09:00
|
|
|
setStrokeColorN: function CanvasGraphics_setStrokeColorN() {
|
2014-05-22 02:47:42 +09:00
|
|
|
this.current.strokeColor = this.getColorN_Pattern(arguments);
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2017-01-20 00:26:32 +09:00
|
|
|
setFillColorN: function CanvasGraphics_setFillColorN() {
|
2014-05-22 02:47:42 +09:00
|
|
|
this.current.fillColor = this.getColorN_Pattern(arguments);
|
2014-10-27 02:20:04 +09:00
|
|
|
this.current.patternFill = true;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
|
2014-10-28 05:30:47 +09:00
|
|
|
var color = Util.makeCssRgb(r, g, b);
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.strokeStyle = color;
|
|
|
|
this.current.strokeColor = color;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
|
2014-10-28 05:30:47 +09:00
|
|
|
var color = Util.makeCssRgb(r, g, b);
|
2011-10-25 08:55:23 +09:00
|
|
|
this.ctx.fillStyle = color;
|
|
|
|
this.current.fillColor = color;
|
2014-10-27 02:20:04 +09:00
|
|
|
this.current.patternFill = false;
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
shadingFill: function CanvasGraphics_shadingFill(patternIR) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var ctx = this.ctx;
|
|
|
|
|
|
|
|
this.save();
|
2014-01-26 03:18:22 +09:00
|
|
|
var pattern = getShadingPatternFromIR(patternIR);
|
2014-01-27 22:17:14 +09:00
|
|
|
ctx.fillStyle = pattern.getPattern(ctx, this, true);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
var inv = ctx.mozCurrentTransformInverse;
|
|
|
|
if (inv) {
|
|
|
|
var canvas = ctx.canvas;
|
|
|
|
var width = canvas.width;
|
|
|
|
var height = canvas.height;
|
|
|
|
|
|
|
|
var bl = Util.applyTransform([0, 0], inv);
|
2011-12-07 21:42:01 +09:00
|
|
|
var br = Util.applyTransform([0, height], inv);
|
|
|
|
var ul = Util.applyTransform([width, 0], inv);
|
|
|
|
var ur = Util.applyTransform([width, height], inv);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
|
|
|
|
var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
|
|
|
|
var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
|
|
|
|
var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
|
|
|
|
|
|
|
|
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
|
|
} else {
|
|
|
|
// HACK to draw the gradient onto an infinite rectangle.
|
|
|
|
// PDF gradients are drawn across the entire image while
|
|
|
|
// Canvas only allows gradients to be drawn in a rectangle
|
|
|
|
// The following bug should allow us to remove this.
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=664884
|
|
|
|
|
|
|
|
this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.restore();
|
|
|
|
},
|
|
|
|
|
|
|
|
// Images
|
2012-04-05 05:43:26 +09:00
|
|
|
beginInlineImage: function CanvasGraphics_beginInlineImage() {
|
2011-10-25 08:55:23 +09:00
|
|
|
error('Should not call beginInlineImage');
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
beginImageData: function CanvasGraphics_beginImageData() {
|
2011-10-25 08:55:23 +09:00
|
|
|
error('Should not call beginImageData');
|
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
|
2011-10-30 20:46:15 +09:00
|
|
|
bbox) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.save();
|
2013-07-23 06:52:44 +09:00
|
|
|
this.baseTransformStack.push(this.baseTransform);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2016-12-11 01:23:46 +09:00
|
|
|
if (isArray(matrix) && matrix.length === 6) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.transform.apply(this, matrix);
|
2014-03-14 21:11:11 +09:00
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2013-07-23 06:52:44 +09:00
|
|
|
this.baseTransform = this.ctx.mozCurrentTransform;
|
|
|
|
|
2016-12-11 01:23:46 +09:00
|
|
|
if (isArray(bbox) && bbox.length === 4) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var width = bbox[2] - bbox[0];
|
|
|
|
var height = bbox[3] - bbox[1];
|
2014-06-24 05:07:31 +09:00
|
|
|
this.ctx.rect(bbox[0], bbox[1], width, height);
|
2011-10-25 08:55:23 +09:00
|
|
|
this.clip();
|
|
|
|
this.endPath();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
|
2014-01-17 22:16:52 +09:00
|
|
|
this.restore();
|
2013-07-23 06:52:44 +09:00
|
|
|
this.baseTransform = this.baseTransformStack.pop();
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
2013-03-13 09:20:38 +09:00
|
|
|
beginGroup: function CanvasGraphics_beginGroup(group) {
|
|
|
|
this.save();
|
|
|
|
var currentCtx = this.ctx;
|
|
|
|
// TODO non-isolated groups - according to Rik at adobe non-isolated
|
|
|
|
// group results aren't usually that different and they even have tools
|
2016-07-17 21:33:41 +09:00
|
|
|
// that ignore this setting. Notes from Rik on implementing:
|
2013-03-13 09:20:38 +09:00
|
|
|
// - When you encounter an transparency group, create a new canvas with
|
|
|
|
// the dimensions of the bbox
|
|
|
|
// - copy the content from the previous canvas to the new canvas
|
|
|
|
// - draw as usual
|
|
|
|
// - remove the backdrop alpha:
|
|
|
|
// alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
|
|
|
|
// value of your transparency group and 'alphaBackdrop' the alpha of the
|
|
|
|
// backdrop
|
|
|
|
// - remove background color:
|
|
|
|
// colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
|
|
|
|
if (!group.isolated) {
|
2013-04-17 07:45:29 +09:00
|
|
|
info('TODO: Support non-isolated groups.');
|
2013-03-13 09:20:38 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO knockout - supposedly possible with the clever use of compositing
|
|
|
|
// modes.
|
|
|
|
if (group.knockout) {
|
2014-01-04 02:34:13 +09:00
|
|
|
warn('Knockout groups not supported.');
|
2013-03-13 09:20:38 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
var currentTransform = currentCtx.mozCurrentTransform;
|
|
|
|
if (group.matrix) {
|
|
|
|
currentCtx.transform.apply(currentCtx, group.matrix);
|
|
|
|
}
|
|
|
|
assert(group.bbox, 'Bounding box is required.');
|
|
|
|
|
|
|
|
// Based on the current transform figure out how big the bounding box
|
|
|
|
// will actually be.
|
|
|
|
var bounds = Util.getAxialAlignedBoundingBox(
|
|
|
|
group.bbox,
|
|
|
|
currentCtx.mozCurrentTransform);
|
2013-10-11 04:41:11 +09:00
|
|
|
// Clip the bounding box to the current canvas.
|
2013-11-05 02:16:33 +09:00
|
|
|
var canvasBounds = [0,
|
|
|
|
0,
|
|
|
|
currentCtx.canvas.width,
|
|
|
|
currentCtx.canvas.height];
|
|
|
|
bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
|
2013-03-13 09:20:38 +09:00
|
|
|
// Use ceil in case we're between sizes so we don't create canvas that is
|
2013-04-12 03:19:42 +09:00
|
|
|
// too small and make the canvas at least 1x1 pixels.
|
2014-02-13 22:01:45 +09:00
|
|
|
var offsetX = Math.floor(bounds[0]);
|
|
|
|
var offsetY = Math.floor(bounds[1]);
|
|
|
|
var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
|
|
|
|
var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
|
|
|
|
var scaleX = 1, scaleY = 1;
|
|
|
|
if (drawnWidth > MAX_GROUP_SIZE) {
|
|
|
|
scaleX = drawnWidth / MAX_GROUP_SIZE;
|
|
|
|
drawnWidth = MAX_GROUP_SIZE;
|
|
|
|
}
|
|
|
|
if (drawnHeight > MAX_GROUP_SIZE) {
|
|
|
|
scaleY = drawnHeight / MAX_GROUP_SIZE;
|
|
|
|
drawnHeight = MAX_GROUP_SIZE;
|
|
|
|
}
|
2013-04-12 03:19:42 +09:00
|
|
|
|
2014-01-24 02:13:32 +09:00
|
|
|
var cacheId = 'groupAt' + this.groupLevel;
|
|
|
|
if (group.smask) {
|
|
|
|
// Using two cache entries is case if masks are used one after another.
|
2016-12-10 22:28:27 +09:00
|
|
|
cacheId += '_smask_' + ((this.smaskCounter++) % 2);
|
2014-01-24 02:13:32 +09:00
|
|
|
}
|
2015-11-17 01:50:02 +09:00
|
|
|
var scratchCanvas = this.cachedCanvases.getCanvas(
|
2014-01-24 02:13:32 +09:00
|
|
|
cacheId, drawnWidth, drawnHeight, true);
|
2013-08-16 23:50:48 +09:00
|
|
|
var groupCtx = scratchCanvas.context;
|
2014-01-24 02:13:32 +09:00
|
|
|
|
2013-03-13 09:20:38 +09:00
|
|
|
// Since we created a new canvas that is just the size of the bounding box
|
|
|
|
// we have to translate the group ctx.
|
2014-02-13 22:01:45 +09:00
|
|
|
groupCtx.scale(1 / scaleX, 1 / scaleY);
|
2013-03-13 09:20:38 +09:00
|
|
|
groupCtx.translate(-offsetX, -offsetY);
|
|
|
|
groupCtx.transform.apply(groupCtx, currentTransform);
|
|
|
|
|
2014-01-24 02:13:32 +09:00
|
|
|
if (group.smask) {
|
|
|
|
// Saving state and cached mask to be used in setGState.
|
|
|
|
this.smaskStack.push({
|
|
|
|
canvas: scratchCanvas.canvas,
|
|
|
|
context: groupCtx,
|
|
|
|
offsetX: offsetX,
|
|
|
|
offsetY: offsetY,
|
2014-02-13 22:01:45 +09:00
|
|
|
scaleX: scaleX,
|
|
|
|
scaleY: scaleY,
|
2014-01-24 02:13:32 +09:00
|
|
|
subtype: group.smask.subtype,
|
2015-12-05 03:52:45 +09:00
|
|
|
backdrop: group.smask.backdrop,
|
2016-04-10 08:46:15 +09:00
|
|
|
transferMap: group.smask.transferMap || null,
|
|
|
|
startTransformInverse: null, // used during suspend operation
|
2014-01-24 02:13:32 +09:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Setup the current ctx so when the group is popped we draw it at the
|
|
|
|
// right location.
|
|
|
|
currentCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
currentCtx.translate(offsetX, offsetY);
|
2014-02-13 22:01:45 +09:00
|
|
|
currentCtx.scale(scaleX, scaleY);
|
2014-01-24 02:13:32 +09:00
|
|
|
}
|
2013-03-13 09:20:38 +09:00
|
|
|
// The transparency group inherits all off the current graphics state
|
|
|
|
// except the blend mode, soft mask, and alpha constants.
|
|
|
|
copyCtxState(currentCtx, groupCtx);
|
|
|
|
this.ctx = groupCtx;
|
|
|
|
this.setGState([
|
2017-04-11 03:58:02 +09:00
|
|
|
['BM', 'source-over'],
|
2013-03-13 09:20:38 +09:00
|
|
|
['ca', 1],
|
|
|
|
['CA', 1]
|
|
|
|
]);
|
|
|
|
this.groupStack.push(currentCtx);
|
2013-08-16 23:50:48 +09:00
|
|
|
this.groupLevel++;
|
2016-04-10 08:46:15 +09:00
|
|
|
|
|
|
|
// Reseting mask state, masks will be applied on restore of the group.
|
|
|
|
this.current.activeSMask = null;
|
2013-03-13 09:20:38 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
endGroup: function CanvasGraphics_endGroup(group) {
|
2013-08-16 23:50:48 +09:00
|
|
|
this.groupLevel--;
|
2013-03-13 09:20:38 +09:00
|
|
|
var groupCtx = this.ctx;
|
|
|
|
this.ctx = this.groupStack.pop();
|
|
|
|
// Turn off image smoothing to avoid sub pixel interpolation which can
|
|
|
|
// look kind of blurry for some pdfs.
|
2014-05-15 23:25:12 +09:00
|
|
|
if (this.ctx.imageSmoothingEnabled !== undefined) {
|
2013-03-13 09:20:38 +09:00
|
|
|
this.ctx.imageSmoothingEnabled = false;
|
|
|
|
} else {
|
|
|
|
this.ctx.mozImageSmoothingEnabled = false;
|
|
|
|
}
|
2014-01-24 02:13:32 +09:00
|
|
|
if (group.smask) {
|
|
|
|
this.tempSMask = this.smaskStack.pop();
|
|
|
|
} else {
|
|
|
|
this.ctx.drawImage(groupCtx.canvas, 0, 0);
|
|
|
|
}
|
2013-03-13 09:20:38 +09:00
|
|
|
this.restore();
|
|
|
|
},
|
|
|
|
|
2013-05-29 07:12:35 +09:00
|
|
|
beginAnnotations: function CanvasGraphics_beginAnnotations() {
|
|
|
|
this.save();
|
|
|
|
this.current = new CanvasExtraState();
|
2015-12-28 22:10:30 +09:00
|
|
|
|
|
|
|
if (this.baseTransform) {
|
|
|
|
this.ctx.setTransform.apply(this.ctx, this.baseTransform);
|
|
|
|
}
|
2013-05-29 07:12:35 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
endAnnotations: function CanvasGraphics_endAnnotations() {
|
|
|
|
this.restore();
|
|
|
|
},
|
|
|
|
|
2013-03-14 04:24:55 +09:00
|
|
|
beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform,
|
2013-03-21 17:04:44 +09:00
|
|
|
matrix) {
|
2013-03-14 04:24:55 +09:00
|
|
|
this.save();
|
|
|
|
|
2016-12-11 01:23:46 +09:00
|
|
|
if (isArray(rect) && rect.length === 4) {
|
2013-03-14 04:24:55 +09:00
|
|
|
var width = rect[2] - rect[0];
|
|
|
|
var height = rect[3] - rect[1];
|
2014-06-24 05:07:31 +09:00
|
|
|
this.ctx.rect(rect[0], rect[1], width, height);
|
2013-03-14 04:24:55 +09:00
|
|
|
this.clip();
|
|
|
|
this.endPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.transform.apply(this, transform);
|
|
|
|
this.transform.apply(this, matrix);
|
|
|
|
},
|
|
|
|
|
|
|
|
endAnnotation: function CanvasGraphics_endAnnotation() {
|
|
|
|
this.restore();
|
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
|
2011-12-09 14:18:04 +09:00
|
|
|
var domImage = this.objs.get(objId);
|
|
|
|
if (!domImage) {
|
2014-04-15 05:22:35 +09:00
|
|
|
warn('Dependent image isn\'t ready yet');
|
|
|
|
return;
|
2011-12-09 14:18:04 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
this.save();
|
|
|
|
|
|
|
|
var ctx = this.ctx;
|
|
|
|
// scale the image to the unit square
|
|
|
|
ctx.scale(1 / w, -1 / h);
|
|
|
|
|
|
|
|
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
|
|
|
0, -h, w, h);
|
2013-02-11 03:19:36 +09:00
|
|
|
if (this.imageLayer) {
|
|
|
|
var currentTransform = ctx.mozCurrentTransformInverse;
|
|
|
|
var position = this.getCanvasPosition(0, 0);
|
|
|
|
this.imageLayer.appendImage({
|
|
|
|
objId: objId,
|
|
|
|
left: position[0],
|
|
|
|
top: position[1],
|
2013-02-12 19:30:19 +09:00
|
|
|
width: w / currentTransform[0],
|
|
|
|
height: h / currentTransform[3]
|
2013-02-11 03:19:36 +09:00
|
|
|
});
|
|
|
|
}
|
2011-12-09 14:18:04 +09:00
|
|
|
this.restore();
|
|
|
|
},
|
2011-12-13 02:53:31 +09:00
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
|
2012-12-08 03:19:43 +09:00
|
|
|
var ctx = this.ctx;
|
2013-05-31 09:42:26 +09:00
|
|
|
var width = img.width, height = img.height;
|
2014-10-27 02:20:04 +09:00
|
|
|
var fillColor = this.current.fillColor;
|
|
|
|
var isPatternFill = this.current.patternFill;
|
2013-05-31 09:42:26 +09:00
|
|
|
|
2013-05-11 12:50:14 +09:00
|
|
|
var glyph = this.processingType3;
|
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) {
|
2013-05-11 12:50:14 +09:00
|
|
|
if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
|
|
|
|
glyph.compiled =
|
2013-05-31 09:42:26 +09:00
|
|
|
compileType3Glyph({data: img.data, width: width, height: height});
|
2013-05-11 12:50:14 +09:00
|
|
|
} else {
|
|
|
|
glyph.compiled = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (glyph && glyph.compiled) {
|
|
|
|
glyph.compiled(ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-17 01:50:02 +09:00
|
|
|
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
|
|
|
width, height);
|
2013-08-16 23:50:48 +09:00
|
|
|
var maskCtx = maskCanvas.context;
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.save();
|
2013-05-11 12:50:14 +09:00
|
|
|
|
2014-01-14 11:08:39 +09:00
|
|
|
putBinaryImageMask(maskCtx, img);
|
2013-05-31 09:42:26 +09:00
|
|
|
|
|
|
|
maskCtx.globalCompositeOperation = 'source-in';
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
maskCtx.fillStyle = isPatternFill ?
|
2013-08-16 23:50:48 +09:00
|
|
|
fillColor.getPattern(maskCtx, this) : fillColor;
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.fillRect(0, 0, width, height);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.restore();
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2013-08-16 23:50:48 +09:00
|
|
|
this.paintInlineImageXObject(maskCanvas.canvas);
|
2012-12-08 03:19:43 +09:00
|
|
|
},
|
|
|
|
|
2014-02-25 00:59:02 +09:00
|
|
|
paintImageMaskXObjectRepeat:
|
|
|
|
function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX,
|
|
|
|
scaleY, positions) {
|
|
|
|
var width = imgData.width;
|
|
|
|
var height = imgData.height;
|
2014-10-27 02:20:04 +09:00
|
|
|
var fillColor = this.current.fillColor;
|
|
|
|
var isPatternFill = this.current.patternFill;
|
2014-02-25 00:59:02 +09:00
|
|
|
|
2015-11-17 01:50:02 +09:00
|
|
|
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
|
|
|
width, height);
|
2014-02-25 00:59:02 +09:00
|
|
|
var maskCtx = maskCanvas.context;
|
|
|
|
maskCtx.save();
|
|
|
|
|
|
|
|
putBinaryImageMask(maskCtx, imgData);
|
|
|
|
|
|
|
|
maskCtx.globalCompositeOperation = 'source-in';
|
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
maskCtx.fillStyle = isPatternFill ?
|
|
|
|
fillColor.getPattern(maskCtx, this) : fillColor;
|
2014-02-25 00:59:02 +09:00
|
|
|
maskCtx.fillRect(0, 0, width, height);
|
|
|
|
|
|
|
|
maskCtx.restore();
|
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
var ctx = this.ctx;
|
2014-02-25 00:59:02 +09:00
|
|
|
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.scale(1, -1);
|
|
|
|
ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
|
|
|
|
0, -1, 1, 1);
|
|
|
|
ctx.restore();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-12-08 03:19:43 +09:00
|
|
|
paintImageMaskXObjectGroup:
|
|
|
|
function CanvasGraphics_paintImageMaskXObjectGroup(images) {
|
|
|
|
var ctx = this.ctx;
|
2013-05-31 09:42:26 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
var fillColor = this.current.fillColor;
|
|
|
|
var isPatternFill = this.current.patternFill;
|
2012-12-08 03:19:43 +09:00
|
|
|
for (var i = 0, ii = images.length; i < ii; i++) {
|
|
|
|
var image = images[i];
|
2013-05-31 09:42:26 +09:00
|
|
|
var width = image.width, height = image.height;
|
|
|
|
|
2015-11-17 01:50:02 +09:00
|
|
|
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
|
|
|
width, height);
|
2013-08-16 23:50:48 +09:00
|
|
|
var maskCtx = maskCanvas.context;
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.save();
|
2012-12-08 03:19:43 +09:00
|
|
|
|
2014-01-14 11:08:39 +09:00
|
|
|
putBinaryImageMask(maskCtx, image);
|
2012-12-08 03:19:43 +09:00
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.globalCompositeOperation = 'source-in';
|
2012-12-08 03:19:43 +09:00
|
|
|
|
2014-10-27 02:20:04 +09:00
|
|
|
maskCtx.fillStyle = isPatternFill ?
|
2013-08-16 23:50:48 +09:00
|
|
|
fillColor.getPattern(maskCtx, this) : fillColor;
|
2013-05-31 09:42:26 +09:00
|
|
|
maskCtx.fillRect(0, 0, width, height);
|
|
|
|
|
|
|
|
maskCtx.restore();
|
2012-12-08 03:19:43 +09:00
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
ctx.transform.apply(ctx, image.transform);
|
|
|
|
ctx.scale(1, -1);
|
2013-08-16 23:50:48 +09:00
|
|
|
ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
|
2012-12-08 03:19:43 +09:00
|
|
|
0, -1, 1, 1);
|
|
|
|
ctx.restore();
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
|
2011-12-09 05:50:34 +09:00
|
|
|
var imgData = this.objs.get(objId);
|
2014-02-24 23:00:08 +09:00
|
|
|
if (!imgData) {
|
2014-04-15 05:22:35 +09:00
|
|
|
warn('Dependent image isn\'t ready yet');
|
|
|
|
return;
|
2014-02-24 23:00:08 +09:00
|
|
|
}
|
2011-12-16 08:13:48 +09:00
|
|
|
|
2012-12-08 03:19:43 +09:00
|
|
|
this.paintInlineImageXObject(imgData);
|
2012-12-05 02:36:42 +09:00
|
|
|
},
|
|
|
|
|
2014-02-25 00:59:02 +09:00
|
|
|
paintImageXObjectRepeat:
|
|
|
|
function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY,
|
2014-02-24 23:00:08 +09:00
|
|
|
positions) {
|
2014-02-25 00:59:02 +09:00
|
|
|
var imgData = this.objs.get(objId);
|
|
|
|
if (!imgData) {
|
2014-04-15 05:22:35 +09:00
|
|
|
warn('Dependent image isn\'t ready yet');
|
|
|
|
return;
|
2014-02-25 00:59:02 +09:00
|
|
|
}
|
2014-02-24 23:00:08 +09:00
|
|
|
|
2014-02-25 00:59:02 +09:00
|
|
|
var width = imgData.width;
|
|
|
|
var height = imgData.height;
|
|
|
|
var map = [];
|
|
|
|
for (var i = 0, ii = positions.length; i < ii; i += 2) {
|
|
|
|
map.push({transform: [scaleX, 0, 0, scaleY, positions[i],
|
|
|
|
positions[i + 1]], x: 0, y: 0, w: width, h: height});
|
|
|
|
}
|
|
|
|
this.paintInlineImageXObjectGroup(imgData, map);
|
2014-02-24 23:00:08 +09:00
|
|
|
},
|
|
|
|
|
2012-12-08 03:19:43 +09:00
|
|
|
paintInlineImageXObject:
|
|
|
|
function CanvasGraphics_paintInlineImageXObject(imgData) {
|
2012-12-05 02:36:42 +09:00
|
|
|
var width = imgData.width;
|
|
|
|
var height = imgData.height;
|
2011-10-25 08:55:23 +09:00
|
|
|
var ctx = this.ctx;
|
2013-05-31 09:42:26 +09:00
|
|
|
|
2012-12-05 02:36:42 +09:00
|
|
|
this.save();
|
2011-10-25 08:55:23 +09:00
|
|
|
// scale the image to the unit square
|
2012-12-05 02:36:42 +09:00
|
|
|
ctx.scale(1 / width, -1 / height);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-12-05 02:36:42 +09:00
|
|
|
var currentTransform = ctx.mozCurrentTransformInverse;
|
2013-05-31 09:42:26 +09:00
|
|
|
var a = currentTransform[0], b = currentTransform[1];
|
|
|
|
var widthScale = Math.max(Math.sqrt(a * a + b * b), 1);
|
|
|
|
var c = currentTransform[2], d = currentTransform[3];
|
|
|
|
var heightScale = Math.max(Math.sqrt(c * c + d * d), 1);
|
|
|
|
|
2014-04-09 04:48:16 +09:00
|
|
|
var imgToPaint, tmpCanvas;
|
2013-06-30 12:24:07 +09:00
|
|
|
// instanceof HTMLElement does not work in jsdom node.js module
|
|
|
|
if (imgData instanceof HTMLElement || !imgData.data) {
|
2013-05-31 09:42:26 +09:00
|
|
|
imgToPaint = imgData;
|
2012-12-05 02:36:42 +09:00
|
|
|
} else {
|
2015-11-17 01:50:02 +09:00
|
|
|
tmpCanvas = this.cachedCanvases.getCanvas('inlineImage',
|
|
|
|
width, height);
|
2013-08-16 23:50:48 +09:00
|
|
|
var tmpCtx = tmpCanvas.context;
|
2013-05-31 09:42:26 +09:00
|
|
|
putBinaryImageData(tmpCtx, imgData);
|
2013-08-16 23:50:48 +09:00
|
|
|
imgToPaint = tmpCanvas.canvas;
|
2013-05-31 09:42:26 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
var paintWidth = width, paintHeight = height;
|
|
|
|
var tmpCanvasId = 'prescale1';
|
|
|
|
// Vertial or horizontal scaling shall not be more than 2 to not loose 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)) {
|
|
|
|
var 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;
|
2012-12-05 02:36:42 +09:00
|
|
|
}
|
2015-11-17 01:50:02 +09:00
|
|
|
tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId,
|
|
|
|
newWidth, newHeight);
|
2013-08-16 23:50:48 +09:00
|
|
|
tmpCtx = tmpCanvas.context;
|
2013-05-31 09:42:26 +09:00
|
|
|
tmpCtx.clearRect(0, 0, newWidth, newHeight);
|
|
|
|
tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
|
|
|
|
0, 0, newWidth, newHeight);
|
2013-08-16 23:50:48 +09:00
|
|
|
imgToPaint = tmpCanvas.canvas;
|
2013-05-31 09:42:26 +09:00
|
|
|
paintWidth = newWidth;
|
|
|
|
paintHeight = newHeight;
|
|
|
|
tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1';
|
2012-12-05 02:36:42 +09:00
|
|
|
}
|
2013-05-31 09:42:26 +09:00
|
|
|
ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
|
|
|
|
0, -height, width, height);
|
2013-02-11 03:19:36 +09:00
|
|
|
|
|
|
|
if (this.imageLayer) {
|
|
|
|
var position = this.getCanvasPosition(0, -height);
|
|
|
|
this.imageLayer.appendImage({
|
|
|
|
imgData: imgData,
|
|
|
|
left: position[0],
|
|
|
|
top: position[1],
|
2013-02-12 19:30:19 +09:00
|
|
|
width: width / currentTransform[0],
|
|
|
|
height: height / currentTransform[3]
|
2013-02-11 03:19:36 +09:00
|
|
|
});
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
this.restore();
|
|
|
|
},
|
|
|
|
|
2012-12-08 03:19:43 +09:00
|
|
|
paintInlineImageXObjectGroup:
|
|
|
|
function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
|
|
|
|
var ctx = this.ctx;
|
|
|
|
var w = imgData.width;
|
|
|
|
var h = imgData.height;
|
|
|
|
|
2015-11-17 01:50:02 +09:00
|
|
|
var tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', w, h);
|
2013-08-16 23:50:48 +09:00
|
|
|
var tmpCtx = tmpCanvas.context;
|
2013-05-31 09:42:26 +09:00
|
|
|
putBinaryImageData(tmpCtx, imgData);
|
2012-12-08 03:19:43 +09:00
|
|
|
|
|
|
|
for (var i = 0, ii = map.length; i < ii; i++) {
|
|
|
|
var entry = map[i];
|
|
|
|
ctx.save();
|
|
|
|
ctx.transform.apply(ctx, entry.transform);
|
|
|
|
ctx.scale(1, -1);
|
2013-08-16 23:50:48 +09:00
|
|
|
ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h,
|
2012-12-08 03:19:43 +09:00
|
|
|
0, -1, 1, 1);
|
2013-02-11 03:19:36 +09:00
|
|
|
if (this.imageLayer) {
|
|
|
|
var position = this.getCanvasPosition(entry.x, entry.y);
|
|
|
|
this.imageLayer.appendImage({
|
|
|
|
imgData: imgData,
|
|
|
|
left: position[0],
|
|
|
|
top: position[1],
|
|
|
|
width: w,
|
|
|
|
height: h
|
|
|
|
});
|
|
|
|
}
|
2012-12-08 03:19:43 +09:00
|
|
|
ctx.restore();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-03-17 00:17:13 +09:00
|
|
|
paintSolidColorImageMask:
|
|
|
|
function CanvasGraphics_paintSolidColorImageMask() {
|
|
|
|
this.ctx.fillRect(0, 0, 1, 1);
|
|
|
|
},
|
|
|
|
|
2015-10-21 03:25:29 +09:00
|
|
|
paintXObject: function CanvasGraphics_paintXObject() {
|
|
|
|
warn('Unsupported \'paintXObject\' command.');
|
|
|
|
},
|
|
|
|
|
2011-10-25 08:55:23 +09:00
|
|
|
// Marked content
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
markPoint: function CanvasGraphics_markPoint(tag) {
|
2012-07-21 01:09:48 +09:00
|
|
|
// TODO Marked content.
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
|
2012-07-21 01:09:48 +09:00
|
|
|
// TODO Marked content.
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
|
2012-07-21 01:09:48 +09:00
|
|
|
// TODO Marked content.
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
|
|
|
|
tag, properties) {
|
2012-07-21 01:09:48 +09:00
|
|
|
// TODO Marked content.
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
endMarkedContent: function CanvasGraphics_endMarkedContent() {
|
2012-07-21 01:09:48 +09:00
|
|
|
// TODO Marked content.
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
// Compatibility
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
beginCompat: function CanvasGraphics_beginCompat() {
|
2013-02-06 03:57:59 +09:00
|
|
|
// TODO ignore undefined operators (should we do that anyway?)
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
endCompat: function CanvasGraphics_endCompat() {
|
2013-02-06 03:57:59 +09:00
|
|
|
// TODO stop ignoring undefined operators
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
// Helper functions
|
|
|
|
|
2012-04-05 05:43:26 +09:00
|
|
|
consumePath: function CanvasGraphics_consumePath() {
|
2014-05-15 23:25:12 +09:00
|
|
|
var ctx = this.ctx;
|
2011-10-25 08:55:23 +09:00
|
|
|
if (this.pendingClip) {
|
2014-06-02 21:46:59 +09:00
|
|
|
if (this.pendingClip === EO_CLIP) {
|
2017-01-30 07:24:44 +09:00
|
|
|
ctx.clip('evenodd');
|
2013-05-04 08:47:40 +09:00
|
|
|
} else {
|
2014-05-15 23:25:12 +09:00
|
|
|
ctx.clip();
|
2013-05-04 08:47:40 +09:00
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
this.pendingClip = null;
|
|
|
|
}
|
2014-05-15 23:25:12 +09:00
|
|
|
ctx.beginPath();
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
|
2014-04-12 03:19:39 +09:00
|
|
|
if (this.cachedGetSinglePixelWidth === null) {
|
2016-05-16 19:15:00 +09:00
|
|
|
// NOTE: The `save` and `restore` commands used below is a workaround
|
|
|
|
// that is necessary in order to prevent `mozCurrentTransformInverse`
|
|
|
|
// from intermittently returning incorrect values in Firefox, see:
|
|
|
|
// https://github.com/mozilla/pdf.js/issues/7188.
|
|
|
|
this.ctx.save();
|
2014-04-12 03:19:39 +09:00
|
|
|
var inverse = this.ctx.mozCurrentTransformInverse;
|
2016-05-16 19:15:00 +09:00
|
|
|
this.ctx.restore();
|
2014-04-12 03:19:39 +09:00
|
|
|
// max of the current horizontal and vertical scale
|
|
|
|
this.cachedGetSinglePixelWidth = Math.sqrt(Math.max(
|
|
|
|
(inverse[0] * inverse[0] + inverse[1] * inverse[1]),
|
|
|
|
(inverse[2] * inverse[2] + inverse[3] * inverse[3])));
|
|
|
|
}
|
|
|
|
return this.cachedGetSinglePixelWidth;
|
2013-02-11 03:19:36 +09:00
|
|
|
},
|
|
|
|
getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
|
2015-12-17 06:31:30 +09:00
|
|
|
var transform = this.ctx.mozCurrentTransform;
|
|
|
|
return [
|
|
|
|
transform[0] * x + transform[2] * y + transform[4],
|
|
|
|
transform[1] * x + transform[3] * y + transform[5]
|
|
|
|
];
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-14 04:43:38 +09:00
|
|
|
for (var op in OPS) {
|
|
|
|
CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
|
|
|
|
}
|
|
|
|
|
2011-12-09 07:18:43 +09:00
|
|
|
return CanvasGraphics;
|
2011-10-25 08:55:23 +09:00
|
|
|
})();
|
2015-11-22 01:32:47 +09:00
|
|
|
|
|
|
|
exports.CanvasGraphics = CanvasGraphics;
|
|
|
|
}));
|