[PATCH] Add fallback for font loading when eval disabled
In some cases, such as in use with a CSP header, constructing a function with a string of javascript is not allowed. However, compiling the various commands that need to be done on the canvas element is faster than interpreting them. This patch changes the font renderer to instead emit commands that are compiled by the font loader. If, during compilation, we receive an EvalError, we instead interpret them.
This commit is contained in:
parent
18e1a14e65
commit
341c5e9d1f
@ -134,16 +134,15 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compileGlyf(code, js, font) {
|
||||
function compileGlyf(code, cmds, font) {
|
||||
function moveTo(x, y) {
|
||||
js.push('c.moveTo(' + x + ',' + y + ');');
|
||||
cmds.push({cmd: 'moveTo', args: [x, y]});
|
||||
}
|
||||
function lineTo(x, y) {
|
||||
js.push('c.lineTo(' + x + ',' + y + ');');
|
||||
cmds.push({cmd: 'lineTo', args: [x, y]});
|
||||
}
|
||||
function quadraticCurveTo(xa, ya, x, y) {
|
||||
js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' +
|
||||
x + ',' + y + ');');
|
||||
cmds.push({cmd: 'quadraticCurveTo', args: [xa, ya, x, y]});
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
@ -189,11 +188,11 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
}
|
||||
var subglyph = font.glyphs[glyphIndex];
|
||||
if (subglyph) {
|
||||
js.push('c.save();');
|
||||
js.push('c.transform(' + scaleX + ',' + scale01 + ',' +
|
||||
scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
|
||||
compileGlyf(subglyph, js, font);
|
||||
js.push('c.restore();');
|
||||
cmds.push({cmd: 'save'});
|
||||
cmds.push({cmd: 'transform',
|
||||
args: [scaleX, scale01, scale10, scaleY, x, y]});
|
||||
compileGlyf(subglyph, cmds, font);
|
||||
cmds.push({cmd: 'restore'});
|
||||
}
|
||||
} while ((flags & 0x20));
|
||||
} else {
|
||||
@ -289,20 +288,19 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
function compileCharString(code, js, font) {
|
||||
function compileCharString(code, cmds, font) {
|
||||
var stack = [];
|
||||
var x = 0, y = 0;
|
||||
var stems = 0;
|
||||
|
||||
function moveTo(x, y) {
|
||||
js.push('c.moveTo(' + x + ',' + y + ');');
|
||||
cmds.push({cmd: 'moveTo', args: [x, y]});
|
||||
}
|
||||
function lineTo(x, y) {
|
||||
js.push('c.lineTo(' + x + ',' + y + ');');
|
||||
cmds.push({cmd: 'lineTo', args: [x, y]});
|
||||
}
|
||||
function bezierCurveTo(x1, y1, x2, y2, x, y) {
|
||||
js.push('c.bezierCurveTo(' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' +
|
||||
x + ',' + y + ');');
|
||||
cmds.push({cmd: 'bezierCurveTo', args: [x1, y1, x2, y2, x, y]});
|
||||
}
|
||||
|
||||
function parse(code) {
|
||||
@ -431,16 +429,16 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
var bchar = stack.pop();
|
||||
y = stack.pop();
|
||||
x = stack.pop();
|
||||
js.push('c.save();');
|
||||
js.push('c.translate('+ x + ',' + y + ');');
|
||||
cmds.push({cmd: 'save'});
|
||||
cmds.push({cmd: 'translate', args: [x, y]});
|
||||
var gid = lookupCmap(font.cmap, String.fromCharCode(
|
||||
font.glyphNameMap[Encodings.StandardEncoding[achar]]));
|
||||
compileCharString(font.glyphs[gid], js, font);
|
||||
js.push('c.restore();');
|
||||
compileCharString(font.glyphs[gid], cmds, font);
|
||||
cmds.push({cmd: 'restore'});
|
||||
|
||||
gid = lookupCmap(font.cmap, String.fromCharCode(
|
||||
font.glyphNameMap[Encodings.StandardEncoding[bchar]]));
|
||||
compileCharString(font.glyphs[gid], js, font);
|
||||
compileCharString(font.glyphs[gid], cmds, font);
|
||||
}
|
||||
return;
|
||||
case 18: // hstemhm
|
||||
@ -609,16 +607,16 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
return noop;
|
||||
}
|
||||
|
||||
var js = [];
|
||||
js.push('c.save();');
|
||||
js.push('c.transform(' + this.fontMatrix.join(',') + ');');
|
||||
js.push('c.scale(size, -size);');
|
||||
var cmds = [];
|
||||
cmds.push({cmd: 'save'});
|
||||
cmds.push({cmd: 'transform', args: this.fontMatrix.slice()});
|
||||
cmds.push({cmd: 'scale', args: ['size', '-size']});
|
||||
|
||||
this.compileGlyphImpl(code, js);
|
||||
this.compileGlyphImpl(code, cmds);
|
||||
|
||||
js.push('c.restore();');
|
||||
cmds.push({cmd: 'restore'});
|
||||
|
||||
return js.join('\n');
|
||||
return cmds;
|
||||
},
|
||||
|
||||
compileGlyphImpl: function () {
|
||||
@ -642,8 +640,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
}
|
||||
|
||||
Util.inherit(TrueTypeCompiled, CompiledFont, {
|
||||
compileGlyphImpl: function (code, js) {
|
||||
compileGlyf(code, js, this);
|
||||
compileGlyphImpl: function (code, cmds) {
|
||||
compileGlyf(code, cmds, this);
|
||||
}
|
||||
});
|
||||
|
||||
@ -664,8 +662,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
||||
}
|
||||
|
||||
Util.inherit(Type2Compiled, CompiledFont, {
|
||||
compileGlyphImpl: function (code, js) {
|
||||
compileCharString(code, js, this);
|
||||
compileGlyphImpl: function (code, cmds) {
|
||||
compileCharString(code, cmds, this);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -177,6 +177,14 @@ PDFJS.openExternalLinksInNewWindow = (
|
||||
PDFJS.openExternalLinksInNewWindow === undefined ?
|
||||
false : PDFJS.openExternalLinksInNewWindow);
|
||||
|
||||
/**
|
||||
* Determines if we can eval strings as JS. Primarily used to improve
|
||||
* performance for font rendering.
|
||||
* @var {boolean}
|
||||
*/
|
||||
PDFJS.isEvalSupported = (PDFJS.isEvalSupported === undefined ?
|
||||
true : PDFJS.isEvalSupported);
|
||||
|
||||
/**
|
||||
* Document initialization / loading parameters object.
|
||||
*
|
||||
|
@ -77,6 +77,18 @@ var FontLoader = {
|
||||
));
|
||||
},
|
||||
|
||||
get isEvalSupported() {
|
||||
var evalSupport = false;
|
||||
if (PDFJS.isEvalSupported) {
|
||||
try {
|
||||
/* jshint evil: true */
|
||||
new Function('');
|
||||
evalSupport = true;
|
||||
} catch (e) {}
|
||||
}
|
||||
return shadow(this, 'isEvalSupported', evalSupport);
|
||||
},
|
||||
|
||||
loadTestFontId: 0,
|
||||
|
||||
loadingContext: {
|
||||
@ -365,9 +377,40 @@ var FontFaceObject = (function FontFaceObjectClosure() {
|
||||
|
||||
getPathGenerator: function FontLoader_getPathGenerator(objs, character) {
|
||||
if (!(character in this.compiledGlyphs)) {
|
||||
var js = objs.get(this.loadedName + '_path_' + character);
|
||||
/*jshint -W054 */
|
||||
this.compiledGlyphs[character] = new Function('c', 'size', js);
|
||||
var cmds = objs.get(this.loadedName + '_path_' + character);
|
||||
var current, i, len;
|
||||
|
||||
// If we can, compile cmds into JS for MAXIMUM SPEED
|
||||
if (FontLoader.isEvalSupported) {
|
||||
var args, js = '';
|
||||
for (i = 0, len = cmds.length; i < len; i++) {
|
||||
current = cmds[i];
|
||||
|
||||
if (current.args !== undefined) {
|
||||
args = current.args.join(',');
|
||||
} else {
|
||||
args = '';
|
||||
}
|
||||
|
||||
js += 'c.' + current.cmd + '(' + args + ');\n';
|
||||
}
|
||||
/* jshint -W054 */
|
||||
this.compiledGlyphs[character] = new Function('c', 'size', js);
|
||||
} else {
|
||||
// But fall back on using Function.prototype.apply() if we're
|
||||
// blocked from using eval() for whatever reason (like CSP policies)
|
||||
this.compiledGlyphs[character] = function(c, size) {
|
||||
for (i = 0, len = cmds.length; i < len; i++) {
|
||||
current = cmds[i];
|
||||
|
||||
if (current.cmd === 'scale') {
|
||||
current.args = [size, -size];
|
||||
}
|
||||
|
||||
c[current.cmd].apply(c, current.args);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return this.compiledGlyphs[character];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user