Merge pull request #20197 from ryzokuken/sab-font-data
Serialize font data into an ArrayBuffer
This commit is contained in:
commit
44affa76b9
@ -72,6 +72,7 @@ import { BaseStream } from "./base_stream.js";
|
|||||||
import { bidi } from "./bidi.js";
|
import { bidi } from "./bidi.js";
|
||||||
import { ColorSpace } from "./colorspace.js";
|
import { ColorSpace } from "./colorspace.js";
|
||||||
import { ColorSpaceUtils } from "./colorspace_utils.js";
|
import { ColorSpaceUtils } from "./colorspace_utils.js";
|
||||||
|
import { FontInfo } from "../shared/obj-bin-transform.js";
|
||||||
import { getFontSubstitution } from "./font_substitutions.js";
|
import { getFontSubstitution } from "./font_substitutions.js";
|
||||||
import { getGlyphsUnicode } from "./glyphlist.js";
|
import { getGlyphsUnicode } from "./glyphlist.js";
|
||||||
import { getMetrics } from "./metrics.js";
|
import { getMetrics } from "./metrics.js";
|
||||||
@ -4709,12 +4710,21 @@ class TranslatedFont {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#sent = true;
|
this.#sent = true;
|
||||||
|
const fontData = this.font.exportData();
|
||||||
handler.send("commonobj", [
|
const transfer = [];
|
||||||
this.loadedName,
|
if (fontData.data) {
|
||||||
"Font",
|
if (fontData.data.charProcOperatorList) {
|
||||||
this.font.exportData(),
|
fontData.charProcOperatorList = fontData.data.charProcOperatorList;
|
||||||
]);
|
}
|
||||||
|
fontData.data = FontInfo.write(fontData.data);
|
||||||
|
transfer.push(fontData.data);
|
||||||
|
}
|
||||||
|
handler.send("commonobj", [this.loadedName, "Font", fontData], transfer);
|
||||||
|
// future path: switch to a SharedArrayBuffer
|
||||||
|
// const sab = new SharedArrayBuffer(data.byteLength);
|
||||||
|
// const view = new Uint8Array(sab);
|
||||||
|
// view.set(new Uint8Array(data));
|
||||||
|
// handler.send("commonobj", [this.loadedName, "Font", sab]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fallback(handler, evaluatorOptions) {
|
fallback(handler, evaluatorOptions) {
|
||||||
|
|||||||
@ -1144,19 +1144,27 @@ class Font {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exportData() {
|
exportData() {
|
||||||
const exportDataProps = this.fontExtraProperties
|
|
||||||
? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES]
|
|
||||||
: EXPORT_DATA_PROPERTIES;
|
|
||||||
|
|
||||||
const data = Object.create(null);
|
const data = Object.create(null);
|
||||||
for (const prop of exportDataProps) {
|
for (const prop of EXPORT_DATA_PROPERTIES) {
|
||||||
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) {
|
||||||
|
return { data };
|
||||||
|
}
|
||||||
|
|
||||||
|
const extra = Object.create(null);
|
||||||
|
for (const prop of EXPORT_DATA_EXTRA_PROPERTIES) {
|
||||||
|
const value = this[prop];
|
||||||
|
if (value !== undefined) {
|
||||||
|
extra[prop] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { data, extra };
|
||||||
}
|
}
|
||||||
|
|
||||||
fallbackToSystemFont(properties) {
|
fallbackToSystemFont(properties) {
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
|
|||||||
import { DOMFilterFactory } from "./filter_factory.js";
|
import { DOMFilterFactory } from "./filter_factory.js";
|
||||||
import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory";
|
import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory";
|
||||||
import { DOMWasmFactory } from "display-wasm_factory";
|
import { DOMWasmFactory } from "display-wasm_factory";
|
||||||
|
import { FontInfo } from "../shared/obj-bin-transform.js";
|
||||||
import { GlobalWorkerOptions } from "./worker_options.js";
|
import { GlobalWorkerOptions } from "./worker_options.js";
|
||||||
import { Metadata } from "./metadata.js";
|
import { Metadata } from "./metadata.js";
|
||||||
import { OptionalContentConfig } from "./optional_content_config.js";
|
import { OptionalContentConfig } from "./optional_content_config.js";
|
||||||
@ -2760,11 +2761,17 @@ class WorkerTransport {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fontData = new FontInfo(exportedData);
|
||||||
const inspectFont =
|
const inspectFont =
|
||||||
this._params.pdfBug && globalThis.FontInspector?.enabled
|
this._params.pdfBug && globalThis.FontInspector?.enabled
|
||||||
? (font, url) => globalThis.FontInspector.fontAdded(font, url)
|
? (font, url) => globalThis.FontInspector.fontAdded(font, url)
|
||||||
: null;
|
: null;
|
||||||
const font = new FontFaceObject(exportedData, inspectFont);
|
const font = new FontFaceObject(
|
||||||
|
fontData,
|
||||||
|
inspectFont,
|
||||||
|
exportedData.extra,
|
||||||
|
exportedData.charProcOperatorList
|
||||||
|
);
|
||||||
|
|
||||||
this.fontLoader
|
this.fontLoader
|
||||||
.bind(font)
|
.bind(font)
|
||||||
@ -2776,7 +2783,7 @@ class WorkerTransport {
|
|||||||
// rather than waiting for a `PDFDocumentProxy.cleanup` call.
|
// rather than waiting for a `PDFDocumentProxy.cleanup` call.
|
||||||
// Since `font.data` could be very large, e.g. in some cases
|
// Since `font.data` could be very large, e.g. in some cases
|
||||||
// multiple megabytes, this will help reduce memory usage.
|
// multiple megabytes, this will help reduce memory usage.
|
||||||
font.data = null;
|
font.clearData();
|
||||||
}
|
}
|
||||||
this.commonObjs.resolve(id, font);
|
this.commonObjs.resolve(id, font);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -355,12 +355,11 @@ class FontLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FontFaceObject {
|
class FontFaceObject {
|
||||||
constructor(translatedData, inspectFont = null) {
|
#fontData;
|
||||||
|
|
||||||
|
constructor(translatedData, inspectFont = null, extra, charProcOperatorList) {
|
||||||
this.compiledGlyphs = Object.create(null);
|
this.compiledGlyphs = Object.create(null);
|
||||||
// importing translated data
|
this.#fontData = translatedData;
|
||||||
for (const i in translatedData) {
|
|
||||||
this[i] = translatedData[i];
|
|
||||||
}
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
if (typeof this.disableFontFace !== "boolean") {
|
if (typeof this.disableFontFace !== "boolean") {
|
||||||
unreachable("disableFontFace must be available.");
|
unreachable("disableFontFace must be available.");
|
||||||
@ -370,6 +369,12 @@ class FontFaceObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._inspectFont = inspectFont;
|
this._inspectFont = inspectFont;
|
||||||
|
if (extra) {
|
||||||
|
Object.assign(this, extra);
|
||||||
|
}
|
||||||
|
if (charProcOperatorList) {
|
||||||
|
this.charProcOperatorList = charProcOperatorList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNativeFontFace() {
|
createNativeFontFace() {
|
||||||
@ -438,6 +443,102 @@ class FontFaceObject {
|
|||||||
}
|
}
|
||||||
return (this.compiledGlyphs[character] = path);
|
return (this.compiledGlyphs[character] = path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get black() {
|
||||||
|
return this.#fontData.black;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bold() {
|
||||||
|
return this.#fontData.bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disableFontFace() {
|
||||||
|
return this.#fontData.disableFontFace ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontExtraProperties() {
|
||||||
|
return this.#fontData.fontExtraProperties ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isInvalidPDFjsFont() {
|
||||||
|
return this.#fontData.isInvalidPDFjsFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isType3Font() {
|
||||||
|
return this.#fontData.isType3Font;
|
||||||
|
}
|
||||||
|
|
||||||
|
get italic() {
|
||||||
|
return this.#fontData.italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
get missingFile() {
|
||||||
|
return this.#fontData.missingFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
get remeasure() {
|
||||||
|
return this.#fontData.remeasure;
|
||||||
|
}
|
||||||
|
|
||||||
|
get vertical() {
|
||||||
|
return this.#fontData.vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ascent() {
|
||||||
|
return this.#fontData.ascent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultWidth() {
|
||||||
|
return this.#fontData.defaultWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
get descent() {
|
||||||
|
return this.#fontData.descent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bbox() {
|
||||||
|
return this.#fontData.bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontMatrix() {
|
||||||
|
return this.#fontData.fontMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fallbackName() {
|
||||||
|
return this.#fontData.fallbackName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loadedName() {
|
||||||
|
return this.#fontData.loadedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mimetype() {
|
||||||
|
return this.#fontData.mimetype;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.#fontData.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get data() {
|
||||||
|
return this.#fontData.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearData() {
|
||||||
|
this.#fontData.clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
get cssFontInfo() {
|
||||||
|
return this.#fontData.cssFontInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
get systemFontInfo() {
|
||||||
|
return this.#fontData.systemFontInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultVMetrics() {
|
||||||
|
return this.#fontData.defaultVMetrics;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { FontFaceObject, FontLoader };
|
export { FontFaceObject, FontLoader };
|
||||||
|
|||||||
609
src/shared/obj-bin-transform.js
Normal file
609
src/shared/obj-bin-transform.js
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
/* Copyright 2025 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { assert } from "./util.js";
|
||||||
|
|
||||||
|
class CssFontInfo {
|
||||||
|
#buffer;
|
||||||
|
|
||||||
|
#view;
|
||||||
|
|
||||||
|
#decoder;
|
||||||
|
|
||||||
|
static strings = ["fontFamily", "fontWeight", "italicAngle"];
|
||||||
|
|
||||||
|
static write(info) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encodedStrings = {};
|
||||||
|
let stringsLength = 0;
|
||||||
|
for (const prop of CssFontInfo.strings) {
|
||||||
|
const encoded = encoder.encode(info[prop]);
|
||||||
|
encodedStrings[prop] = encoded;
|
||||||
|
stringsLength += 4 + encoded.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = new ArrayBuffer(stringsLength);
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
for (const prop of CssFontInfo.strings) {
|
||||||
|
const encoded = encodedStrings[prop];
|
||||||
|
const length = encoded.length;
|
||||||
|
view.setUint32(offset, length);
|
||||||
|
data.set(encoded, offset + 4);
|
||||||
|
offset += 4 + length;
|
||||||
|
}
|
||||||
|
assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow");
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(buffer) {
|
||||||
|
this.#buffer = buffer;
|
||||||
|
this.#view = new DataView(this.#buffer);
|
||||||
|
this.#decoder = new TextDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
#readString(index) {
|
||||||
|
assert(index < CssFontInfo.strings.length, "Invalid string index");
|
||||||
|
let offset = 0;
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
offset += this.#view.getUint32(offset) + 4;
|
||||||
|
}
|
||||||
|
const length = this.#view.getUint32(offset);
|
||||||
|
return this.#decoder.decode(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontFamily() {
|
||||||
|
return this.#readString(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontWeight() {
|
||||||
|
return this.#readString(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
get italicAngle() {
|
||||||
|
return this.#readString(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SystemFontInfo {
|
||||||
|
#buffer;
|
||||||
|
|
||||||
|
#view;
|
||||||
|
|
||||||
|
#decoder;
|
||||||
|
|
||||||
|
static strings = ["css", "loadedName", "baseFontName", "src"];
|
||||||
|
|
||||||
|
static write(info) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encodedStrings = {};
|
||||||
|
let stringsLength = 0;
|
||||||
|
for (const prop of SystemFontInfo.strings) {
|
||||||
|
const encoded = encoder.encode(info[prop]);
|
||||||
|
encodedStrings[prop] = encoded;
|
||||||
|
stringsLength += 4 + encoded.length;
|
||||||
|
}
|
||||||
|
stringsLength += 4;
|
||||||
|
let encodedStyleStyle,
|
||||||
|
encodedStyleWeight,
|
||||||
|
lengthEstimate = 1 + stringsLength;
|
||||||
|
if (info.style) {
|
||||||
|
encodedStyleStyle = encoder.encode(info.style.style);
|
||||||
|
encodedStyleWeight = encoder.encode(info.style.weight);
|
||||||
|
lengthEstimate +=
|
||||||
|
4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = new ArrayBuffer(lengthEstimate);
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
view.setUint8(offset++, info.guessFallback ? 1 : 0);
|
||||||
|
view.setUint32(offset, 0);
|
||||||
|
offset += 4;
|
||||||
|
stringsLength = 0;
|
||||||
|
for (const prop of SystemFontInfo.strings) {
|
||||||
|
const encoded = encodedStrings[prop];
|
||||||
|
const length = encoded.length;
|
||||||
|
stringsLength += 4 + length;
|
||||||
|
view.setUint32(offset, length);
|
||||||
|
data.set(encoded, offset + 4);
|
||||||
|
offset += 4 + length;
|
||||||
|
}
|
||||||
|
view.setUint32(offset - stringsLength - 4, stringsLength);
|
||||||
|
|
||||||
|
if (info.style) {
|
||||||
|
view.setUint32(offset, encodedStyleStyle.length);
|
||||||
|
data.set(encodedStyleStyle, offset + 4);
|
||||||
|
offset += 4 + encodedStyleStyle.length;
|
||||||
|
view.setUint32(offset, encodedStyleWeight.length);
|
||||||
|
data.set(encodedStyleWeight, offset + 4);
|
||||||
|
offset += 4 + encodedStyleWeight.length;
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
offset <= buffer.byteLength,
|
||||||
|
"SubstitionInfo.write: Buffer overflow"
|
||||||
|
);
|
||||||
|
return buffer.transferToFixedLength(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(buffer) {
|
||||||
|
this.#buffer = buffer;
|
||||||
|
this.#view = new DataView(this.#buffer);
|
||||||
|
this.#decoder = new TextDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
get guessFallback() {
|
||||||
|
return this.#view.getUint8(0) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#readString(index) {
|
||||||
|
assert(index < SystemFontInfo.strings.length, "Invalid string index");
|
||||||
|
let offset = 5;
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
offset += this.#view.getUint32(offset) + 4;
|
||||||
|
}
|
||||||
|
const length = this.#view.getUint32(offset);
|
||||||
|
return this.#decoder.decode(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get css() {
|
||||||
|
return this.#readString(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get loadedName() {
|
||||||
|
return this.#readString(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseFontName() {
|
||||||
|
return this.#readString(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get src() {
|
||||||
|
return this.#readString(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
get style() {
|
||||||
|
let offset = 1;
|
||||||
|
offset += 4 + this.#view.getUint32(offset);
|
||||||
|
const styleLength = this.#view.getUint32(offset);
|
||||||
|
const style = this.#decoder.decode(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, styleLength)
|
||||||
|
);
|
||||||
|
offset += 4 + styleLength;
|
||||||
|
const weightLength = this.#view.getUint32(offset);
|
||||||
|
const weight = this.#decoder.decode(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, weightLength)
|
||||||
|
);
|
||||||
|
return { style, weight };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FontInfo {
|
||||||
|
static bools = [
|
||||||
|
"black",
|
||||||
|
"bold",
|
||||||
|
"disableFontFace",
|
||||||
|
"fontExtraProperties",
|
||||||
|
"isInvalidPDFjsFont",
|
||||||
|
"isType3Font",
|
||||||
|
"italic",
|
||||||
|
"missingFile",
|
||||||
|
"remeasure",
|
||||||
|
"vertical",
|
||||||
|
];
|
||||||
|
|
||||||
|
static numbers = ["ascent", "defaultWidth", "descent"];
|
||||||
|
|
||||||
|
static strings = ["fallbackName", "loadedName", "mimetype", "name"];
|
||||||
|
|
||||||
|
static #OFFSET_NUMBERS = Math.ceil((this.bools.length * 2) / 8);
|
||||||
|
|
||||||
|
static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8;
|
||||||
|
|
||||||
|
static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4;
|
||||||
|
|
||||||
|
static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6;
|
||||||
|
|
||||||
|
static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3;
|
||||||
|
|
||||||
|
#buffer;
|
||||||
|
|
||||||
|
#decoder;
|
||||||
|
|
||||||
|
#view;
|
||||||
|
|
||||||
|
constructor({ data, extra }) {
|
||||||
|
this.#buffer = data;
|
||||||
|
this.#decoder = new TextDecoder();
|
||||||
|
this.#view = new DataView(this.#buffer);
|
||||||
|
if (extra) {
|
||||||
|
Object.assign(this, extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#readBoolean(index) {
|
||||||
|
assert(index < FontInfo.bools.length, "Invalid boolean index");
|
||||||
|
const byteOffset = Math.floor(index / 4);
|
||||||
|
const bitOffset = (index * 2) % 8;
|
||||||
|
const value = (this.#view.getUint8(byteOffset) >> bitOffset) & 0x03;
|
||||||
|
return value === 0x00 ? undefined : value === 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
get black() {
|
||||||
|
return this.#readBoolean(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get bold() {
|
||||||
|
return this.#readBoolean(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
get disableFontFace() {
|
||||||
|
return this.#readBoolean(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontExtraProperties() {
|
||||||
|
return this.#readBoolean(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isInvalidPDFjsFont() {
|
||||||
|
return this.#readBoolean(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isType3Font() {
|
||||||
|
return this.#readBoolean(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
get italic() {
|
||||||
|
return this.#readBoolean(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
get missingFile() {
|
||||||
|
return this.#readBoolean(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
get remeasure() {
|
||||||
|
return this.#readBoolean(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
get vertical() {
|
||||||
|
return this.#readBoolean(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
#readNumber(index) {
|
||||||
|
assert(index < FontInfo.numbers.length, "Invalid number index");
|
||||||
|
return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
get ascent() {
|
||||||
|
return this.#readNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultWidth() {
|
||||||
|
return this.#readNumber(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
get descent() {
|
||||||
|
return this.#readNumber(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get bbox() {
|
||||||
|
let offset = FontInfo.#OFFSET_BBOX;
|
||||||
|
const numCoords = this.#view.getUint8(offset);
|
||||||
|
if (numCoords === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
offset += 1;
|
||||||
|
const bbox = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
bbox.push(this.#view.getInt16(offset, true));
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontMatrix() {
|
||||||
|
let offset = FontInfo.#OFFSET_FONT_MATRIX;
|
||||||
|
const numPoints = this.#view.getUint8(offset);
|
||||||
|
if (numPoints === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
offset += 1;
|
||||||
|
const fontMatrix = [];
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
fontMatrix.push(this.#view.getFloat64(offset, true));
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
return fontMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultVMetrics() {
|
||||||
|
let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS;
|
||||||
|
const numMetrics = this.#view.getUint8(offset);
|
||||||
|
if (numMetrics === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
offset += 1;
|
||||||
|
const defaultVMetrics = [];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
defaultVMetrics.push(this.#view.getInt16(offset, true));
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
return defaultVMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
#readString(index) {
|
||||||
|
assert(index < FontInfo.strings.length, "Invalid string index");
|
||||||
|
let offset = FontInfo.#OFFSET_STRINGS + 4;
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
offset += this.#view.getUint32(offset) + 4;
|
||||||
|
}
|
||||||
|
const length = this.#view.getUint32(offset);
|
||||||
|
const stringData = new Uint8Array(length);
|
||||||
|
stringData.set(new Uint8Array(this.#buffer, offset + 4, length));
|
||||||
|
return this.#decoder.decode(stringData);
|
||||||
|
}
|
||||||
|
|
||||||
|
get fallbackName() {
|
||||||
|
return this.#readString(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get loadedName() {
|
||||||
|
return this.#readString(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
get mimetype() {
|
||||||
|
return this.#readString(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.#readString(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
get data() {
|
||||||
|
let offset = FontInfo.#OFFSET_STRINGS;
|
||||||
|
const stringsLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + stringsLength;
|
||||||
|
const systemFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + systemFontInfoLength;
|
||||||
|
const cssFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + cssFontInfoLength;
|
||||||
|
const length = this.#view.getUint32(offset);
|
||||||
|
if (length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return new Uint8Array(this.#buffer, offset + 4, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearData() {
|
||||||
|
let offset = FontInfo.#OFFSET_STRINGS;
|
||||||
|
const stringsLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + stringsLength;
|
||||||
|
const systemFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + systemFontInfoLength;
|
||||||
|
const cssFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + cssFontInfoLength;
|
||||||
|
const length = this.#view.getUint32(offset);
|
||||||
|
const data = new Uint8Array(this.#buffer, offset + 4, length);
|
||||||
|
data.fill(0);
|
||||||
|
this.#view.setUint32(offset, 0);
|
||||||
|
// this.#buffer.resize(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
get cssFontInfo() {
|
||||||
|
let offset = FontInfo.#OFFSET_STRINGS;
|
||||||
|
const stringsLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + stringsLength;
|
||||||
|
const systemFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + systemFontInfoLength;
|
||||||
|
const cssFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
if (cssFontInfoLength === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const cssFontInfoData = new Uint8Array(cssFontInfoLength);
|
||||||
|
cssFontInfoData.set(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength)
|
||||||
|
);
|
||||||
|
return new CssFontInfo(cssFontInfoData.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
get systemFontInfo() {
|
||||||
|
let offset = FontInfo.#OFFSET_STRINGS;
|
||||||
|
const stringsLength = this.#view.getUint32(offset);
|
||||||
|
offset += 4 + stringsLength;
|
||||||
|
const systemFontInfoLength = this.#view.getUint32(offset);
|
||||||
|
if (systemFontInfoLength === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const systemFontInfoData = new Uint8Array(systemFontInfoLength);
|
||||||
|
systemFontInfoData.set(
|
||||||
|
new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength)
|
||||||
|
);
|
||||||
|
return new SystemFontInfo(systemFontInfoData.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static write(font) {
|
||||||
|
const systemFontInfoBuffer = font.systemFontInfo
|
||||||
|
? SystemFontInfo.write(font.systemFontInfo)
|
||||||
|
: null;
|
||||||
|
const cssFontInfoBuffer = font.cssFontInfo
|
||||||
|
? CssFontInfo.write(font.cssFontInfo)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encodedStrings = {};
|
||||||
|
let stringsLength = 0;
|
||||||
|
for (const prop of FontInfo.strings) {
|
||||||
|
encodedStrings[prop] = encoder.encode(font[prop]);
|
||||||
|
stringsLength += 4 + encodedStrings[prop].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lengthEstimate =
|
||||||
|
FontInfo.#OFFSET_STRINGS +
|
||||||
|
4 +
|
||||||
|
stringsLength +
|
||||||
|
4 +
|
||||||
|
(systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) +
|
||||||
|
4 +
|
||||||
|
(cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) +
|
||||||
|
4 +
|
||||||
|
(font.data ? font.data.length : 0);
|
||||||
|
|
||||||
|
const buffer = new ArrayBuffer(lengthEstimate);
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
const numBools = FontInfo.bools.length;
|
||||||
|
let boolByte = 0,
|
||||||
|
boolBit = 0;
|
||||||
|
for (let i = 0; i < numBools; i++) {
|
||||||
|
const value = font[FontInfo.bools[i]];
|
||||||
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01;
|
||||||
|
boolByte |= bits << boolBit;
|
||||||
|
boolBit += 2;
|
||||||
|
if (boolBit === 8 || i === numBools - 1) {
|
||||||
|
view.setUint8(offset++, boolByte);
|
||||||
|
boolByte = 0;
|
||||||
|
boolBit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
offset === FontInfo.#OFFSET_NUMBERS,
|
||||||
|
"FontInfo.write: Boolean properties offset mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const prop of FontInfo.numbers) {
|
||||||
|
view.setFloat64(offset, font[prop]);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
offset === FontInfo.#OFFSET_BBOX,
|
||||||
|
"FontInfo.write: Number properties offset mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (font.bbox) {
|
||||||
|
view.setUint8(offset++, 4);
|
||||||
|
for (const coord of font.bbox) {
|
||||||
|
view.setInt16(offset, coord, true);
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view.setUint8(offset++, 0);
|
||||||
|
offset += 2 * 4; // TODO: optimize this padding away
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(
|
||||||
|
offset === FontInfo.#OFFSET_FONT_MATRIX,
|
||||||
|
"FontInfo.write: BBox properties offset mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (font.fontMatrix) {
|
||||||
|
view.setUint8(offset++, 6);
|
||||||
|
for (const point of font.fontMatrix) {
|
||||||
|
view.setFloat64(offset, point, true);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view.setUint8(offset++, 0);
|
||||||
|
offset += 8 * 6; // TODO: optimize this padding away
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(
|
||||||
|
offset === FontInfo.#OFFSET_DEFAULT_VMETRICS,
|
||||||
|
"FontInfo.write: FontMatrix properties offset mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (font.defaultVMetrics) {
|
||||||
|
view.setUint8(offset++, 1);
|
||||||
|
for (const metric of font.defaultVMetrics) {
|
||||||
|
view.setInt16(offset, metric, true);
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view.setUint8(offset++, 0);
|
||||||
|
offset += 3 * 2; // TODO: optimize this padding away
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(
|
||||||
|
offset === FontInfo.#OFFSET_STRINGS,
|
||||||
|
"FontInfo.write: DefaultVMetrics properties offset mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
view.setUint32(FontInfo.#OFFSET_STRINGS, 0);
|
||||||
|
offset += 4;
|
||||||
|
for (const prop of FontInfo.strings) {
|
||||||
|
const encoded = encodedStrings[prop];
|
||||||
|
const length = encoded.length;
|
||||||
|
view.setUint32(offset, length);
|
||||||
|
data.set(encoded, offset + 4);
|
||||||
|
offset += 4 + length;
|
||||||
|
}
|
||||||
|
view.setUint32(
|
||||||
|
FontInfo.#OFFSET_STRINGS,
|
||||||
|
offset - FontInfo.#OFFSET_STRINGS - 4
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!systemFontInfoBuffer) {
|
||||||
|
view.setUint32(offset, 0);
|
||||||
|
offset += 4;
|
||||||
|
} else {
|
||||||
|
const length = systemFontInfoBuffer.byteLength;
|
||||||
|
view.setUint32(offset, length);
|
||||||
|
assert(
|
||||||
|
offset + 4 + length <= buffer.byteLength,
|
||||||
|
"FontInfo.write: Buffer overflow at systemFontInfo"
|
||||||
|
);
|
||||||
|
data.set(new Uint8Array(systemFontInfoBuffer), offset + 4);
|
||||||
|
offset += 4 + length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cssFontInfoBuffer) {
|
||||||
|
view.setUint32(offset, 0);
|
||||||
|
offset += 4;
|
||||||
|
} else {
|
||||||
|
const length = cssFontInfoBuffer.byteLength;
|
||||||
|
view.setUint32(offset, length);
|
||||||
|
assert(
|
||||||
|
offset + 4 + length <= buffer.byteLength,
|
||||||
|
"FontInfo.write: Buffer overflow at cssFontInfo"
|
||||||
|
);
|
||||||
|
data.set(new Uint8Array(cssFontInfoBuffer), offset + 4);
|
||||||
|
offset += 4 + length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font.data === undefined) {
|
||||||
|
view.setUint32(offset, 0);
|
||||||
|
offset += 4;
|
||||||
|
} else {
|
||||||
|
view.setUint32(offset, font.data.length);
|
||||||
|
data.set(font.data, offset + 4);
|
||||||
|
offset += 4 + font.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow");
|
||||||
|
return buffer.transferToFixedLength(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CssFontInfo, FontInfo, SystemFontInfo };
|
||||||
164
test/unit/bin_font_info_spec.js
Normal file
164
test/unit/bin_font_info_spec.js
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/* Copyright 2025 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
CssFontInfo,
|
||||||
|
FontInfo,
|
||||||
|
SystemFontInfo,
|
||||||
|
} from "../../src/shared/obj-bin-transform.js";
|
||||||
|
|
||||||
|
const cssFontInfo = {
|
||||||
|
fontFamily: "Sample Family",
|
||||||
|
fontWeight: "not a number",
|
||||||
|
italicAngle: "angle",
|
||||||
|
uselessProp: "doesn't matter",
|
||||||
|
};
|
||||||
|
|
||||||
|
const systemFontInfo = {
|
||||||
|
guessFallback: false,
|
||||||
|
css: "some string",
|
||||||
|
loadedName: "another string",
|
||||||
|
baseFontName: "base name",
|
||||||
|
src: "source",
|
||||||
|
style: {
|
||||||
|
style: "normal",
|
||||||
|
weight: "400",
|
||||||
|
uselessProp: "doesn't matter",
|
||||||
|
},
|
||||||
|
uselessProp: "doesn't matter",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fontInfo = {
|
||||||
|
black: true,
|
||||||
|
bold: true,
|
||||||
|
disableFontFace: true,
|
||||||
|
fontExtraProperties: true,
|
||||||
|
isInvalidPDFjsFont: true,
|
||||||
|
isType3Font: true,
|
||||||
|
italic: true,
|
||||||
|
missingFile: true,
|
||||||
|
remeasure: true,
|
||||||
|
vertical: true,
|
||||||
|
ascent: 1,
|
||||||
|
defaultWidth: 1,
|
||||||
|
descent: 1,
|
||||||
|
bbox: [1, 1, 1, 1],
|
||||||
|
fontMatrix: [1, 1, 1, 1, 1, 1],
|
||||||
|
defaultVMetrics: [1, 1, 1],
|
||||||
|
fallbackName: "string",
|
||||||
|
loadedName: "string",
|
||||||
|
mimetype: "string",
|
||||||
|
name: "string",
|
||||||
|
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
||||||
|
uselessProp: "something",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("font data serialization and deserialization", function () {
|
||||||
|
describe("CssFontInfo", function () {
|
||||||
|
it("must roundtrip correctly for CssFontInfo", function () {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
let sizeEstimate = 0;
|
||||||
|
for (const string of ["Sample Family", "not a number", "angle"]) {
|
||||||
|
sizeEstimate += 4 + encoder.encode(string).length;
|
||||||
|
}
|
||||||
|
const buffer = CssFontInfo.write(cssFontInfo);
|
||||||
|
expect(buffer.byteLength).toEqual(sizeEstimate);
|
||||||
|
const deserialized = new CssFontInfo(buffer);
|
||||||
|
expect(deserialized.fontFamily).toEqual("Sample Family");
|
||||||
|
expect(deserialized.fontWeight).toEqual("not a number");
|
||||||
|
expect(deserialized.italicAngle).toEqual("angle");
|
||||||
|
expect(deserialized.uselessProp).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SystemFontInfo", function () {
|
||||||
|
it("must roundtrip correctly for SystemFontInfo", function () {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
let sizeEstimate = 1 + 4;
|
||||||
|
for (const string of [
|
||||||
|
"some string",
|
||||||
|
"another string",
|
||||||
|
"base name",
|
||||||
|
"source",
|
||||||
|
"normal",
|
||||||
|
"400",
|
||||||
|
]) {
|
||||||
|
sizeEstimate += 4 + encoder.encode(string).length;
|
||||||
|
}
|
||||||
|
const buffer = SystemFontInfo.write(systemFontInfo);
|
||||||
|
expect(buffer.byteLength).toEqual(sizeEstimate);
|
||||||
|
const deserialized = new SystemFontInfo(buffer);
|
||||||
|
expect(deserialized.guessFallback).toEqual(false);
|
||||||
|
expect(deserialized.css).toEqual("some string");
|
||||||
|
expect(deserialized.loadedName).toEqual("another string");
|
||||||
|
expect(deserialized.baseFontName).toEqual("base name");
|
||||||
|
expect(deserialized.src).toEqual("source");
|
||||||
|
expect(deserialized.style.style).toEqual("normal");
|
||||||
|
expect(deserialized.style.weight).toEqual("400");
|
||||||
|
expect(deserialized.style.uselessProp).toBeUndefined();
|
||||||
|
expect(deserialized.uselessProp).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("FontInfo", function () {
|
||||||
|
it("must roundtrip correctly for FontInfo", function () {
|
||||||
|
let sizeEstimate = 92; // fixed offset until the strings
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
sizeEstimate += 4 + 4 * (4 + encoder.encode("string").length);
|
||||||
|
sizeEstimate += 4 + 4; // cssFontInfo and systemFontInfo
|
||||||
|
sizeEstimate += 4 + fontInfo.data.length;
|
||||||
|
const buffer = FontInfo.write(fontInfo);
|
||||||
|
expect(buffer.byteLength).toEqual(sizeEstimate);
|
||||||
|
const deserialized = new FontInfo({ data: buffer });
|
||||||
|
expect(deserialized.black).toEqual(true);
|
||||||
|
expect(deserialized.bold).toEqual(true);
|
||||||
|
expect(deserialized.disableFontFace).toEqual(true);
|
||||||
|
expect(deserialized.fontExtraProperties).toEqual(true);
|
||||||
|
expect(deserialized.isInvalidPDFjsFont).toEqual(true);
|
||||||
|
expect(deserialized.isType3Font).toEqual(true);
|
||||||
|
expect(deserialized.italic).toEqual(true);
|
||||||
|
expect(deserialized.missingFile).toEqual(true);
|
||||||
|
expect(deserialized.remeasure).toEqual(true);
|
||||||
|
expect(deserialized.vertical).toEqual(true);
|
||||||
|
expect(deserialized.ascent).toEqual(1);
|
||||||
|
expect(deserialized.defaultWidth).toEqual(1);
|
||||||
|
expect(deserialized.descent).toEqual(1);
|
||||||
|
expect(deserialized.bbox).toEqual([1, 1, 1, 1]);
|
||||||
|
expect(deserialized.fontMatrix).toEqual([1, 1, 1, 1, 1, 1]);
|
||||||
|
expect(deserialized.defaultVMetrics).toEqual([1, 1, 1]);
|
||||||
|
expect(deserialized.fallbackName).toEqual("string");
|
||||||
|
expect(deserialized.loadedName).toEqual("string");
|
||||||
|
expect(deserialized.mimetype).toEqual("string");
|
||||||
|
expect(deserialized.name).toEqual("string");
|
||||||
|
expect(Array.from(deserialized.data)).toEqual([
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||||
|
]);
|
||||||
|
expect(deserialized.uselessProp).toBeUndefined();
|
||||||
|
expect(deserialized.cssFontInfo).toBeNull();
|
||||||
|
expect(deserialized.systemFontInfo).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("nesting should work as expected", function () {
|
||||||
|
const buffer = FontInfo.write({
|
||||||
|
...fontInfo,
|
||||||
|
cssFontInfo,
|
||||||
|
systemFontInfo,
|
||||||
|
});
|
||||||
|
const deserialized = new FontInfo({ data: buffer });
|
||||||
|
expect(deserialized.cssFontInfo.fontWeight).toEqual("not a number");
|
||||||
|
expect(deserialized.systemFontInfo.src).toEqual("source");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -10,6 +10,7 @@
|
|||||||
"app_options_spec.js",
|
"app_options_spec.js",
|
||||||
"autolinker_spec.js",
|
"autolinker_spec.js",
|
||||||
"bidi_spec.js",
|
"bidi_spec.js",
|
||||||
|
"bin_font_info_spec.js",
|
||||||
"canvas_factory_spec.js",
|
"canvas_factory_spec.js",
|
||||||
"cff_parser_spec.js",
|
"cff_parser_spec.js",
|
||||||
"cmap_spec.js",
|
"cmap_spec.js",
|
||||||
|
|||||||
@ -53,6 +53,7 @@ async function initializePDFJS(callback) {
|
|||||||
"pdfjs-test/unit/app_options_spec.js",
|
"pdfjs-test/unit/app_options_spec.js",
|
||||||
"pdfjs-test/unit/autolinker_spec.js",
|
"pdfjs-test/unit/autolinker_spec.js",
|
||||||
"pdfjs-test/unit/bidi_spec.js",
|
"pdfjs-test/unit/bidi_spec.js",
|
||||||
|
"pdfjs-test/unit/bin_font_info_spec.js",
|
||||||
"pdfjs-test/unit/canvas_factory_spec.js",
|
"pdfjs-test/unit/canvas_factory_spec.js",
|
||||||
"pdfjs-test/unit/cff_parser_spec.js",
|
"pdfjs-test/unit/cff_parser_spec.js",
|
||||||
"pdfjs-test/unit/cmap_spec.js",
|
"pdfjs-test/unit/cmap_spec.js",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user