Skip to content

Commit b070a50

Browse files
Merge pull request #19929 from Snuffleupagus/bug-1966086
Prefer the /Metadata, when available, in the document properties dialog (bug 1966086)
2 parents d4d0081 + ab89773 commit b070a50

7 files changed

Lines changed: 148 additions & 36 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* Copyright 2024 Mozilla Foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
17+
18+
const FIELDS = [
19+
"fileName",
20+
"fileSize",
21+
"title",
22+
"author",
23+
"subject",
24+
"keywords",
25+
"creationDate",
26+
"modificationDate",
27+
"creator",
28+
"producer",
29+
"version",
30+
"pageCount",
31+
"pageSize",
32+
"linearized",
33+
];
34+
35+
describe("PDFDocumentProperties", () => {
36+
async function getFieldProperties(page) {
37+
const promises = [];
38+
39+
for (const name of FIELDS) {
40+
promises.push(
41+
page.evaluate(
42+
n => [n, document.getElementById(`${n}Field`).textContent],
43+
name
44+
)
45+
);
46+
}
47+
return Object.fromEntries(await Promise.all(promises));
48+
}
49+
50+
describe("Document with both /Info and /Metadata", () => {
51+
let pages;
52+
53+
beforeEach(async () => {
54+
pages = await loadAndWait("basicapi.pdf", ".textLayer .endOfContent");
55+
});
56+
57+
afterEach(async () => {
58+
await closePages(pages);
59+
});
60+
61+
it("must check that the document properties dialog has the correct information", async () => {
62+
await Promise.all(
63+
pages.map(async ([browserName, page]) => {
64+
await page.click("#secondaryToolbarToggleButton");
65+
await page.waitForSelector("#secondaryToolbar", { hidden: false });
66+
67+
await page.click("#documentProperties");
68+
await page.waitForSelector("#documentPropertiesDialog", {
69+
hidden: false,
70+
});
71+
72+
await page.waitForFunction(
73+
`document.getElementById("fileSizeField").textContent !== "-"`
74+
);
75+
const props = await getFieldProperties(page);
76+
77+
expect(props).toEqual({
78+
fileName: "basicapi.pdf",
79+
fileSize: `${FSI}103${PDI} KB (${FSI}105,779${PDI} bytes)`,
80+
title: "Basic API Test",
81+
author: "Brendan Dahl",
82+
subject: "-",
83+
keywords: "TCPDF",
84+
creationDate: "4/10/12, 7:30:26 AM",
85+
modificationDate: "4/10/12, 7:30:26 AM",
86+
creator: "TCPDF",
87+
producer: "TCPDF 5.9.133 (http://www.tcpdf.org)",
88+
version: "1.7",
89+
pageCount: "3",
90+
pageSize: `${FSI}8.27${PDI} × ${FSI}11.69${PDI} ${FSI}in${PDI} (${FSI}A4${PDI}, ${FSI}portrait${PDI})`,
91+
linearized: "No",
92+
});
93+
})
94+
);
95+
});
96+
});
97+
});

