diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 23c8b218d..e74089607 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -16,6 +16,8 @@ import { assert, bytesToString, + DrawOPS, + FeatureTest, FONT_IDENTITY_MATRIX, FormatError, unreachable, @@ -169,16 +171,16 @@ function compileGlyf(code, cmds, font) { function moveTo(x, y) { if (firstPoint) { // Close the current subpath in adding a straight line to the first point. - cmds.add("L", firstPoint); + cmds.add(DrawOPS.lineTo, firstPoint); } firstPoint = [x, y]; - cmds.add("M", [x, y]); + cmds.add(DrawOPS.moveTo, [x, y]); } function lineTo(x, y) { - cmds.add("L", [x, y]); + cmds.add(DrawOPS.lineTo, [x, y]); } function quadraticCurveTo(xa, ya, x, y) { - cmds.add("Q", [xa, ya, x, y]); + cmds.add(DrawOPS.quadraticCurveTo, [xa, ya, x, y]); } let i = 0; @@ -355,16 +357,16 @@ function compileCharString(charStringCode, cmds, font, glyphId) { function moveTo(x, y) { if (firstPoint) { // Close the current subpath in adding a straight line to the first point. - cmds.add("L", firstPoint); + cmds.add(DrawOPS.lineTo, firstPoint); } firstPoint = [x, y]; - cmds.add("M", [x, y]); + cmds.add(DrawOPS.moveTo, [x, y]); } function lineTo(x, y) { - cmds.add("L", [x, y]); + cmds.add(DrawOPS.lineTo, [x, y]); } function bezierCurveTo(x1, y1, x2, y2, x, y) { - cmds.add("C", [x1, y1, x2, y2, x, y]); + cmds.add(DrawOPS.curveTo, [x1, y1, x2, y2, x, y]); } const stack = []; @@ -749,7 +751,7 @@ class Commands { for (let i = 0, ii = args.length; i < ii; i += 2) { Util.applyTransform(args, currentTransform, i); } - this.cmds.push(`${cmd}${args.join(" ")}`); + this.cmds.push(cmd, ...args); } else { this.cmds.push(cmd); } @@ -771,8 +773,13 @@ class Commands { this.currentTransform = this.transformStack.pop() || [1, 0, 0, 1, 0, 0]; } - getSVG() { - return this.cmds.join(""); + getPath() { + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + return new Float16Array(this.cmds); + } + return new ( + FeatureTest.isFloat16ArraySupported ? Float16Array : Float32Array + )(this.cmds); } } @@ -834,9 +841,9 @@ class CompiledFont { const cmds = new Commands(); cmds.transform(fontMatrix.slice()); this.compileGlyphImpl(code, cmds, glyphId); - cmds.add("Z"); + cmds.add(DrawOPS.closePath); - return cmds.getSVG(); + return cmds.getPath(); } compileGlyphImpl() { diff --git a/src/display/canvas.js b/src/display/canvas.js index 6673df90f..12b556f71 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -18,7 +18,6 @@ import { Dependencies, } from "./canvas_dependency_tracker.js"; import { - DrawOPS, FeatureTest, FONT_IDENTITY_MATRIX, ImageKind, @@ -33,6 +32,7 @@ import { import { getCurrentTransform, getCurrentTransformInverse, + makePathFromDrawOPS, OutputScale, PixelsPerInch, } from "./display_utils.js"; @@ -1489,35 +1489,7 @@ class CanvasGraphics { } if (!(path instanceof Path2D)) { - // Using a SVG string is slightly slower than using the following loop. - const path2d = (data[0] = new Path2D()); - for (let i = 0, ii = path.length; i < ii; ) { - switch (path[i++]) { - case DrawOPS.moveTo: - path2d.moveTo(path[i++], path[i++]); - break; - case DrawOPS.lineTo: - path2d.lineTo(path[i++], path[i++]); - break; - case DrawOPS.curveTo: - path2d.bezierCurveTo( - path[i++], - path[i++], - path[i++], - path[i++], - path[i++], - path[i++] - ); - break; - case DrawOPS.closePath: - path2d.closePath(); - break; - default: - warn(`Unrecognized drawing path operator: ${path[i - 1]}`); - break; - } - } - path = path2d; + path = data[0] = makePathFromDrawOPS(path); } Util.axialAlignedBoundingBox( minMax, diff --git a/src/display/display_utils.js b/src/display/display_utils.js index 29c16c2ec..0a9c5e4db 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -15,6 +15,7 @@ import { BaseException, + DrawOPS, FeatureTest, shadow, Util, @@ -995,6 +996,44 @@ function renderRichText({ html, dir, className }, container) { container.append(fragment); } +function makePathFromDrawOPS(data) { + // Using a SVG string is slightly slower than using the following loop. + const path = new Path2D(); + if (!data) { + return path; + } + for (let i = 0, ii = data.length; i < ii; ) { + switch (data[i++]) { + case DrawOPS.moveTo: + path.moveTo(data[i++], data[i++]); + break; + case DrawOPS.lineTo: + path.lineTo(data[i++], data[i++]); + break; + case DrawOPS.curveTo: + path.bezierCurveTo( + data[i++], + data[i++], + data[i++], + data[i++], + data[i++], + data[i++] + ); + break; + case DrawOPS.quadraticCurveTo: + path.quadraticCurveTo(data[i++], data[i++], data[i++], data[i++]); + break; + case DrawOPS.closePath: + path.closePath(); + break; + default: + warn(`Unrecognized drawing path operator: ${data[i - 1]}`); + break; + } + } + return path; +} + export { applyOpacity, ColorScheme, @@ -1012,6 +1051,7 @@ export { isDataScheme, isPdfFile, isValidFetchUrl, + makePathFromDrawOPS, noContextMenu, OutputScale, PageViewport, diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 7d48d8c71..9e6fa1e34 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -23,6 +23,7 @@ import { unreachable, warn, } from "../shared/util.js"; +import { makePathFromDrawOPS } from "./display_utils.js"; class FontLoader { #systemFonts = new Set(); @@ -435,7 +436,7 @@ class FontFaceObject { } catch (ex) { warn(`getPathGenerator - ignoring character: "${ex}".`); } - const path = new Path2D(cmds || ""); + const path = makePathFromDrawOPS(cmds); if (!this.fontExtraProperties) { // Remove the raw path-string, since we don't need it anymore. diff --git a/src/shared/util.js b/src/shared/util.js index e2e617764..ffc010129 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -354,7 +354,8 @@ const DrawOPS = { moveTo: 0, lineTo: 1, curveTo: 2, - closePath: 3, + quadraticCurveTo: 3, + closePath: 4, }; const PasswordResponses = { @@ -649,6 +650,14 @@ class FeatureTest { ); } + static get isFloat16ArraySupported() { + return shadow( + this, + "isFloat16ArraySupported", + typeof Float16Array !== "undefined" + ); + } + static get platform() { const { platform, userAgent } = navigator;