Merge pull request #19902 from Snuffleupagus/core-document-shorten

Shorten the code in the `src/core/document.js` file
This commit is contained in:
calixteman 2025-05-09 15:48:49 +02:00 committed by GitHub
commit ff0d9b13a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -79,6 +79,8 @@ import { XRef } from "./xref.js";
const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792]; const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
class Page { class Page {
#resourcesPromise = null;
constructor({ constructor({
pdfManager, pdfManager,
xref, xref,
@ -108,7 +110,6 @@ class Page {
this.systemFontCache = systemFontCache; this.systemFontCache = systemFontCache;
this.nonBlendModesSet = nonBlendModesSet; this.nonBlendModesSet = nonBlendModesSet;
this.evaluatorOptions = pdfManager.evaluatorOptions; this.evaluatorOptions = pdfManager.evaluatorOptions;
this.resourcesPromise = null;
this.xfaFactory = xfaFactory; this.xfaFactory = xfaFactory;
const idCounters = { const idCounters = {
@ -125,10 +126,23 @@ class Page {
}; };
} }
/** #createPartialEvaluator(handler) {
* @private return new PartialEvaluator({
*/ xref: this.xref,
_getInheritableProperty(key, getArray = false) { handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
}
#getInheritableProperty(key, getArray = false) {
const value = getInheritableProperty({ const value = getInheritableProperty({
dict: this.pageDict, dict: this.pageDict,
key, key,
@ -152,7 +166,7 @@ class Page {
// For robustness: The spec states that a \Resources entry has to be // For robustness: The spec states that a \Resources entry has to be
// present, but can be empty. Some documents still omit it; in this case // present, but can be empty. Some documents still omit it; in this case
// we return an empty dictionary. // we return an empty dictionary.
const resources = this._getInheritableProperty("Resources"); const resources = this.#getInheritableProperty("Resources");
return shadow( return shadow(
this, this,
@ -161,12 +175,12 @@ class Page {
); );
} }
_getBoundingBox(name) { #getBoundingBox(name) {
if (this.xfaData) { if (this.xfaData) {
return this.xfaData.bbox; return this.xfaData.bbox;
} }
const box = lookupNormalRect( const box = lookupNormalRect(
this._getInheritableProperty(name, /* getArray = */ true), this.#getInheritableProperty(name, /* getArray = */ true),
null null
); );
@ -184,7 +198,7 @@ class Page {
return shadow( return shadow(
this, this,
"mediaBox", "mediaBox",
this._getBoundingBox("MediaBox") || LETTER_SIZE_MEDIABOX this.#getBoundingBox("MediaBox") || LETTER_SIZE_MEDIABOX
); );
} }
@ -193,7 +207,7 @@ class Page {
return shadow( return shadow(
this, this,
"cropBox", "cropBox",
this._getBoundingBox("CropBox") || this.mediaBox this.#getBoundingBox("CropBox") || this.mediaBox
); );
} }
@ -224,7 +238,7 @@ class Page {
} }
get rotate() { get rotate() {
let rotate = this._getInheritableProperty("Rotate") || 0; let rotate = this.#getInheritableProperty("Rotate") || 0;
// Normalize rotation so it's a multiple of 90 and between 0 and 270. // Normalize rotation so it's a multiple of 90 and between 0 and 270.
if (rotate % 90 !== 0) { if (rotate % 90 !== 0) {
@ -239,10 +253,7 @@ class Page {
return shadow(this, "rotate", rotate); return shadow(this, "rotate", rotate);
} }
/** #onSubStreamError(reason, objId) {
* @private
*/
_onSubStreamError(reason, objId) {
if (this.evaluatorOptions.ignoreErrors) { if (this.evaluatorOptions.ignoreErrors) {
warn(`getContentStream - ignoring sub-stream (${objId}): "${reason}".`); warn(`getContentStream - ignoring sub-stream (${objId}): "${reason}".`);
return; return;
@ -262,7 +273,7 @@ class Page {
if (Array.isArray(content)) { if (Array.isArray(content)) {
return new StreamsSequenceStream( return new StreamsSequenceStream(
content, content,
this._onSubStreamError.bind(this) this.#onSubStreamError.bind(this)
); );
} }
// Replace non-existent page content with empty content. // Replace non-existent page content with empty content.
@ -322,20 +333,7 @@ class Page {
if (this.xfaFactory) { if (this.xfaFactory) {
throw new Error("XFA: Cannot save new annotations."); throw new Error("XFA: Cannot save new annotations.");
} }
const partialEvaluator = this.#createPartialEvaluator(handler);
const partialEvaluator = new PartialEvaluator({
xref: this.xref,
handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
const deletedAnnotations = new RefSetCache(); const deletedAnnotations = new RefSetCache();
const existingAnnotations = new RefSet(); const existingAnnotations = new RefSet();
@ -378,19 +376,7 @@ class Page {
} }
async save(handler, task, annotationStorage, changes) { async save(handler, task, annotationStorage, changes) {
const partialEvaluator = new PartialEvaluator({ const partialEvaluator = this.#createPartialEvaluator(handler);
xref: this.xref,
handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
// Fetch the page's annotations and save the content // Fetch the page's annotations and save the content
// in case of interactive form fields. // in case of interactive form fields.
@ -414,8 +400,11 @@ class Page {
} }
async loadResources(keys) { async loadResources(keys) {
// TODO: add async `_getInheritableProperty` and remove this. // TODO: add async `#getInheritableProperty` and remove this.
await (this.resourcesPromise ??= this.pdfManager.ensure(this, "resources")); await (this.#resourcesPromise ??= this.pdfManager.ensure(
this,
"resources"
));
await ObjectLoader.load(this.resources, keys, this.xref); await ObjectLoader.load(this.resources, keys, this.xref);
} }
@ -450,19 +439,7 @@ class Page {
const contentStreamPromise = this.getContentStream(); const contentStreamPromise = this.getContentStream();
const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST); const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST);
const partialEvaluator = new PartialEvaluator({ const partialEvaluator = this.#createPartialEvaluator(handler);
xref: this.xref,
handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
const newAnnotsByPage = !this.xfaFactory const newAnnotsByPage = !this.xfaFactory
? getNewAnnotationsMap(annotationStorage) ? getNewAnnotationsMap(annotationStorage)
@ -670,19 +647,7 @@ class Page {
RESOURCES_KEYS_TEXT_CONTENT RESOURCES_KEYS_TEXT_CONTENT
); );
const partialEvaluator = new PartialEvaluator({ const partialEvaluator = this.#createPartialEvaluator(handler);
xref: this.xref,
handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
return partialEvaluator.getTextContent({ return partialEvaluator.getTextContent({
stream: contentStream, stream: contentStream,
@ -751,19 +716,7 @@ class Page {
} }
if (annotation.hasTextContent && isVisible) { if (annotation.hasTextContent && isVisible) {
partialEvaluator ||= new PartialEvaluator({ partialEvaluator ??= this.#createPartialEvaluator(handler);
xref: this.xref,
handler,
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalColorSpaceCache: this.globalColorSpaceCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions,
});
textContentPromises.push( textContentPromises.push(
annotation annotation
@ -787,7 +740,7 @@ class Page {
} }
get annotations() { get annotations() {
const annots = this._getInheritableProperty("Annots"); const annots = this.#getInheritableProperty("Annots");
return shadow(this, "annotations", Array.isArray(annots) ? annots : []); return shadow(this, "annotations", Array.isArray(annots) ? annots : []);
} }
@ -927,6 +880,10 @@ function find(stream, signature, limit = 1024, backwards = false) {
* The `PDFDocument` class holds all the (worker-thread) data of the PDF file. * The `PDFDocument` class holds all the (worker-thread) data of the PDF file.
*/ */
class PDFDocument { class PDFDocument {
#pagePromises = new Map();
#version = null;
constructor(pdfManager, stream) { constructor(pdfManager, stream) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
@ -943,8 +900,6 @@ class PDFDocument {
this.pdfManager = pdfManager; this.pdfManager = pdfManager;
this.stream = stream; this.stream = stream;
this.xref = new XRef(stream, pdfManager); this.xref = new XRef(stream, pdfManager);
this._pagePromises = new Map();
this._version = null;
const idCounters = { const idCounters = {
font: 0, font: 0,
@ -1065,7 +1020,7 @@ class PDFDocument {
} }
if (PDF_VERSION_REGEXP.test(version)) { if (PDF_VERSION_REGEXP.test(version)) {
this._version = version; this.#version = version;
} else { } else {
warn(`Invalid PDF header version: ${version}`); warn(`Invalid PDF header version: ${version}`);
} }
@ -1090,10 +1045,7 @@ class PDFDocument {
return shadow(this, "numPages", num); return shadow(this, "numPages", num);
} }
/** #hasOnlyDocumentSignatures(fields, recursionDepth = 0) {
* @private
*/
_hasOnlyDocumentSignatures(fields, recursionDepth = 0) {
const RECURSION_LIMIT = 10; const RECURSION_LIMIT = 10;
if (!Array.isArray(fields)) { if (!Array.isArray(fields)) {
@ -1106,10 +1058,10 @@ class PDFDocument {
} }
if (field.has("Kids")) { if (field.has("Kids")) {
if (++recursionDepth > RECURSION_LIMIT) { if (++recursionDepth > RECURSION_LIMIT) {
warn("_hasOnlyDocumentSignatures: maximum recursion depth reached"); warn("#hasOnlyDocumentSignatures: maximum recursion depth reached");
return false; return false;
} }
return this._hasOnlyDocumentSignatures( return this.#hasOnlyDocumentSignatures(
field.get("Kids"), field.get("Kids"),
recursionDepth recursionDepth
); );
@ -1401,7 +1353,7 @@ class PDFDocument {
* the catalog, if present, should overwrite the version from the header. * the catalog, if present, should overwrite the version from the header.
*/ */
get version() { get version() {
return this.catalog.version || this._version; return this.catalog.version || this.#version;
} }
get formInfo() { get formInfo() {
@ -1411,7 +1363,7 @@ class PDFDocument {
hasXfa: false, hasXfa: false,
hasSignatures: false, hasSignatures: false,
}; };
const acroForm = this.catalog.acroForm; const { acroForm } = this.catalog;
if (!acroForm) { if (!acroForm) {
return shadow(this, "formInfo", formInfo); return shadow(this, "formInfo", formInfo);
} }
@ -1438,7 +1390,7 @@ class PDFDocument {
const sigFlags = acroForm.get("SigFlags"); const sigFlags = acroForm.get("SigFlags");
const hasSignatures = !!(sigFlags & 0x1); const hasSignatures = !!(sigFlags & 0x1);
const hasOnlyDocumentSignatures = const hasOnlyDocumentSignatures =
hasSignatures && this._hasOnlyDocumentSignatures(fields); hasSignatures && this.#hasOnlyDocumentSignatures(fields);
formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures; formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
formInfo.hasSignatures = hasSignatures; formInfo.hasSignatures = hasSignatures;
} catch (ex) { } catch (ex) {
@ -1451,22 +1403,22 @@ class PDFDocument {
} }
get documentInfo() { get documentInfo() {
const { catalog, formInfo, xref } = this;
const docInfo = { const docInfo = {
PDFFormatVersion: this.version, PDFFormatVersion: this.version,
Language: this.catalog.lang, Language: catalog.lang,
EncryptFilterName: this.xref.encrypt EncryptFilterName: xref.encrypt?.filterName ?? null,
? this.xref.encrypt.filterName
: null,
IsLinearized: !!this.linearization, IsLinearized: !!this.linearization,
IsAcroFormPresent: this.formInfo.hasAcroForm, IsAcroFormPresent: formInfo.hasAcroForm,
IsXFAPresent: this.formInfo.hasXfa, IsXFAPresent: formInfo.hasXfa,
IsCollectionPresent: !!this.catalog.collection, IsCollectionPresent: !!catalog.collection,
IsSignaturesPresent: this.formInfo.hasSignatures, IsSignaturesPresent: formInfo.hasSignatures,
}; };
let infoDict; let infoDict;
try { try {
infoDict = this.xref.trailer.get("Info"); infoDict = xref.trailer.get("Info");
} catch (err) { } catch (err) {
if (err instanceof MissingDataException) { if (err instanceof MissingDataException) {
throw err; throw err;
@ -1565,7 +1517,7 @@ class PDFDocument {
]); ]);
} }
async _getLinearizationPage(pageIndex) { async #getLinearizationPage(pageIndex) {
const { catalog, linearization, xref } = this; const { catalog, linearization, xref } = this;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
@ -1608,7 +1560,7 @@ class PDFDocument {
} }
getPage(pageIndex) { getPage(pageIndex) {
const cachedPromise = this._pagePromises.get(pageIndex); const cachedPromise = this.#pagePromises.get(pageIndex);
if (cachedPromise) { if (cachedPromise) {
return cachedPromise; return cachedPromise;
} }
@ -1618,7 +1570,7 @@ class PDFDocument {
if (xfaFactory) { if (xfaFactory) {
promise = Promise.resolve([Dict.empty, null]); promise = Promise.resolve([Dict.empty, null]);
} else if (linearization?.pageFirst === pageIndex) { } else if (linearization?.pageFirst === pageIndex) {
promise = this._getLinearizationPage(pageIndex); promise = this.#getLinearizationPage(pageIndex);
} else { } else {
promise = catalog.getPageDict(pageIndex); promise = catalog.getPageDict(pageIndex);
} }
@ -1642,7 +1594,7 @@ class PDFDocument {
}) })
); );
this._pagePromises.set(pageIndex, promise); this.#pagePromises.set(pageIndex, promise);
return promise; return promise;
} }
@ -1657,7 +1609,7 @@ class PDFDocument {
// Clear out the various caches to ensure that we haven't stored any // Clear out the various caches to ensure that we haven't stored any
// inconsistent and/or incorrect state, since that could easily break // inconsistent and/or incorrect state, since that could easily break
// subsequent `this.getPage` calls. // subsequent `this.getPage` calls.
this._pagePromises.delete(0); this.#pagePromises.delete(0);
await this.cleanup(); await this.cleanup();
throw new XRefParseException(); throw new XRefParseException();
@ -1696,7 +1648,7 @@ class PDFDocument {
// Clear out the various caches to ensure that we haven't stored any // Clear out the various caches to ensure that we haven't stored any
// inconsistent and/or incorrect state, since that could easily break // inconsistent and/or incorrect state, since that could easily break
// subsequent `this.getPage` calls. // subsequent `this.getPage` calls.
this._pagePromises.delete(numPages - 1); this.#pagePromises.delete(numPages - 1);
await this.cleanup(); await this.cleanup();
if (reason instanceof XRefEntryException && !recoveryMode) { if (reason instanceof XRefEntryException && !recoveryMode) {
@ -1743,7 +1695,7 @@ class PDFDocument {
); );
} }
this._pagePromises.set(pageIndex, promise); this.#pagePromises.set(pageIndex, promise);
} }
catalog.setActualNumPages(pagesTree.size); catalog.setActualNumPages(pagesTree.size);
} }