test/integration/find_spec.mjs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
import { closePages, loadAndWait } from "./test_utils.mjs";
16+
import { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
1717

1818
function fuzzyMatch(a, b, browserName, pixelFuzz = 3) {
1919
expect(a)
@@ -110,10 +110,6 @@ describe("find bar", () => {
110110
);
111111
const resultElement = await page.waitForSelector("#findResultsCount");
112112
const resultText = await resultElement.evaluate(el => el.textContent);
113-
/** Unicode bidi isolation characters. */
114-
const FSI = "\u2068";
115-
const PDI = "\u2069";
116-
// Fluent adds these markers to the result text.
117113
expect(resultText).toEqual(`${FSI}1${PDI} of ${FSI}1${PDI} match`);
118114
const selectedElement = await page.waitForSelector(
119115
".highlight.selected"

test/integration/jasmine-boot.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ async function runTests(results) {
3131
"autolinker_spec.mjs",
3232
"caret_browsing_spec.mjs",
3333
"copy_paste_spec.mjs",
34+
"document_properties_spec.mjs",
3435
"find_spec.mjs",
3536
"freetext_editor_spec.mjs",
3637
"highlight_editor_spec.mjs",

test/integration/signature_editor_spec.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import {
1717
awaitPromise,
1818
closePages,
1919
copy,
20+
FSI,
2021
getEditorSelector,
2122
getRect,
2223
loadAndWait,
2324
paste,
25+
PDI,
2426
switchToEditor,
2527
waitForPointerUp,
2628
waitForTimeout,
@@ -188,7 +190,7 @@ describe("Signature Editor", () => {
188190

189191
// Check the aria description.
190192
await page.waitForSelector(
191-
`${editorSelector}[aria-description="Signature editor: \u2068Hello World\u2069"]`
193+
`${editorSelector}[aria-description="Signature editor: ${FSI}Hello World${PDI}"]`
192194
);
193195

194196
// Edit the description.

test/integration/test_utils.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,10 @@ async function moveEditor(page, selector, n, pressKey) {
882882
}
883883
}
884884

885+
// Unicode bidi isolation characters, Fluent adds these markers to the text.
886+
const FSI = "\u2068";
887+
const PDI = "\u2069";
888+
885889
export {
886890
applyFunctionToEditor,
887891
awaitPromise,
@@ -894,6 +898,7 @@ export {
894898
createPromise,
895899
dragAndDrop,
896900
firstPageOnTop,
901+
FSI,
897902
getAnnotationSelector,
898903
getAnnotationStorage,
899904
getComputedStyleSelector,
@@ -929,6 +934,7 @@ export {
929934
moveEditor,
930935
paste,
931936
pasteFromClipboard,
937+
PDI,
932938
scrollIntoView,
933939
selectEditor,
934940
selectEditors,

web/app.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ const PDFViewerApplication = {
598598
overlayManager,
599599
eventBus,
600600
l10n,
601-
/* fileNameLookup = */ () => this._docFilename
601+
/* fileNameLookup = */ () => this._docFilename,
602+
/* titleLookup = */ () => this._docTitle
602603
);
603604
}
604605

@@ -1003,6 +1004,23 @@ const PDFViewerApplication = {
10031004
return this._contentDispositionFilename || getPdfFilenameFromUrl(this.url);
10041005
},
10051006

1007+
get _docTitle() {
1008+
const { documentInfo, metadata } = this;
1009+
1010+
const title = metadata?.get("dc:title");
1011+
if (title) {
1012+
// Ghostscript can produce invalid 'dc:title' Metadata entries:
1013+
// - The title may be "Untitled" (fixes bug 1031612).
1014+
// - The title may contain incorrectly encoded characters, which thus
1015+
// looks broken, hence we ignore the Metadata entry when it contains
1016+
// characters from the Specials Unicode block (fixes bug 1605526).
1017+
if (title !== "Untitled" && !/[\uFFF0-\uFFFF]/g.test(title)) {
1018+
return title;
1019+
}
1020+
}
1021+
return documentInfo.Title;
1022+
},
1023+
10061024
/**
10071025
* @private
10081026
*/
@@ -1621,25 +1639,12 @@ const PDFViewerApplication = {
16211639
// Provides some basic debug information
16221640
console.log(
16231641
`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` +
1624-
`${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` +
1625-
`(PDF.js: ${version || "?"} [${build || "?"}])`
1642+
`${(metadata?.get("pdf:producer") || info.Producer || "-").trim()} / ` +
1643+
`${(metadata?.get("xmp:creatortool") || info.Creator || "-").trim()}` +
1644+
`] (PDF.js: ${version || "?"} [${build || "?"}])`
16261645
);
1627-
let pdfTitle = info.Title;
1646+
const pdfTitle = this._docTitle;
16281647

1629-
const metadataTitle = metadata?.get("dc:title");
1630-
if (metadataTitle) {
1631-
// Ghostscript can produce invalid 'dc:title' Metadata entries:
1632-
// - The title may be "Untitled" (fixes bug 1031612).
1633-
// - The title may contain incorrectly encoded characters, which thus
1634-
// looks broken, hence we ignore the Metadata entry when it contains
1635-
// characters from the Specials Unicode block (fixes bug 1605526).
1636-
if (
1637-
metadataTitle !== "Untitled" &&
1638-
!/[\uFFF0-\uFFFF]/g.test(metadataTitle)
1639-
) {
1640-
pdfTitle = metadataTitle;
1641-
}
1642-
}
16431648
if (pdfTitle) {
16441649
this.setTitle(
16451650
`${pdfTitle} - ${this._contentDispositionFilename || this._title}`

web/pdf_document_properties.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ class PDFDocumentProperties {
6767
overlayManager,
6868
eventBus,
6969
l10n,
70-
fileNameLookup
70+
fileNameLookup,
71+
titleLookup
7172
) {
7273
this.dialog = dialog;
7374
this.fields = fields;
7475
this.overlayManager = overlayManager;
7576
this.l10n = l10n;
7677
this._fileNameLookup = fileNameLookup;
78+
this._titleLookup = titleLookup;
7779

7880
this.#reset();
7981
// Bind the event listener for the Close button.
@@ -113,7 +115,7 @@ class PDFDocumentProperties {
113115

114116
// Get the document properties.
115117
const [
116-
{ info, /* metadata, contentDispositionFilename, */ contentLength },
118+
{ info, metadata, /* contentDispositionFilename, */ contentLength },
117119
pdfPage,
118120
] = await Promise.all([
119121
this.pdfDocument.getMetadata(),
@@ -123,30 +125,32 @@ class PDFDocumentProperties {
123125
const [
124126
fileName,
125127
fileSize,
128+
title,
126129
creationDate,
127130
modificationDate,
128131
pageSize,
129132
isLinearized,
130133
] = await Promise.all([
131134
this._fileNameLookup(),
132135
this.#parseFileSize(contentLength),
133-
this.#parseDate(info.CreationDate),
134-
this.#parseDate(info.ModDate),
136+
this._titleLookup(),
137+
this.#parseDate(metadata?.get("xmp:createdate"), info.CreationDate),
138+
this.#parseDate(metadata?.get("xmp:modifydate"), info.ModDate),
135139
this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation),
136140
this.#parseLinearization(info.IsLinearized),
137141
]);
138142

139143
this.#fieldData = Object.freeze({
140144
fileName,
141145
fileSize,
142-
title: info.Title,
143-
author: info.Author,
144-
subject: info.Subject,
145-
keywords: info.Keywords,
146+
title,
147+
author: metadata?.get("dc:creator")?.join("\n") || info.Author,
148+
subject: metadata?.get("dc:subject")?.join("\n") || info.Subject,
149+
keywords: metadata?.get("pdf:keywords") || info.Keywords,
146150
creationDate,
147151
modificationDate,
148-
creator: info.Creator,
149-
producer: info.Producer,
152+
creator: metadata?.get("xmp:creatortool") || info.Creator,
153+
producer: metadata?.get("pdf:producer") || info.Producer,
150154
version: info.PDFFormatVersion,
151155
pageCount: this.pdfDocument.numPages,
152156
pageSize,
@@ -324,8 +328,9 @@ class PDFDocumentProperties {
324328
);
325329
}
326330

327-
async #parseDate(inputDate) {
328-
const dateObj = PDFDateString.toDateObject(inputDate);
331+
async #parseDate(metadataDate, infoDate) {
332+
const dateObj =
333+
Date.parse(metadataDate) || PDFDateString.toDateObject(infoDate);
329334
return dateObj
330335
? this.l10n.get("pdfjs-document-properties-date-time-string", {
331336
dateObj: dateObj.valueOf(),

0 commit comments

Comments
 (0)