pdf.js.mirror/test/integration/document_properties_spec.mjs
Jonas Jenwald 1e62f01773 Improve handling of corrupt pages in the PDFDocumentProperties dialog
If the active page is corrupt that currently results in the entire dialog being "blank", thus providing no information, which seems unfortunate and it's easy enough to only skip `pageSizeField` in that rare case.
2026-05-18 17:38:43 +02:00

430 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright 2024 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 { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
import fs from "fs/promises";
const FIELDS = [
"fileName",
"fileSize",
"title",
"author",
"subject",
"keywords",
"creationDate",
"modificationDate",
"creator",
"producer",
"version",
"pageCount",
"pageSize",
"linearized",
];
async function openDocumentProperties(page) {
await page.click("#secondaryToolbarToggleButton");
await page.waitForSelector("#secondaryToolbar", { hidden: false });
await page.click("#documentProperties");
await page.waitForSelector("#documentPropertiesDialog", {
hidden: false,
});
}
async function closeDocumentProperties(page) {
await page.click("#documentPropertiesClose");
await page.waitForSelector("#documentPropertiesDialog", {
hidden: true,
});
}
async function checkFieldProperties(page, expectedProps) {
await page.waitForFunction(
`document.getElementById("fileSizeField").textContent !== "-"`
);
const promises = [];
for (const name of FIELDS) {
promises.push(
page.evaluate(
n => [n, document.getElementById(`${n}Field`).textContent],
name
)
);
}
const props = Object.fromEntries(await Promise.all(promises));
expect(props).toEqual(expectedProps);
}
function getFieldDataLastUpdated(page) {
return page.evaluate(
() =>
document.getElementById("documentPropertiesDialog").dataset
.fieldDataLastUpdated
);
}
describe("PDFDocumentProperties", () => {
describe("Document with both /Info and /Metadata", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("basicapi.pdf", ".textLayer .endOfContent");
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "basicapi.pdf",
fileSize: `${FSI}103${PDI} KB (${FSI}105,779${PDI} bytes)`,
title: "Basic API Test",
author: "Brendan Dahl",
subject: "-",
keywords: "TCPDF",
creationDate: "4/10/12, 7:30:26 AM",
modificationDate: "4/10/12, 7:30:26 AM",
creator: "TCPDF",
producer: "TCPDF 5.9.133 (http://www.tcpdf.org)",
version: "1.7",
pageCount: "3",
pageSize: `${FSI}8.27${PDI} × ${FSI}11.69${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}portrait${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
describe("Document with approximately A4-sized page", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"arial_unicode_en_cidfont.pdf",
".textLayer .endOfContent"
);
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "arial_unicode_en_cidfont.pdf",
fileSize: `${FSI}15.4${PDI} KB (${FSI}15,779${PDI} bytes)`,
title: "-",
author: "Adil Allawi",
subject: "-",
keywords: "-",
creationDate: "7/10/11, 7:17:28 PM",
modificationDate: "-",
creator: "Writer",
producer: "NeoOffice 3.2 Beta",
version: "1.4",
pageCount: "1",
pageSize: `${FSI}8.27${PDI} × ${FSI}11.69${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}portrait${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
describe("Document without contentLength", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("empty.pdf", ".textLayer .endOfContent");
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
// Open a binary PDF document, such that `contentLength` is undefined.
const base64 = await fs.readFile("./pdfs/clippath.pdf", {
encoding: "base64",
});
await page.evaluate(async b64 => {
await window.PDFViewerApplication.open({
data: Uint8Array.fromBase64(b64),
});
}, base64);
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "document.pdf",
fileSize: `${FSI}0.448${PDI} KB (${FSI}459${PDI} bytes)`,
title: "-",
author: "-",
subject: "-",
keywords: "-",
creationDate: "-",
modificationDate: "-",
creator: "-",
producer: "-",
version: "1.1",
pageCount: "1",
pageSize: `${FSI}2.78${PDI} × ${FSI}1.39${PDI} ${FSI}in${PDI} (${FSI}landscape${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
describe("Document with multiple pages, and changed viewer page/rotation", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("basicapi.pdf", ".textLayer .endOfContent");
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "basicapi.pdf",
fileSize: `${FSI}103${PDI} KB (${FSI}105,779${PDI} bytes)`,
title: "Basic API Test",
author: "Brendan Dahl",
subject: "-",
keywords: "TCPDF",
creationDate: "4/10/12, 7:30:26 AM",
modificationDate: "4/10/12, 7:30:26 AM",
creator: "TCPDF",
producer: "TCPDF 5.9.133 (http://www.tcpdf.org)",
version: "1.7",
pageCount: "3",
pageSize: `${FSI}8.27${PDI} × ${FSI}11.69${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}portrait${PDI})`,
linearized: "No",
});
const fieldDataLastUpdated = await getFieldDataLastUpdated(page);
await closeDocumentProperties(page);
// Ensure that immediately re-opening the dialog doesn't cause
// the field-data to be fetched and parsed again.
await openDocumentProperties(page);
expect(await getFieldDataLastUpdated(page)).toEqual(
fieldDataLastUpdated
);
await closeDocumentProperties(page);
// Goto the second page, and rotate the document.
await page.click("#next");
await page.waitForFunction(
() => window.PDFViewerApplication.page === 2
);
await page.keyboard.press("r");
await page.waitForFunction(
() => window.PDFViewerApplication.pdfViewer.pagesRotation === 90
);
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "basicapi.pdf",
fileSize: `${FSI}103${PDI} KB (${FSI}105,779${PDI} bytes)`,
title: "Basic API Test",
author: "Brendan Dahl",
subject: "-",
keywords: "TCPDF",
creationDate: "4/10/12, 7:30:26 AM",
modificationDate: "4/10/12, 7:30:26 AM",
creator: "TCPDF",
producer: "TCPDF 5.9.133 (http://www.tcpdf.org)",
version: "1.7",
pageCount: "3",
pageSize: `${FSI}11.69${PDI} × ${FSI}8.27${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}landscape${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
describe("Document with different page sizes", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("sizes.pdf", ".textLayer .endOfContent");
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "sizes.pdf",
fileSize: `${FSI}13.4${PDI} KB (${FSI}13,739${PDI} bytes)`,
title: "-",
author: "Yury ",
subject: "-",
keywords: "-",
creationDate: "6/26/11, 1:26:03 PM",
modificationDate: "-",
creator: "Writer",
producer: "OpenOffice.org 3.3",
version: "1.4",
pageCount: "3",
pageSize: `${FSI}8.5${PDI} × ${FSI}11${PDI} ${FSI}in${PDI} (${FSI}Letter${PDI}, ${FSI}portrait${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
// Goto the second page.
await page.click("#next");
await page.waitForFunction(
() => window.PDFViewerApplication.page === 2
);
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "sizes.pdf",
fileSize: `${FSI}13.4${PDI} KB (${FSI}13,739${PDI} bytes)`,
title: "-",
author: "Yury ",
subject: "-",
keywords: "-",
creationDate: "6/26/11, 1:26:03 PM",
modificationDate: "-",
creator: "Writer",
producer: "OpenOffice.org 3.3",
version: "1.4",
pageCount: "3",
pageSize: `${FSI}9.01${PDI} × ${FSI}4.49${PDI} ${FSI}in${PDI} (${FSI}landscape${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
describe("Document with corrupt page", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"Pages-tree-refs.pdf",
".textLayer .endOfContent",
null,
null,
{ page: 2 }
);
});
afterEach(async () => {
await closePages(pages);
});
it("check that the document properties dialog has the correct information", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "Pages-tree-refs.pdf",
fileSize: `${FSI}1.07${PDI} KB (${FSI}1,098${PDI} bytes)`,
title: "-",
author: "-",
subject: "-",
keywords: "-",
creationDate: "-",
modificationDate: "-",
creator: "-",
producer: "-",
version: "1.7",
pageCount: "2",
pageSize: "-",
linearized: "No",
});
await closeDocumentProperties(page);
// Goto the first page (which is *not* corrupt).
await page.click("#previous");
await page.waitForFunction(
() => window.PDFViewerApplication.page === 1
);
await openDocumentProperties(page);
await checkFieldProperties(page, {
fileName: "Pages-tree-refs.pdf",
fileSize: `${FSI}1.07${PDI} KB (${FSI}1,098${PDI} bytes)`,
title: "-",
author: "-",
subject: "-",
keywords: "-",
creationDate: "-",
modificationDate: "-",
creator: "-",
producer: "-",
version: "1.7",
pageCount: "2",
pageSize: `${FSI}8.27${PDI} × ${FSI}11.69${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}portrait${PDI})`,
linearized: "No",
});
await closeDocumentProperties(page);
})
);
});
});
});