Remove caching/shadowing from the FileSpec getters, and simplify the code

Given that only the `FileSpec.prototype.serializable` getter is ever invoked from "outside" of the class, and only once per `FileSpec`-instance, the caching/shadowing isn't actually necessary.

Furthermore the `_contentRef`-caching wasn't actually correct, since it ended up storing a `BaseStream`-instance and those should *generally* never be cached.
(Since calling `BaseStream.prototype.getBytes()` more than once, without resetting the stream in between, will return an empty TypedArray after the first time.)
This commit is contained in:
Jonas Jenwald 2026-01-25 13:16:29 +01:00
parent 84b5866853
commit 640a3106d5
3 changed files with 21 additions and 37 deletions

View File

@ -5290,8 +5290,8 @@ class FileAttachmentAnnotation extends MarkupAnnotation {
constructor(params) {
super(params);
const { dict, xref } = params;
const file = new FileSpec(dict.get("FS"), xref);
const { dict } = params;
const file = new FileSpec(dict.get("FS"));
this.data.annotationType = AnnotationType.FILEATTACHMENT;
this.data.hasOwnCanvas = this.data.noRotate;

View File

@ -1057,7 +1057,7 @@ class Catalog {
if (obj instanceof Dict && obj.has("EmbeddedFiles")) {
const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
for (const [key, value] of nameTree.getAll()) {
const fs = new FileSpec(value, this.xref);
const fs = new FileSpec(value);
attachments ??= Object.create(null);
attachments[stringToPDFString(key, /* keepEscapeSequence = */ true)] =
fs.serializable;
@ -1623,11 +1623,7 @@ class Catalog {
case "GoToR":
const urlDict = action.get("F");
if (urlDict instanceof Dict) {
const fs = new FileSpec(
urlDict,
/* xref = */ null,
/* skipContent = */ true
);
const fs = new FileSpec(urlDict, /* skipContent = */ true);
({ rawFilename: url } = fs.serializable);
} else if (typeof urlDict === "string") {
url = urlDict;

View File

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { shadow, stringToPDFString, warn } from "../shared/util.js";
import { stringToPDFString, warn } from "../shared/util.js";
import { BaseStream } from "./base_stream.js";
import { Dict } from "./primitives.js";
@ -43,11 +43,10 @@ function stripPath(str) {
class FileSpec {
#contentAvailable = false;
constructor(root, xref, skipContent = false) {
constructor(root, skipContent = false) {
if (!(root instanceof Dict)) {
return;
}
this.xref = xref;
this.root = root;
if (root.has("FS")) {
this.fs = root.get("FS");
@ -65,56 +64,45 @@ class FileSpec {
}
get filename() {
let filename = "";
const item = pickPlatformItem(this.root);
let name;
if (item && typeof item === "string") {
filename = stringToPDFString(item, /* keepEscapeSequence = */ true)
name = stringToPDFString(item, /* keepEscapeSequence = */ true)
.replaceAll("\\\\", "\\")
.replaceAll("\\/", "/")
.replaceAll("\\", "/");
}
return shadow(this, "filename", filename || "unnamed");
return name || "unnamed";
}
get content() {
if (!this.#contentAvailable) {
return null;
}
this._contentRef ||= pickPlatformItem(this.root?.get("EF"));
const ef = pickPlatformItem(this.root?.get("EF"));
let content = null;
if (this._contentRef) {
const fileObj = this.xref.fetchIfRef(this._contentRef);
if (fileObj instanceof BaseStream) {
content = fileObj.getBytes();
} else {
warn(
"Embedded file specification points to non-existing/invalid content"
);
}
} else {
warn("Embedded file specification does not have any content");
if (ef instanceof BaseStream) {
return ef.getBytes();
}
return content;
warn("Embedded file specification points to non-existing/invalid content");
return null;
}
get description() {
let description = "";
const desc = this.root?.get("Desc");
if (desc && typeof desc === "string") {
description = stringToPDFString(desc);
return stringToPDFString(desc);
}
return shadow(this, "description", description);
return "";
}
get serializable() {
const { filename, content, description } = this;
return {
rawFilename: this.filename,
filename: stripPath(this.filename),
content: this.content,
description: this.description,
rawFilename: filename,
filename: stripPath(filename),
content,
description,
};
}
}