Prefer the /Metadata, when available, in the document properties dialog (bug 1966086)
This commit is contained in:
parent
d4d0081ac9
commit
ab89773e49
97
test/integration/document_properties_spec.mjs
Normal file
97
test/integration/document_properties_spec.mjs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/* 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";
|
||||||
|
|
||||||
|
const FIELDS = [
|
||||||
|
"fileName",
|
||||||
|
"fileSize",
|
||||||
|
"title",
|
||||||
|
"author",
|
||||||
|
"subject",
|
||||||
|
"keywords",
|
||||||
|
"creationDate",
|
||||||
|
"modificationDate",
|
||||||
|
"creator",
|
||||||
|
"producer",
|
||||||
|
"version",
|
||||||
|
"pageCount",
|
||||||
|
"pageSize",
|
||||||
|
"linearized",
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("PDFDocumentProperties", () => {
|
||||||
|
async function getFieldProperties(page) {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
for (const name of FIELDS) {
|
||||||
|
promises.push(
|
||||||
|
page.evaluate(
|
||||||
|
n => [n, document.getElementById(`${n}Field`).textContent],
|
||||||
|
name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Object.fromEntries(await Promise.all(promises));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Document with both /Info and /Metadata", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("basicapi.pdf", ".textLayer .endOfContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the document properties dialog has the correct information", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#secondaryToolbarToggleButton");
|
||||||
|
await page.waitForSelector("#secondaryToolbar", { hidden: false });
|
||||||
|
|
||||||
|
await page.click("#documentProperties");
|
||||||
|
await page.waitForSelector("#documentPropertiesDialog", {
|
||||||
|
hidden: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
`document.getElementById("fileSizeField").textContent !== "-"`
|
||||||
|
);
|
||||||
|
const props = await getFieldProperties(page);
|
||||||
|
|
||||||
|
expect(props).toEqual({
|
||||||
|
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",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { closePages, loadAndWait } from "./test_utils.mjs";
|
import { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
|
||||||
|
|
||||||
function fuzzyMatch(a, b, browserName, pixelFuzz = 3) {
|
function fuzzyMatch(a, b, browserName, pixelFuzz = 3) {
|
||||||
expect(a)
|
expect(a)
|
||||||
@ -110,10 +110,6 @@ describe("find bar", () => {
|
|||||||
);
|
);
|
||||||
const resultElement = await page.waitForSelector("#findResultsCount");
|
const resultElement = await page.waitForSelector("#findResultsCount");
|
||||||
const resultText = await resultElement.evaluate(el => el.textContent);
|
const resultText = await resultElement.evaluate(el => el.textContent);
|
||||||
/** Unicode bidi isolation characters. */
|
|
||||||
const FSI = "\u2068";
|
|
||||||
const PDI = "\u2069";
|
|
||||||
// Fluent adds these markers to the result text.
|
|
||||||
expect(resultText).toEqual(`${FSI}1${PDI} of ${FSI}1${PDI} match`);
|
expect(resultText).toEqual(`${FSI}1${PDI} of ${FSI}1${PDI} match`);
|
||||||
const selectedElement = await page.waitForSelector(
|
const selectedElement = await page.waitForSelector(
|
||||||
".highlight.selected"
|
".highlight.selected"
|
||||||
|
|||||||
@ -31,6 +31,7 @@ async function runTests(results) {
|
|||||||
"autolinker_spec.mjs",
|
"autolinker_spec.mjs",
|
||||||
"caret_browsing_spec.mjs",
|
"caret_browsing_spec.mjs",
|
||||||
"copy_paste_spec.mjs",
|
"copy_paste_spec.mjs",
|
||||||
|
"document_properties_spec.mjs",
|
||||||
"find_spec.mjs",
|
"find_spec.mjs",
|
||||||
"freetext_editor_spec.mjs",
|
"freetext_editor_spec.mjs",
|
||||||
"highlight_editor_spec.mjs",
|
"highlight_editor_spec.mjs",
|
||||||
|
|||||||
@ -17,10 +17,12 @@ import {
|
|||||||
awaitPromise,
|
awaitPromise,
|
||||||
closePages,
|
closePages,
|
||||||
copy,
|
copy,
|
||||||
|
FSI,
|
||||||
getEditorSelector,
|
getEditorSelector,
|
||||||
getRect,
|
getRect,
|
||||||
loadAndWait,
|
loadAndWait,
|
||||||
paste,
|
paste,
|
||||||
|
PDI,
|
||||||
switchToEditor,
|
switchToEditor,
|
||||||
waitForPointerUp,
|
waitForPointerUp,
|
||||||
waitForTimeout,
|
waitForTimeout,
|
||||||
@ -188,7 +190,7 @@ describe("Signature Editor", () => {
|
|||||||
|
|
||||||
// Check the aria description.
|
// Check the aria description.
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`${editorSelector}[aria-description="Signature editor: \u2068Hello World\u2069"]`
|
`${editorSelector}[aria-description="Signature editor: ${FSI}Hello World${PDI}"]`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Edit the description.
|
// Edit the description.
|
||||||
|
|||||||
@ -882,6 +882,10 @@ async function moveEditor(page, selector, n, pressKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode bidi isolation characters, Fluent adds these markers to the text.
|
||||||
|
const FSI = "\u2068";
|
||||||
|
const PDI = "\u2069";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
applyFunctionToEditor,
|
applyFunctionToEditor,
|
||||||
awaitPromise,
|
awaitPromise,
|
||||||
@ -894,6 +898,7 @@ export {
|
|||||||
createPromise,
|
createPromise,
|
||||||
dragAndDrop,
|
dragAndDrop,
|
||||||
firstPageOnTop,
|
firstPageOnTop,
|
||||||
|
FSI,
|
||||||
getAnnotationSelector,
|
getAnnotationSelector,
|
||||||
getAnnotationStorage,
|
getAnnotationStorage,
|
||||||
getComputedStyleSelector,
|
getComputedStyleSelector,
|
||||||
@ -929,6 +934,7 @@ export {
|
|||||||
moveEditor,
|
moveEditor,
|
||||||
paste,
|
paste,
|
||||||
pasteFromClipboard,
|
pasteFromClipboard,
|
||||||
|
PDI,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
selectEditor,
|
selectEditor,
|
||||||
selectEditors,
|
selectEditors,
|
||||||
|
|||||||
41
web/app.js
41
web/app.js
@ -598,7 +598,8 @@ const PDFViewerApplication = {
|
|||||||
overlayManager,
|
overlayManager,
|
||||||
eventBus,
|
eventBus,
|
||||||
l10n,
|
l10n,
|
||||||
/* fileNameLookup = */ () => this._docFilename
|
/* fileNameLookup = */ () => this._docFilename,
|
||||||
|
/* titleLookup = */ () => this._docTitle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1003,6 +1004,23 @@ const PDFViewerApplication = {
|
|||||||
return this._contentDispositionFilename || getPdfFilenameFromUrl(this.url);
|
return this._contentDispositionFilename || getPdfFilenameFromUrl(this.url);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get _docTitle() {
|
||||||
|
const { documentInfo, metadata } = this;
|
||||||
|
|
||||||
|
const title = metadata?.get("dc:title");
|
||||||
|
if (title) {
|
||||||
|
// Ghostscript can produce invalid 'dc:title' Metadata entries:
|
||||||
|
// - The title may be "Untitled" (fixes bug 1031612).
|
||||||
|
// - The title may contain incorrectly encoded characters, which thus
|
||||||
|
// looks broken, hence we ignore the Metadata entry when it contains
|
||||||
|
// characters from the Specials Unicode block (fixes bug 1605526).
|
||||||
|
if (title !== "Untitled" && !/[\uFFF0-\uFFFF]/g.test(title)) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return documentInfo.Title;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -1621,25 +1639,12 @@ const PDFViewerApplication = {
|
|||||||
// Provides some basic debug information
|
// Provides some basic debug information
|
||||||
console.log(
|
console.log(
|
||||||
`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` +
|
`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` +
|
||||||
`${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` +
|
`${(metadata?.get("pdf:producer") || info.Producer || "-").trim()} / ` +
|
||||||
`(PDF.js: ${version || "?"} [${build || "?"}])`
|
`${(metadata?.get("xmp:creatortool") || info.Creator || "-").trim()}` +
|
||||||
|
`] (PDF.js: ${version || "?"} [${build || "?"}])`
|
||||||
);
|
);
|
||||||
let pdfTitle = info.Title;
|
const pdfTitle = this._docTitle;
|
||||||
|
|
||||||
const metadataTitle = metadata?.get("dc:title");
|
|
||||||
if (metadataTitle) {
|
|
||||||
// Ghostscript can produce invalid 'dc:title' Metadata entries:
|
|
||||||
// - The title may be "Untitled" (fixes bug 1031612).
|
|
||||||
// - The title may contain incorrectly encoded characters, which thus
|
|
||||||
// looks broken, hence we ignore the Metadata entry when it contains
|
|
||||||
// characters from the Specials Unicode block (fixes bug 1605526).
|
|
||||||
if (
|
|
||||||
metadataTitle !== "Untitled" &&
|
|
||||||
!/[\uFFF0-\uFFFF]/g.test(metadataTitle)
|
|
||||||
) {
|
|
||||||
pdfTitle = metadataTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pdfTitle) {
|
if (pdfTitle) {
|
||||||
this.setTitle(
|
this.setTitle(
|
||||||
`${pdfTitle} - ${this._contentDispositionFilename || this._title}`
|
`${pdfTitle} - ${this._contentDispositionFilename || this._title}`
|
||||||
|
|||||||
@ -67,13 +67,15 @@ class PDFDocumentProperties {
|
|||||||
overlayManager,
|
overlayManager,
|
||||||
eventBus,
|
eventBus,
|
||||||
l10n,
|
l10n,
|
||||||
fileNameLookup
|
fileNameLookup,
|
||||||
|
titleLookup
|
||||||
) {
|
) {
|
||||||
this.dialog = dialog;
|
this.dialog = dialog;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.overlayManager = overlayManager;
|
this.overlayManager = overlayManager;
|
||||||
this.l10n = l10n;
|
this.l10n = l10n;
|
||||||
this._fileNameLookup = fileNameLookup;
|
this._fileNameLookup = fileNameLookup;
|
||||||
|
this._titleLookup = titleLookup;
|
||||||
|
|
||||||
this.#reset();
|
this.#reset();
|
||||||
// Bind the event listener for the Close button.
|
// Bind the event listener for the Close button.
|
||||||
@ -113,7 +115,7 @@ class PDFDocumentProperties {
|
|||||||
|
|
||||||
// Get the document properties.
|
// Get the document properties.
|
||||||
const [
|
const [
|
||||||
{ info, /* metadata, contentDispositionFilename, */ contentLength },
|
{ info, metadata, /* contentDispositionFilename, */ contentLength },
|
||||||
pdfPage,
|
pdfPage,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.pdfDocument.getMetadata(),
|
this.pdfDocument.getMetadata(),
|
||||||
@ -123,6 +125,7 @@ class PDFDocumentProperties {
|
|||||||
const [
|
const [
|
||||||
fileName,
|
fileName,
|
||||||
fileSize,
|
fileSize,
|
||||||
|
title,
|
||||||
creationDate,
|
creationDate,
|
||||||
modificationDate,
|
modificationDate,
|
||||||
pageSize,
|
pageSize,
|
||||||
@ -130,8 +133,9 @@ class PDFDocumentProperties {
|
|||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this._fileNameLookup(),
|
this._fileNameLookup(),
|
||||||
this.#parseFileSize(contentLength),
|
this.#parseFileSize(contentLength),
|
||||||
this.#parseDate(info.CreationDate),
|
this._titleLookup(),
|
||||||
this.#parseDate(info.ModDate),
|
this.#parseDate(metadata?.get("xmp:createdate"), info.CreationDate),
|
||||||
|
this.#parseDate(metadata?.get("xmp:modifydate"), info.ModDate),
|
||||||
this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation),
|
this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation),
|
||||||
this.#parseLinearization(info.IsLinearized),
|
this.#parseLinearization(info.IsLinearized),
|
||||||
]);
|
]);
|
||||||
@ -139,14 +143,14 @@ class PDFDocumentProperties {
|
|||||||
this.#fieldData = Object.freeze({
|
this.#fieldData = Object.freeze({
|
||||||
fileName,
|
fileName,
|
||||||
fileSize,
|
fileSize,
|
||||||
title: info.Title,
|
title,
|
||||||
author: info.Author,
|
author: metadata?.get("dc:creator")?.join("\n") || info.Author,
|
||||||
subject: info.Subject,
|
subject: metadata?.get("dc:subject")?.join("\n") || info.Subject,
|
||||||
keywords: info.Keywords,
|
keywords: metadata?.get("pdf:keywords") || info.Keywords,
|
||||||
creationDate,
|
creationDate,
|
||||||
modificationDate,
|
modificationDate,
|
||||||
creator: info.Creator,
|
creator: metadata?.get("xmp:creatortool") || info.Creator,
|
||||||
producer: info.Producer,
|
producer: metadata?.get("pdf:producer") || info.Producer,
|
||||||
version: info.PDFFormatVersion,
|
version: info.PDFFormatVersion,
|
||||||
pageCount: this.pdfDocument.numPages,
|
pageCount: this.pdfDocument.numPages,
|
||||||
pageSize,
|
pageSize,
|
||||||
@ -324,8 +328,9 @@ class PDFDocumentProperties {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #parseDate(inputDate) {
|
async #parseDate(metadataDate, infoDate) {
|
||||||
const dateObj = PDFDateString.toDateObject(inputDate);
|
const dateObj =
|
||||||
|
Date.parse(metadataDate) || PDFDateString.toDateObject(infoDate);
|
||||||
return dateObj
|
return dateObj
|
||||||
? this.l10n.get("pdfjs-document-properties-date-time-string", {
|
? this.l10n.get("pdfjs-document-properties-date-time-string", {
|
||||||
dateObj: dateObj.valueOf(),
|
dateObj: dateObj.valueOf(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user