Move the compileFontInfo call into the Font.prototype.exportData method (PR 20197 follow-up)

After the changes in PR 20197 the code in the `TranslatedFont.prototype.send` method is not all that readable[1] given how it handles e.g. the `charProcOperatorList` data used with Type3 fonts.
Since this is the only spot where `Font.prototype.exportData` is used, it seems much simpler to move the `compileFontInfo` call there and *directly* return the intended data rather than messing with it after the fact.

Finally, while it doesn't really matter, the patch flips the order of the `charProcOperatorList` and `extra` properties throughout the code-base since the former is used with Type3 fonts while the latter (effectively) requires that debugging is enabled.

---

[1] I had to re-read it twice, also looking at all the involved methods, in order to convince myself that it's actually correct.
This commit is contained in:
Jonas Jenwald 2026-03-14 09:19:44 +01:00
parent d38cddf2b6
commit 7d963ddc7c
7 changed files with 35 additions and 42 deletions

View File

@ -32,7 +32,6 @@ import {
import { CMapFactory, IdentityCMap } from "./cmap.js"; import { CMapFactory, IdentityCMap } from "./cmap.js";
import { Cmd, Dict, EOF, isName, Name, Ref, RefSet } from "./primitives.js"; import { Cmd, Dict, EOF, isName, Name, Ref, RefSet } from "./primitives.js";
import { import {
compileFontInfo,
compileFontPathInfo, compileFontPathInfo,
compilePatternInfo, compilePatternInfo,
} from "./obj_bin_transform_core.js"; } from "./obj_bin_transform_core.js";
@ -4804,16 +4803,11 @@ class TranslatedFont {
return; return;
} }
this.#sent = true; this.#sent = true;
const fontData = this.font.exportData();
const transfer = []; const fontData = this.font.exportData(),
if (fontData.data) { transfers = fontData.buffer ? [fontData.buffer] : null;
if (fontData.data.charProcOperatorList) {
fontData.charProcOperatorList = fontData.data.charProcOperatorList; handler.send("commonobj", [this.loadedName, "Font", fontData], transfers);
}
fontData.data = compileFontInfo(fontData.data);
transfer.push(fontData.data);
}
handler.send("commonobj", [this.loadedName, "Font", fontData], transfer);
// future path: switch to a SharedArrayBuffer // future path: switch to a SharedArrayBuffer
// const sab = new SharedArrayBuffer(data.byteLength); // const sab = new SharedArrayBuffer(data.byteLength);
// const view = new Uint8Array(sab); // const view = new Uint8Array(sab);

View File

@ -57,6 +57,7 @@ import {
} from "./standard_fonts.js"; } from "./standard_fonts.js";
import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js"; import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js";
import { CFFFont } from "./cff_font.js"; import { CFFFont } from "./cff_font.js";
import { compileFontInfo } from "./obj_bin_transform_core.js";
import { FontRendererFactory } from "./font_renderer.js"; import { FontRendererFactory } from "./font_renderer.js";
import { getFontBasicMetrics } from "./metrics.js"; import { getFontBasicMetrics } from "./metrics.js";
import { GlyfTable } from "./glyf.js"; import { GlyfTable } from "./glyf.js";
@ -80,7 +81,7 @@ const EXPORT_DATA_PROPERTIES = [
"bbox", "bbox",
"black", "black",
"bold", "bold",
"charProcOperatorList", // "charProcOperatorList" is handled separately, since it's not compiled.
"cssFontInfo", "cssFontInfo",
"data", "data",
"defaultVMetrics", "defaultVMetrics",
@ -973,6 +974,8 @@ class Font {
#glyphCache = new Map(); #glyphCache = new Map();
charProcOperatorList;
constructor(name, file, properties, evaluatorOptions) { constructor(name, file, properties, evaluatorOptions) {
this.name = name; this.name = name;
this.psName = null; this.psName = null;
@ -1147,28 +1150,26 @@ class Font {
return shadow(this, "renderer", renderer); return shadow(this, "renderer", renderer);
} }
exportData() { #getExportData(props) {
const data = Object.create(null); const data = Object.create(null);
for (const prop of EXPORT_DATA_PROPERTIES) { for (const prop of props) {
const value = this[prop]; const value = this[prop];
// Ignore properties that haven't been explicitly set. // Ignore properties that haven't been explicitly set.
if (value !== undefined) { if (value !== undefined) {
data[prop] = value; data[prop] = value;
} }
} }
return data;
}
if (!this.fontExtraProperties) { exportData() {
return { data }; return {
} buffer: compileFontInfo(this.#getExportData(EXPORT_DATA_PROPERTIES)),
charProcOperatorList: this.charProcOperatorList,
const extra = Object.create(null); extra: this.fontExtraProperties
for (const prop of EXPORT_DATA_EXTRA_PROPERTIES) { ? this.#getExportData(EXPORT_DATA_EXTRA_PROPERTIES)
const value = this[prop]; : undefined,
if (value !== undefined) { };
extra[prop] = value;
}
}
return { data, extra };
} }
fallbackToSystemFont(properties) { fallbackToSystemFont(properties) {

View File

@ -169,7 +169,6 @@ function compileFontInfo(font) {
view.setUint8(offset++, 0); view.setUint8(offset++, 0);
offset += 2 * 4; // TODO: optimize this padding away offset += 2 * 4; // TODO: optimize this padding away
} }
assert( assert(
offset === FONT_INFO.OFFSET_FONT_MATRIX, offset === FONT_INFO.OFFSET_FONT_MATRIX,
"compileFontInfo: BBox properties offset mismatch" "compileFontInfo: BBox properties offset mismatch"
@ -185,7 +184,6 @@ function compileFontInfo(font) {
view.setUint8(offset++, 0); view.setUint8(offset++, 0);
offset += 8 * 6; // TODO: optimize this padding away offset += 8 * 6; // TODO: optimize this padding away
} }
assert( assert(
offset === FONT_INFO.OFFSET_DEFAULT_VMETRICS, offset === FONT_INFO.OFFSET_DEFAULT_VMETRICS,
"compileFontInfo: FontMatrix properties offset mismatch" "compileFontInfo: FontMatrix properties offset mismatch"
@ -201,7 +199,6 @@ function compileFontInfo(font) {
view.setUint8(offset++, 0); view.setUint8(offset++, 0);
offset += 3 * 2; // TODO: optimize this padding away offset += 3 * 2; // TODO: optimize this padding away
} }
assert( assert(
offset === FONT_INFO.OFFSET_STRINGS, offset === FONT_INFO.OFFSET_STRINGS,
"compileFontInfo: DefaultVMetrics properties offset mismatch" "compileFontInfo: DefaultVMetrics properties offset mismatch"

View File

@ -2890,8 +2890,8 @@ class WorkerTransport {
const font = new FontFaceObject( const font = new FontFaceObject(
fontData, fontData,
inspectFont, inspectFont,
exportedData.extra, exportedData.charProcOperatorList,
exportedData.charProcOperatorList exportedData.extra
); );
this.fontLoader this.fontLoader

View File

@ -359,7 +359,7 @@ class FontFaceObject {
#fontData; #fontData;
constructor(translatedData, inspectFont = null, extra, charProcOperatorList) { constructor(translatedData, inspectFont = null, charProcOperatorList, extra) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
typeof translatedData.disableFontFace === "boolean", typeof translatedData.disableFontFace === "boolean",
@ -372,12 +372,13 @@ class FontFaceObject {
} }
this.#fontData = translatedData; this.#fontData = translatedData;
this._inspectFont = inspectFont; this._inspectFont = inspectFont;
if (extra) {
Object.assign(this, extra);
}
if (charProcOperatorList) { if (charProcOperatorList) {
this.charProcOperatorList = charProcOperatorList; this.charProcOperatorList = charProcOperatorList;
} }
if (extra) {
Object.assign(this, extra);
}
} }
createNativeFontFace() { createNativeFontFace() {

View File

@ -30,7 +30,7 @@ class CssFontInfo {
constructor(buffer) { constructor(buffer) {
this.#buffer = buffer; this.#buffer = buffer;
this.#view = new DataView(this.#buffer); this.#view = new DataView(buffer);
} }
#readString(index) { #readString(index) {
@ -67,7 +67,7 @@ class SystemFontInfo {
constructor(buffer) { constructor(buffer) {
this.#buffer = buffer; this.#buffer = buffer;
this.#view = new DataView(this.#buffer); this.#view = new DataView(buffer);
} }
get guessFallback() { get guessFallback() {
@ -125,9 +125,9 @@ class FontInfo {
#view; #view;
constructor({ data, extra }) { constructor({ buffer, extra }) {
this.#buffer = data; this.#buffer = buffer;
this.#view = new DataView(this.#buffer); this.#view = new DataView(buffer);
if (extra) { if (extra) {
Object.assign(this, extra); Object.assign(this, extra);
} }

View File

@ -133,7 +133,7 @@ describe("obj_bin_transform", function () {
sizeEstimate += 4 + fontInfo.data.length; sizeEstimate += 4 + fontInfo.data.length;
const buffer = compileFontInfo(fontInfo); const buffer = compileFontInfo(fontInfo);
expect(buffer.byteLength).toEqual(sizeEstimate); expect(buffer.byteLength).toEqual(sizeEstimate);
const deserialized = new FontInfo({ data: buffer }); const deserialized = new FontInfo({ buffer });
expect(deserialized.black).toEqual(true); expect(deserialized.black).toEqual(true);
expect(deserialized.bold).toEqual(true); expect(deserialized.bold).toEqual(true);
expect(deserialized.disableFontFace).toEqual(true); expect(deserialized.disableFontFace).toEqual(true);
@ -168,7 +168,7 @@ describe("obj_bin_transform", function () {
cssFontInfo, cssFontInfo,
systemFontInfo, systemFontInfo,
}); });
const deserialized = new FontInfo({ data: buffer }); const deserialized = new FontInfo({ buffer });
expect(deserialized.cssFontInfo.fontWeight).toEqual("not a number"); expect(deserialized.cssFontInfo.fontWeight).toEqual("not a number");
expect(deserialized.systemFontInfo.src).toEqual("source"); expect(deserialized.systemFontInfo.src).toEqual("source");
}); });