mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-08 09:20:06 +02:00
Merge pull request #18015 from calixteman/rm_eval_font_loader
Simplify the way to pass the glyph drawing instructions from the worker to the main thread
This commit is contained in:
commit
85e64b5c16
5 changed files with 149 additions and 60 deletions
|
@ -4391,6 +4391,15 @@ class PartialEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fontMatrix = dict.getArray("FontMatrix");
|
||||||
|
if (
|
||||||
|
!Array.isArray(fontMatrix) ||
|
||||||
|
fontMatrix.length !== 6 ||
|
||||||
|
fontMatrix.some(x => typeof x !== "number")
|
||||||
|
) {
|
||||||
|
fontMatrix = FONT_IDENTITY_MATRIX;
|
||||||
|
}
|
||||||
|
|
||||||
const properties = {
|
const properties = {
|
||||||
type,
|
type,
|
||||||
name: fontName.name,
|
name: fontName.name,
|
||||||
|
@ -4403,7 +4412,7 @@ class PartialEvaluator {
|
||||||
loadedName: baseDict.loadedName,
|
loadedName: baseDict.loadedName,
|
||||||
composite,
|
composite,
|
||||||
fixedPitch: false,
|
fixedPitch: false,
|
||||||
fontMatrix: dict.getArray("FontMatrix") || FONT_IDENTITY_MATRIX,
|
fontMatrix,
|
||||||
firstChar,
|
firstChar,
|
||||||
lastChar,
|
lastChar,
|
||||||
toUnicode,
|
toUnicode,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import {
|
import {
|
||||||
bytesToString,
|
bytesToString,
|
||||||
FONT_IDENTITY_MATRIX,
|
FONT_IDENTITY_MATRIX,
|
||||||
|
FontRenderOps,
|
||||||
FormatError,
|
FormatError,
|
||||||
unreachable,
|
unreachable,
|
||||||
warn,
|
warn,
|
||||||
|
@ -180,13 +181,13 @@ function lookupCmap(ranges, unicode) {
|
||||||
|
|
||||||
function compileGlyf(code, cmds, font) {
|
function compileGlyf(code, cmds, font) {
|
||||||
function moveTo(x, y) {
|
function moveTo(x, y) {
|
||||||
cmds.push({ cmd: "moveTo", args: [x, y] });
|
cmds.add(FontRenderOps.MOVE_TO, [x, y]);
|
||||||
}
|
}
|
||||||
function lineTo(x, y) {
|
function lineTo(x, y) {
|
||||||
cmds.push({ cmd: "lineTo", args: [x, y] });
|
cmds.add(FontRenderOps.LINE_TO, [x, y]);
|
||||||
}
|
}
|
||||||
function quadraticCurveTo(xa, ya, x, y) {
|
function quadraticCurveTo(xa, ya, x, y) {
|
||||||
cmds.push({ cmd: "quadraticCurveTo", args: [xa, ya, x, y] });
|
cmds.add(FontRenderOps.QUADRATIC_CURVE_TO, [xa, ya, x, y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -247,20 +248,22 @@ function compileGlyf(code, cmds, font) {
|
||||||
if (subglyph) {
|
if (subglyph) {
|
||||||
// TODO: the transform should be applied only if there is a scale:
|
// TODO: the transform should be applied only if there is a scale:
|
||||||
// https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1205
|
// https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1205
|
||||||
cmds.push(
|
cmds.add(FontRenderOps.SAVE);
|
||||||
{ cmd: "save" },
|
cmds.add(FontRenderOps.TRANSFORM, [
|
||||||
{
|
scaleX,
|
||||||
cmd: "transform",
|
scale01,
|
||||||
args: [scaleX, scale01, scale10, scaleY, x, y],
|
scale10,
|
||||||
}
|
scaleY,
|
||||||
);
|
x,
|
||||||
|
y,
|
||||||
|
]);
|
||||||
|
|
||||||
if (!(flags & 0x02)) {
|
if (!(flags & 0x02)) {
|
||||||
// TODO: we must use arg1 and arg2 to make something similar to:
|
// TODO: we must use arg1 and arg2 to make something similar to:
|
||||||
// https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1209
|
// https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1209
|
||||||
}
|
}
|
||||||
compileGlyf(subglyph, cmds, font);
|
compileGlyf(subglyph, cmds, font);
|
||||||
cmds.push({ cmd: "restore" });
|
cmds.add(FontRenderOps.RESTORE);
|
||||||
}
|
}
|
||||||
} while (flags & 0x20);
|
} while (flags & 0x20);
|
||||||
} else {
|
} else {
|
||||||
|
@ -365,13 +368,13 @@ function compileGlyf(code, cmds, font) {
|
||||||
|
|
||||||
function compileCharString(charStringCode, cmds, font, glyphId) {
|
function compileCharString(charStringCode, cmds, font, glyphId) {
|
||||||
function moveTo(x, y) {
|
function moveTo(x, y) {
|
||||||
cmds.push({ cmd: "moveTo", args: [x, y] });
|
cmds.add(FontRenderOps.MOVE_TO, [x, y]);
|
||||||
}
|
}
|
||||||
function lineTo(x, y) {
|
function lineTo(x, y) {
|
||||||
cmds.push({ cmd: "lineTo", args: [x, y] });
|
cmds.add(FontRenderOps.LINE_TO, [x, y]);
|
||||||
}
|
}
|
||||||
function bezierCurveTo(x1, y1, x2, y2, x, y) {
|
function bezierCurveTo(x1, y1, x2, y2, x, y) {
|
||||||
cmds.push({ cmd: "bezierCurveTo", args: [x1, y1, x2, y2, x, y] });
|
cmds.add(FontRenderOps.BEZIER_CURVE_TO, [x1, y1, x2, y2, x, y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = [];
|
const stack = [];
|
||||||
|
@ -544,7 +547,8 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
|
||||||
const bchar = stack.pop();
|
const bchar = stack.pop();
|
||||||
y = stack.pop();
|
y = stack.pop();
|
||||||
x = stack.pop();
|
x = stack.pop();
|
||||||
cmds.push({ cmd: "save" }, { cmd: "translate", args: [x, y] });
|
cmds.add(FontRenderOps.SAVE);
|
||||||
|
cmds.add(FontRenderOps.TRANSLATE, [x, y]);
|
||||||
let cmap = lookupCmap(
|
let cmap = lookupCmap(
|
||||||
font.cmap,
|
font.cmap,
|
||||||
String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]])
|
String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]])
|
||||||
|
@ -555,7 +559,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
|
||||||
font,
|
font,
|
||||||
cmap.glyphId
|
cmap.glyphId
|
||||||
);
|
);
|
||||||
cmds.push({ cmd: "restore" });
|
cmds.add(FontRenderOps.RESTORE);
|
||||||
|
|
||||||
cmap = lookupCmap(
|
cmap = lookupCmap(
|
||||||
font.cmap,
|
font.cmap,
|
||||||
|
@ -741,6 +745,27 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
|
||||||
|
|
||||||
const NOOP = [];
|
const NOOP = [];
|
||||||
|
|
||||||
|
class Commands {
|
||||||
|
cmds = [];
|
||||||
|
|
||||||
|
add(cmd, args) {
|
||||||
|
if (args) {
|
||||||
|
if (args.some(arg => typeof arg !== "number")) {
|
||||||
|
warn(
|
||||||
|
`Commands.add - "${cmd}" has at least one non-number arg: "${args}".`
|
||||||
|
);
|
||||||
|
// "Fix" the wrong args by replacing them with 0.
|
||||||
|
const newArgs = args.map(arg => (typeof arg === "number" ? arg : 0));
|
||||||
|
this.cmds.push(cmd, ...newArgs);
|
||||||
|
} else {
|
||||||
|
this.cmds.push(cmd, ...args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.cmds.push(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CompiledFont {
|
class CompiledFont {
|
||||||
constructor(fontMatrix) {
|
constructor(fontMatrix) {
|
||||||
if (this.constructor === CompiledFont) {
|
if (this.constructor === CompiledFont) {
|
||||||
|
@ -757,8 +782,10 @@ class CompiledFont {
|
||||||
let fn = this.compiledGlyphs[glyphId];
|
let fn = this.compiledGlyphs[glyphId];
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
try {
|
try {
|
||||||
fn = this.compileGlyph(this.glyphs[glyphId], glyphId);
|
fn = this.compiledGlyphs[glyphId] = this.compileGlyph(
|
||||||
this.compiledGlyphs[glyphId] = fn;
|
this.glyphs[glyphId],
|
||||||
|
glyphId
|
||||||
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// Avoid attempting to re-compile a corrupt glyph.
|
// Avoid attempting to re-compile a corrupt glyph.
|
||||||
this.compiledGlyphs[glyphId] = NOOP;
|
this.compiledGlyphs[glyphId] = NOOP;
|
||||||
|
@ -793,16 +820,14 @@ class CompiledFont {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmds = [
|
const cmds = new Commands();
|
||||||
{ cmd: "save" },
|
cmds.add(FontRenderOps.SAVE);
|
||||||
{ cmd: "transform", args: fontMatrix.slice() },
|
cmds.add(FontRenderOps.TRANSFORM, fontMatrix.slice());
|
||||||
{ cmd: "scale", args: ["size", "-size"] },
|
cmds.add(FontRenderOps.SCALE);
|
||||||
];
|
|
||||||
this.compileGlyphImpl(code, cmds, glyphId);
|
this.compileGlyphImpl(code, cmds, glyphId);
|
||||||
|
cmds.add(FontRenderOps.RESTORE);
|
||||||
|
|
||||||
cmds.push({ cmd: "restore" });
|
return cmds.cmds;
|
||||||
|
|
||||||
return cmds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileGlyphImpl() {
|
compileGlyphImpl() {
|
||||||
|
|
|
@ -169,8 +169,8 @@ const DefaultStandardFontDataFactory =
|
||||||
* pixels, i.e. width * height. Images above this value will not be rendered.
|
* pixels, i.e. width * height. Images above this value will not be rendered.
|
||||||
* Use -1 for no limit, which is also the default value.
|
* Use -1 for no limit, which is also the default value.
|
||||||
* @property {boolean} [isEvalSupported] - Determines if we can evaluate strings
|
* @property {boolean} [isEvalSupported] - Determines if we can evaluate strings
|
||||||
* as JavaScript. Primarily used to improve performance of font rendering, and
|
* as JavaScript. Primarily used to improve performance of PDF functions.
|
||||||
* when parsing PDF functions. The default value is `true`.
|
* The default value is `true`.
|
||||||
* @property {boolean} [isOffscreenCanvasSupported] - Determines if we can use
|
* @property {boolean} [isOffscreenCanvasSupported] - Determines if we can use
|
||||||
* `OffscreenCanvas` in the worker. Primarily used to improve performance of
|
* `OffscreenCanvas` in the worker. Primarily used to improve performance of
|
||||||
* image conversion/rendering.
|
* image conversion/rendering.
|
||||||
|
@ -384,7 +384,6 @@ function getDocument(src) {
|
||||||
};
|
};
|
||||||
const transportParams = {
|
const transportParams = {
|
||||||
ignoreErrors,
|
ignoreErrors,
|
||||||
isEvalSupported,
|
|
||||||
disableFontFace,
|
disableFontFace,
|
||||||
fontExtraProperties,
|
fontExtraProperties,
|
||||||
enableXfa,
|
enableXfa,
|
||||||
|
@ -2744,7 +2743,6 @@ class WorkerTransport {
|
||||||
? (font, url) => globalThis.FontInspector.fontAdded(font, url)
|
? (font, url) => globalThis.FontInspector.fontAdded(font, url)
|
||||||
: null;
|
: null;
|
||||||
const font = new FontFaceObject(exportedData, {
|
const font = new FontFaceObject(exportedData, {
|
||||||
isEvalSupported: params.isEvalSupported,
|
|
||||||
disableFontFace: params.disableFontFace,
|
disableFontFace: params.disableFontFace,
|
||||||
ignoreErrors: params.ignoreErrors,
|
ignoreErrors: params.ignoreErrors,
|
||||||
inspectFont,
|
inspectFont,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
bytesToString,
|
bytesToString,
|
||||||
FeatureTest,
|
FontRenderOps,
|
||||||
isNodeJS,
|
isNodeJS,
|
||||||
shadow,
|
shadow,
|
||||||
string32,
|
string32,
|
||||||
|
@ -362,19 +362,13 @@ class FontLoader {
|
||||||
class FontFaceObject {
|
class FontFaceObject {
|
||||||
constructor(
|
constructor(
|
||||||
translatedData,
|
translatedData,
|
||||||
{
|
{ disableFontFace = false, ignoreErrors = false, inspectFont = null }
|
||||||
isEvalSupported = true,
|
|
||||||
disableFontFace = false,
|
|
||||||
ignoreErrors = false,
|
|
||||||
inspectFont = null,
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
this.compiledGlyphs = Object.create(null);
|
this.compiledGlyphs = Object.create(null);
|
||||||
// importing translated data
|
// importing translated data
|
||||||
for (const i in translatedData) {
|
for (const i in translatedData) {
|
||||||
this[i] = translatedData[i];
|
this[i] = translatedData[i];
|
||||||
}
|
}
|
||||||
this.isEvalSupported = isEvalSupported !== false;
|
|
||||||
this.disableFontFace = disableFontFace === true;
|
this.disableFontFace = disableFontFace === true;
|
||||||
this.ignoreErrors = ignoreErrors === true;
|
this.ignoreErrors = ignoreErrors === true;
|
||||||
this._inspectFont = inspectFont;
|
this._inspectFont = inspectFont;
|
||||||
|
@ -440,35 +434,85 @@ class FontFaceObject {
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
warn(`getPathGenerator - ignoring character: "${ex}".`);
|
warn(`getPathGenerator - ignoring character: "${ex}".`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(cmds) || cmds.length === 0) {
|
||||||
return (this.compiledGlyphs[character] = function (c, size) {
|
return (this.compiledGlyphs[character] = function (c, size) {
|
||||||
// No-op function, to allow rendering to continue.
|
// No-op function, to allow rendering to continue.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we can, compile cmds into JS for MAXIMUM SPEED...
|
const commands = [];
|
||||||
if (this.isEvalSupported && FeatureTest.isEvalSupported) {
|
for (let i = 0, ii = cmds.length; i < ii; ) {
|
||||||
const jsBuf = [];
|
switch (cmds[i++]) {
|
||||||
for (const current of cmds) {
|
case FontRenderOps.BEZIER_CURVE_TO:
|
||||||
const args = current.args !== undefined ? current.args.join(",") : "";
|
{
|
||||||
jsBuf.push("c.", current.cmd, "(", args, ");\n");
|
const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
|
||||||
|
commands.push(ctx => ctx.bezierCurveTo(a, b, c, d, e, f));
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FontRenderOps.MOVE_TO:
|
||||||
|
{
|
||||||
|
const [a, b] = cmds.slice(i, i + 2);
|
||||||
|
commands.push(ctx => ctx.moveTo(a, b));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FontRenderOps.LINE_TO:
|
||||||
|
{
|
||||||
|
const [a, b] = cmds.slice(i, i + 2);
|
||||||
|
commands.push(ctx => ctx.lineTo(a, b));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FontRenderOps.QUADRATIC_CURVE_TO:
|
||||||
|
{
|
||||||
|
const [a, b, c, d] = cmds.slice(i, i + 4);
|
||||||
|
commands.push(ctx => ctx.quadraticCurveTo(a, b, c, d));
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FontRenderOps.RESTORE:
|
||||||
|
commands.push(ctx => ctx.restore());
|
||||||
|
break;
|
||||||
|
case FontRenderOps.SAVE:
|
||||||
|
commands.push(ctx => ctx.save());
|
||||||
|
break;
|
||||||
|
case FontRenderOps.SCALE:
|
||||||
|
// The scale command must be at the third position, after save and
|
||||||
|
// transform (for the font matrix) commands (see also
|
||||||
|
// font_renderer.js).
|
||||||
|
// The goal is to just scale the canvas and then run the commands loop
|
||||||
|
// without the need to pass the size parameter to each command.
|
||||||
|
assert(
|
||||||
|
commands.length === 2,
|
||||||
|
"Scale command is only valid at the third position."
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case FontRenderOps.TRANSFORM:
|
||||||
|
{
|
||||||
|
const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
|
||||||
|
commands.push(ctx => ctx.transform(a, b, c, d, e, f));
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FontRenderOps.TRANSLATE:
|
||||||
|
{
|
||||||
|
const [a, b] = cmds.slice(i, i + 2);
|
||||||
|
commands.push(ctx => ctx.translate(a, b));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
return (this.compiledGlyphs[character] = new Function(
|
|
||||||
"c",
|
|
||||||
"size",
|
|
||||||
jsBuf.join("")
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
// ... but fall back on using Function.prototype.apply() if we're
|
|
||||||
// blocked from using eval() for whatever reason (like CSP policies).
|
return (this.compiledGlyphs[character] = function glyphDrawer(ctx, size) {
|
||||||
return (this.compiledGlyphs[character] = function (c, size) {
|
commands[0](ctx);
|
||||||
for (const current of cmds) {
|
commands[1](ctx);
|
||||||
if (current.cmd === "scale") {
|
ctx.scale(size, -size);
|
||||||
current.args = [size, -size];
|
for (let i = 2, ii = commands.length; i < ii; i++) {
|
||||||
}
|
commands[i](ctx);
|
||||||
// eslint-disable-next-line prefer-spread
|
|
||||||
c[current.cmd].apply(c, current.args);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1073,6 +1073,18 @@ function getUuid() {
|
||||||
|
|
||||||
const AnnotationPrefix = "pdfjs_internal_id_";
|
const AnnotationPrefix = "pdfjs_internal_id_";
|
||||||
|
|
||||||
|
const FontRenderOps = {
|
||||||
|
BEZIER_CURVE_TO: 0,
|
||||||
|
MOVE_TO: 1,
|
||||||
|
LINE_TO: 2,
|
||||||
|
QUADRATIC_CURVE_TO: 3,
|
||||||
|
RESTORE: 4,
|
||||||
|
SAVE: 5,
|
||||||
|
SCALE: 6,
|
||||||
|
TRANSFORM: 7,
|
||||||
|
TRANSLATE: 8,
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AbortException,
|
AbortException,
|
||||||
AnnotationActionEventType,
|
AnnotationActionEventType,
|
||||||
|
@ -1095,6 +1107,7 @@ export {
|
||||||
DocumentActionEventType,
|
DocumentActionEventType,
|
||||||
FeatureTest,
|
FeatureTest,
|
||||||
FONT_IDENTITY_MATRIX,
|
FONT_IDENTITY_MATRIX,
|
||||||
|
FontRenderOps,
|
||||||
FormatError,
|
FormatError,
|
||||||
getModificationDate,
|
getModificationDate,
|
||||||
getUuid,
|
getUuid,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue