mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-07 17:05:38 +02:00
[api-minor] Improve the FileSpec
implementation
- Check that the `filename` is actually a string, before parsing it further. - Use proper "shadowing" in the `filename` getter. - Add a bit more validation of the data in `pickPlatformItem`. - Last, but not least, return both the original `filename` and the (path stripped) variant needed in the display-layer and viewer.
This commit is contained in:
parent
16dbf5dcfd
commit
2b69fb76ac
6 changed files with 47 additions and 46 deletions
|
@ -1619,8 +1619,8 @@ class Catalog {
|
||||||
/* xref = */ null,
|
/* xref = */ null,
|
||||||
/* skipContent = */ true
|
/* skipContent = */ true
|
||||||
);
|
);
|
||||||
const { filename } = fs.serializable;
|
const { rawFilename } = fs.serializable;
|
||||||
url = filename;
|
url = rawFilename;
|
||||||
} else if (typeof urlDict === "string") {
|
} else if (typeof urlDict === "string") {
|
||||||
url = urlDict;
|
url = urlDict;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ import { BaseStream } from "./base_stream.js";
|
||||||
import { Dict } from "./primitives.js";
|
import { Dict } from "./primitives.js";
|
||||||
|
|
||||||
function pickPlatformItem(dict) {
|
function pickPlatformItem(dict) {
|
||||||
|
if (!(dict instanceof Dict)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// Look for the filename in this order:
|
// Look for the filename in this order:
|
||||||
// UF, F, Unix, Mac, DOS
|
// UF, F, Unix, Mac, DOS
|
||||||
if (dict.has("UF")) {
|
if (dict.has("UF")) {
|
||||||
|
@ -34,6 +37,10 @@ function pickPlatformItem(dict) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripPath(str) {
|
||||||
|
return str.substring(str.lastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "A PDF file can refer to the contents of another file by using a File
|
* "A PDF file can refer to the contents of another file by using a File
|
||||||
* Specification (PDF 1.1)", see the spec (7.11) for more details.
|
* Specification (PDF 1.1)", see the spec (7.11) for more details.
|
||||||
|
@ -66,26 +73,27 @@ class FileSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
get filename() {
|
get filename() {
|
||||||
if (!this._filename && this.root) {
|
let filename = "";
|
||||||
const filename = pickPlatformItem(this.root) || "unnamed";
|
|
||||||
this._filename = stringToPDFString(filename)
|
const item = pickPlatformItem(this.root);
|
||||||
|
if (item && typeof item === "string") {
|
||||||
|
filename = stringToPDFString(item)
|
||||||
.replaceAll("\\\\", "\\")
|
.replaceAll("\\\\", "\\")
|
||||||
.replaceAll("\\/", "/")
|
.replaceAll("\\/", "/")
|
||||||
.replaceAll("\\", "/");
|
.replaceAll("\\", "/");
|
||||||
}
|
}
|
||||||
return this._filename;
|
return shadow(this, "filename", filename || "unnamed");
|
||||||
}
|
}
|
||||||
|
|
||||||
get content() {
|
get content() {
|
||||||
if (!this.#contentAvailable) {
|
if (!this.#contentAvailable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!this.contentRef && this.root) {
|
this._contentRef ||= pickPlatformItem(this.root?.get("EF"));
|
||||||
this.contentRef = pickPlatformItem(this.root.get("EF"));
|
|
||||||
}
|
|
||||||
let content = null;
|
let content = null;
|
||||||
if (this.contentRef) {
|
if (this._contentRef) {
|
||||||
const fileObj = this.xref.fetchIfRef(this.contentRef);
|
const fileObj = this.xref.fetchIfRef(this._contentRef);
|
||||||
if (fileObj instanceof BaseStream) {
|
if (fileObj instanceof BaseStream) {
|
||||||
content = fileObj.getBytes();
|
content = fileObj.getBytes();
|
||||||
} else {
|
} else {
|
||||||
|
@ -94,7 +102,7 @@ class FileSpec {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn("Embedded file specification does not have a content");
|
warn("Embedded file specification does not have any content");
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +119,8 @@ class FileSpec {
|
||||||
|
|
||||||
get serializable() {
|
get serializable() {
|
||||||
return {
|
return {
|
||||||
filename: this.filename,
|
rawFilename: this.filename,
|
||||||
|
filename: stripPath(this.filename),
|
||||||
content: this.content,
|
content: this.content,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,6 @@ import {
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
import {
|
import {
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
getFilenameFromUrl,
|
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
setLayerDimensions,
|
setLayerDimensions,
|
||||||
} from "./display_utils.js";
|
} from "./display_utils.js";
|
||||||
|
@ -2859,15 +2858,13 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
super(parameters, { isRenderable: true });
|
super(parameters, { isRenderable: true });
|
||||||
|
|
||||||
const { filename, content, description } = this.data.file;
|
const { file } = this.data;
|
||||||
this.filename = getFilenameFromUrl(filename, /* onlyStripPath = */ true);
|
this.filename = file.filename;
|
||||||
this.content = content;
|
this.content = file.content;
|
||||||
|
|
||||||
this.linkService.eventBus?.dispatch("fileattachmentannotation", {
|
this.linkService.eventBus?.dispatch("fileattachmentannotation", {
|
||||||
source: this,
|
source: this,
|
||||||
filename,
|
...file,
|
||||||
content,
|
|
||||||
description,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4033,9 +4033,12 @@ describe("annotation", function () {
|
||||||
idFactoryMock
|
idFactoryMock
|
||||||
);
|
);
|
||||||
expect(data.annotationType).toEqual(AnnotationType.FILEATTACHMENT);
|
expect(data.annotationType).toEqual(AnnotationType.FILEATTACHMENT);
|
||||||
expect(data.file.filename).toEqual("Test.txt");
|
expect(data.file).toEqual({
|
||||||
expect(data.file.content).toEqual(stringToBytes("Test attachment"));
|
rawFilename: "Test.txt",
|
||||||
expect(data.file.description).toEqual("abc");
|
filename: "Test.txt",
|
||||||
|
content: stringToBytes("Test attachment"),
|
||||||
|
description: "abc",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1475,12 +1475,12 @@ describe("api", function () {
|
||||||
const pdfDoc = await loadingTask.promise;
|
const pdfDoc = await loadingTask.promise;
|
||||||
const attachments = await pdfDoc.getAttachments();
|
const attachments = await pdfDoc.getAttachments();
|
||||||
|
|
||||||
const { filename, content, description } = attachments["foo.txt"];
|
expect(attachments["foo.txt"]).toEqual({
|
||||||
expect(filename).toEqual("foo.txt");
|
rawFilename: "foo.txt",
|
||||||
expect(content).toEqual(
|
filename: "foo.txt",
|
||||||
new Uint8Array([98, 97, 114, 32, 98, 97, 122, 32, 10])
|
content: new Uint8Array([98, 97, 114, 32, 98, 97, 122, 32, 10]),
|
||||||
);
|
description: "",
|
||||||
expect(description).toEqual("");
|
});
|
||||||
|
|
||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
});
|
});
|
||||||
|
@ -1490,7 +1490,9 @@ describe("api", function () {
|
||||||
const pdfDoc = await loadingTask.promise;
|
const pdfDoc = await loadingTask.promise;
|
||||||
const attachments = await pdfDoc.getAttachments();
|
const attachments = await pdfDoc.getAttachments();
|
||||||
|
|
||||||
const { filename, content, description } = attachments["empty.pdf"];
|
const { rawFilename, filename, content, description } =
|
||||||
|
attachments["empty.pdf"];
|
||||||
|
expect(rawFilename).toEqual("Empty page.pdf");
|
||||||
expect(filename).toEqual("Empty page.pdf");
|
expect(filename).toEqual("Empty page.pdf");
|
||||||
expect(content instanceof Uint8Array).toEqual(true);
|
expect(content instanceof Uint8Array).toEqual(true);
|
||||||
expect(content.length).toEqual(2357);
|
expect(content.length).toEqual(2357);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
/** @typedef {import("./download_manager.js").DownloadManager} DownloadManager */
|
/** @typedef {import("./download_manager.js").DownloadManager} DownloadManager */
|
||||||
|
|
||||||
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
||||||
import { getFilenameFromUrl } from "pdfjs-lib";
|
|
||||||
import { waitOnEventOrTimeout } from "./event_utils.js";
|
import { waitOnEventOrTimeout } from "./event_utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,19 +121,13 @@ class PDFAttachmentViewer extends BaseTreeViewer {
|
||||||
let attachmentsCount = 0;
|
let attachmentsCount = 0;
|
||||||
for (const name in attachments) {
|
for (const name in attachments) {
|
||||||
const item = attachments[name];
|
const item = attachments[name];
|
||||||
const content = item.content,
|
|
||||||
description = item.description,
|
|
||||||
filename = getFilenameFromUrl(
|
|
||||||
item.filename,
|
|
||||||
/* onlyStripPath = */ true
|
|
||||||
);
|
|
||||||
|
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.className = "treeItem";
|
div.className = "treeItem";
|
||||||
|
|
||||||
const element = document.createElement("a");
|
const element = document.createElement("a");
|
||||||
this._bindLink(element, { content, description, filename });
|
this._bindLink(element, item);
|
||||||
element.textContent = this._normalizeTextContent(filename);
|
element.textContent = this._normalizeTextContent(item.filename);
|
||||||
|
|
||||||
div.append(element);
|
div.append(element);
|
||||||
|
|
||||||
|
@ -148,7 +141,7 @@ class PDFAttachmentViewer extends BaseTreeViewer {
|
||||||
/**
|
/**
|
||||||
* Used to append FileAttachment annotations to the sidebar.
|
* Used to append FileAttachment annotations to the sidebar.
|
||||||
*/
|
*/
|
||||||
#appendAttachment({ filename, content, description }) {
|
#appendAttachment(item) {
|
||||||
const renderedPromise = this._renderedCapability.promise;
|
const renderedPromise = this._renderedCapability.promise;
|
||||||
|
|
||||||
renderedPromise.then(() => {
|
renderedPromise.then(() => {
|
||||||
|
@ -158,15 +151,12 @@ class PDFAttachmentViewer extends BaseTreeViewer {
|
||||||
const attachments = this._attachments || Object.create(null);
|
const attachments = this._attachments || Object.create(null);
|
||||||
|
|
||||||
for (const name in attachments) {
|
for (const name in attachments) {
|
||||||
if (filename === name) {
|
if (item.filename === name) {
|
||||||
return; // Ignore the new attachment if it already exists.
|
return; // Ignore the new attachment if it already exists.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attachments[filename] = {
|
attachments[item.filename] = item;
|
||||||
filename,
|
|
||||||
content,
|
|
||||||
description,
|
|
||||||
};
|
|
||||||
this.render({
|
this.render({
|
||||||
attachments,
|
attachments,
|
||||||
keepRenderedCapability: true,
|
keepRenderedCapability: true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue