mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-10-04 06:18:00 +02:00
Shadow the getters in FontInfo, CssFontInfo, and SystemFontInfo (PR 20197 follow-up)
Prior to PR 20197 each font-instance had just a single copy of all its relevant font-properties on the main-thread, however that's unfortunately no longer the case. Given how the compilation was implemented, every single time that a font-property is accessed on the main-thread it'll now be re-parsed. Not only does this seem inefficient, especially for properties needed e.g. during text-rendering, but it'll lead to (potentially) a lot of duplicated object creation. Consider what currently happens when rendering all pages of the following PDFs: - `tracemonkey.pdf` contains `24` separate fonts, but the `fontMatrix`-property is re-parsed a whopping `1009` times (once per `showText` operator). - `standard_fonts.pdf` contains `14` separate fonts, however we create no less than `186` separate `StandardFontInfo` instances. - `xfa_bug1716816.pdf` contains `4` separate fonts, however we create no less than `45` separate `CssFontInfo` instances. This is obviously not limited to just the font-properties listed above, but those are mere examples to illustrate the problem. By shadowing the font-property getters on the main-thread, obviously with the exception of `FontInfo.prototype.data`, we only need to parse each font-property *once* per font-instance. *Note:* Unfortunately this *increases* the size of the `gulp mozcentral` bundle by `792` bytes, but that cannot really be helped since this seems like the correct thing to do regardless.
This commit is contained in:
@@ -423,12 +423,13 @@ class FontFaceObject {
|
||||
}
|
||||
|
||||
createNativeFontFace() {
|
||||
if (!this.data || this.disableFontFace) {
|
||||
const { data } = this; // Get `data` just once, since it's not shadowed.
|
||||
if (!data || this.disableFontFace) {
|
||||
return null;
|
||||
}
|
||||
let nativeFontFace;
|
||||
if (!this.cssFontInfo) {
|
||||
nativeFontFace = new FontFace(this.loadedName, this.data, {});
|
||||
nativeFontFace = new FontFace(this.loadedName, data, {});
|
||||
} else {
|
||||
const css = {
|
||||
weight: this.cssFontInfo.fontWeight,
|
||||
@@ -438,7 +439,7 @@ class FontFaceObject {
|
||||
}
|
||||
nativeFontFace = new FontFace(
|
||||
serializeFontFamily(this.cssFontInfo.fontFamily),
|
||||
this.data,
|
||||
data,
|
||||
css
|
||||
);
|
||||
}
|
||||
@@ -452,11 +453,12 @@ class FontFaceObject {
|
||||
throw new Error("Not implemented: createFontFaceRule");
|
||||
}
|
||||
|
||||
if (!this.data || this.disableFontFace) {
|
||||
const { data } = this; // Get `data` just once, since it's not shadowed.
|
||||
if (!data || this.disableFontFace) {
|
||||
return null;
|
||||
}
|
||||
// Add the @font-face rule to the document.
|
||||
const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`;
|
||||
const url = `url(data:${this.mimetype};base64,${data.toBase64()});`;
|
||||
let rule;
|
||||
if (!this.cssFontInfo) {
|
||||
rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert, BBOX_INIT, FeatureTest, Util } from "../shared/util.js";
|
||||
import {
|
||||
assert,
|
||||
BBOX_INIT,
|
||||
FeatureTest,
|
||||
shadow,
|
||||
Util,
|
||||
} from "../shared/util.js";
|
||||
import {
|
||||
CSS_FONT_INFO,
|
||||
FONT_INFO,
|
||||
@@ -47,15 +53,15 @@ class CssFontInfo {
|
||||
}
|
||||
|
||||
get fontFamily() {
|
||||
return this.#readString(0);
|
||||
return shadow(this, "fontFamily", this.#readString(0));
|
||||
}
|
||||
|
||||
get fontWeight() {
|
||||
return this.#readString(1);
|
||||
return shadow(this, "fontWeight", this.#readString(1));
|
||||
}
|
||||
|
||||
get italicAngle() {
|
||||
return this.#readString(2);
|
||||
return shadow(this, "italicAngle", this.#readString(2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,19 +81,19 @@ class SystemFontInfo {
|
||||
}
|
||||
|
||||
get css() {
|
||||
return this.#readString(0);
|
||||
return shadow(this, "css", this.#readString(0));
|
||||
}
|
||||
|
||||
get loadedName() {
|
||||
return this.#readString(1);
|
||||
return shadow(this, "loadedName", this.#readString(1));
|
||||
}
|
||||
|
||||
get baseFontName() {
|
||||
return this.#readString(2);
|
||||
return shadow(this, "baseFontName", this.#readString(2));
|
||||
}
|
||||
|
||||
get src() {
|
||||
return this.#readString(3);
|
||||
return shadow(this, "src", this.#readString(3));
|
||||
}
|
||||
|
||||
get style() {
|
||||
@@ -95,7 +101,7 @@ class SystemFontInfo {
|
||||
offset += 4 + this.#view.getUint32(offset);
|
||||
const style = readString(this.#buffer, this.#view, /* index = */ 0, offset),
|
||||
weight = readString(this.#buffer, this.#view, /* index = */ 1, offset);
|
||||
return { style, weight };
|
||||
return shadow(this, "style", { style, weight });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,43 +127,43 @@ class FontInfo {
|
||||
}
|
||||
|
||||
get black() {
|
||||
return this.#readBoolean(0);
|
||||
return shadow(this, "black", this.#readBoolean(0));
|
||||
}
|
||||
|
||||
get bold() {
|
||||
return this.#readBoolean(1);
|
||||
return shadow(this, "bold", this.#readBoolean(1));
|
||||
}
|
||||
|
||||
get disableFontFace() {
|
||||
return this.#readBoolean(2);
|
||||
return shadow(this, "disableFontFace", this.#readBoolean(2));
|
||||
}
|
||||
|
||||
get fontExtraProperties() {
|
||||
return this.#readBoolean(3);
|
||||
return shadow(this, "fontExtraProperties", this.#readBoolean(3));
|
||||
}
|
||||
|
||||
get isInvalidPDFjsFont() {
|
||||
return this.#readBoolean(4);
|
||||
return shadow(this, "isInvalidPDFjsFont", this.#readBoolean(4));
|
||||
}
|
||||
|
||||
get isType3Font() {
|
||||
return this.#readBoolean(5);
|
||||
return shadow(this, "isType3Font", this.#readBoolean(5));
|
||||
}
|
||||
|
||||
get italic() {
|
||||
return this.#readBoolean(6);
|
||||
return shadow(this, "italic", this.#readBoolean(6));
|
||||
}
|
||||
|
||||
get missingFile() {
|
||||
return this.#readBoolean(7);
|
||||
return shadow(this, "missingFile", this.#readBoolean(7));
|
||||
}
|
||||
|
||||
get remeasure() {
|
||||
return this.#readBoolean(8);
|
||||
return shadow(this, "remeasure", this.#readBoolean(8));
|
||||
}
|
||||
|
||||
get vertical() {
|
||||
return this.#readBoolean(9);
|
||||
return shadow(this, "vertical", this.#readBoolean(9));
|
||||
}
|
||||
|
||||
#readNumber(index) {
|
||||
@@ -166,20 +172,19 @@ class FontInfo {
|
||||
}
|
||||
|
||||
get ascent() {
|
||||
return this.#readNumber(0);
|
||||
return shadow(this, "ascent", this.#readNumber(0));
|
||||
}
|
||||
|
||||
get descent() {
|
||||
return this.#readNumber(1);
|
||||
return shadow(this, "descent", this.#readNumber(1));
|
||||
}
|
||||
|
||||
#readArray(offset, arrLen, lookupName, increment) {
|
||||
const len = this.#view.getUint8(offset);
|
||||
const len = this.#view.getUint8(offset++);
|
||||
if (len === 0) {
|
||||
return undefined;
|
||||
}
|
||||
assert(len === arrLen, "Invalid array length.");
|
||||
offset += 1;
|
||||
const arr = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
arr[i] = this.#view[lookupName](offset, true);
|
||||
@@ -189,20 +194,28 @@ class FontInfo {
|
||||
}
|
||||
|
||||
get bbox() {
|
||||
return this.#readArray(
|
||||
/* offset = */ FONT_INFO.OFFSET_BBOX,
|
||||
/* arrLen = */ 4,
|
||||
/* lookup = */ "getInt16",
|
||||
/* increment = */ 2
|
||||
return shadow(
|
||||
this,
|
||||
"bbox",
|
||||
this.#readArray(
|
||||
/* offset = */ FONT_INFO.OFFSET_BBOX,
|
||||
/* arrLen = */ 4,
|
||||
/* lookup = */ "getInt16",
|
||||
/* increment = */ 2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get fontMatrix() {
|
||||
return this.#readArray(
|
||||
/* offset = */ FONT_INFO.OFFSET_FONT_MATRIX,
|
||||
/* arrLen = */ 6,
|
||||
/* lookup = */ "getFloat64",
|
||||
/* increment = */ 8
|
||||
return shadow(
|
||||
this,
|
||||
"fontMatrix",
|
||||
this.#readArray(
|
||||
/* offset = */ FONT_INFO.OFFSET_FONT_MATRIX,
|
||||
/* arrLen = */ 6,
|
||||
/* lookup = */ "getFloat64",
|
||||
/* increment = */ 8
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -217,15 +230,15 @@ class FontInfo {
|
||||
}
|
||||
|
||||
get fallbackName() {
|
||||
return this.#readString(0);
|
||||
return shadow(this, "fallbackName", this.#readString(0));
|
||||
}
|
||||
|
||||
get loadedName() {
|
||||
return this.#readString(1);
|
||||
return shadow(this, "loadedName", this.#readString(1));
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.#readString(2);
|
||||
return shadow(this, "name", this.#readString(2));
|
||||
}
|
||||
|
||||
#getBufferOffset(index) {
|
||||
@@ -239,16 +252,17 @@ class FontInfo {
|
||||
return { offset, length };
|
||||
}
|
||||
|
||||
// NOTE: This cannot be shadowed, since it may be cleared.
|
||||
get data() {
|
||||
const { offset, length } = this.#getBufferOffset(2);
|
||||
return length === 0
|
||||
return !length
|
||||
? undefined
|
||||
: new Uint8Array(this.#buffer, offset + 4, length);
|
||||
}
|
||||
|
||||
clearData() {
|
||||
const { offset, length } = this.#getBufferOffset(2);
|
||||
if (length === 0) {
|
||||
if (!length) {
|
||||
return; // The data is either not present, or it was previously cleared.
|
||||
}
|
||||
this.#view.setUint32(offset, 0); // Zero the data-length.
|
||||
@@ -260,20 +274,22 @@ class FontInfo {
|
||||
|
||||
get cssFontInfo() {
|
||||
const { offset, length } = this.#getBufferOffset(1);
|
||||
if (length === 0) {
|
||||
return null;
|
||||
let info = null;
|
||||
if (length) {
|
||||
const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
|
||||
info = new CssFontInfo(data.buffer);
|
||||
}
|
||||
const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
|
||||
return new CssFontInfo(data.buffer);
|
||||
return shadow(this, "cssFontInfo", info);
|
||||
}
|
||||
|
||||
get systemFontInfo() {
|
||||
const { offset, length } = this.#getBufferOffset(0);
|
||||
if (length === 0) {
|
||||
return null;
|
||||
let info = null;
|
||||
if (length) {
|
||||
const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
|
||||
info = new SystemFontInfo(data.buffer);
|
||||
}
|
||||
const data = new Uint8Array(this.#buffer, offset + 4, length).slice();
|
||||
return new SystemFontInfo(data.buffer);
|
||||
return shadow(this, "systemFontInfo", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user