Merge pull request #20367 from calixteman/bin_format_font_path

Use a binary format for the glyph paths
This commit is contained in:
Tim van der Meij 2025-10-16 20:39:29 +02:00 committed by GitHub
commit 455e89a295
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 74 additions and 45 deletions

View File

@ -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() {

View File

@ -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,

View File

@ -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,

View File

@ -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.

View File

@ -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;