mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-25 16:37:22 +02:00
Merge pull request #21566 from calixteman/fix/pdf-editor-unicode-name-trees
Preserve Unicode destination and attachment names
This commit is contained in:
commit
b19cc7c746
@ -2652,7 +2652,15 @@ class PDFEditor {
|
|||||||
#makeNameNumTree(map, areNames) {
|
#makeNameNumTree(map, areNames) {
|
||||||
const allEntries = map.sort(
|
const allEntries = map.sort(
|
||||||
areNames
|
areNames
|
||||||
? ([keyA], [keyB]) => keyA.localeCompare(keyB)
|
? ([keyA], [keyB]) => {
|
||||||
|
if (keyA < keyB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (keyA > keyB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
: ([keyA], [keyB]) => keyA - keyB
|
: ([keyA], [keyB]) => keyA - keyB
|
||||||
);
|
);
|
||||||
const maxLeaves =
|
const maxLeaves =
|
||||||
@ -2729,7 +2737,7 @@ class PDFEditor {
|
|||||||
/* keepEscapeSequence = */ true
|
/* keepEscapeSequence = */ true
|
||||||
);
|
);
|
||||||
for (let i = 1; ; i++) {
|
for (let i = 1; ; i++) {
|
||||||
const deduped = `${displayName}_${i}`;
|
const deduped = stringToAsciiOrUTF16BE(`${displayName}_${i}`);
|
||||||
if (!embeddedFiles.has(deduped)) {
|
if (!embeddedFiles.has(deduped)) {
|
||||||
name = deduped;
|
name = deduped;
|
||||||
break;
|
break;
|
||||||
@ -2775,7 +2783,10 @@ class PDFEditor {
|
|||||||
this.namesDict.set(
|
this.namesDict.set(
|
||||||
"Dests",
|
"Dests",
|
||||||
this.#makeNameNumTree(
|
this.#makeNameNumTree(
|
||||||
Array.from(namedDestinations.entries()),
|
Array.from(namedDestinations, ([name, dest]) => [
|
||||||
|
stringToAsciiOrUTF16BE(name),
|
||||||
|
dest,
|
||||||
|
]),
|
||||||
/* areNames = */ true
|
/* areNames = */ true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -103,25 +103,7 @@ describe("api", function () {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSharedImageResourcePdf() {
|
function assemblePdf(objects) {
|
||||||
const streamObject = (num, dict, data) =>
|
|
||||||
`${num} 0 obj\n<< ${dict} /Length ${data.length} >>\n` +
|
|
||||||
`stream\n${data}\nendstream\nendobj\n`;
|
|
||||||
const objects = [
|
|
||||||
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n",
|
|
||||||
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
|
|
||||||
"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 10 10] " +
|
|
||||||
"/Resources << /XObject << /Im0 4 0 R /Im1 4 0 R >> >> " +
|
|
||||||
"/Contents 5 0 R >>\nendobj\n",
|
|
||||||
streamObject(
|
|
||||||
4,
|
|
||||||
"/Type /XObject /Subtype /Image /Width 1 /Height 1 " +
|
|
||||||
"/ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /ASCIIHexDecode",
|
|
||||||
"FF0000>"
|
|
||||||
),
|
|
||||||
streamObject(5, "", "q 10 0 0 10 0 0 cm /Im0 Do Q"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let pdf = "%PDF-1.7\n";
|
let pdf = "%PDF-1.7\n";
|
||||||
const offsets = [];
|
const offsets = [];
|
||||||
for (const obj of objects) {
|
for (const obj of objects) {
|
||||||
@ -140,6 +122,26 @@ describe("api", function () {
|
|||||||
return stringToBytes(pdf);
|
return stringToBytes(pdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildSharedImageResourcePdf() {
|
||||||
|
const streamObject = (num, dict, data) =>
|
||||||
|
`${num} 0 obj\n<< ${dict} /Length ${data.length} >>\n` +
|
||||||
|
`stream\n${data}\nendstream\nendobj\n`;
|
||||||
|
return assemblePdf([
|
||||||
|
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n",
|
||||||
|
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
|
||||||
|
"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 10 10] " +
|
||||||
|
"/Resources << /XObject << /Im0 4 0 R /Im1 4 0 R >> >> " +
|
||||||
|
"/Contents 5 0 R >>\nendobj\n",
|
||||||
|
streamObject(
|
||||||
|
4,
|
||||||
|
"/Type /XObject /Subtype /Image /Width 1 /Height 1 " +
|
||||||
|
"/ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /ASCIIHexDecode",
|
||||||
|
"FF0000>"
|
||||||
|
),
|
||||||
|
streamObject(5, "", "q 10 0 0 10 0 0 cm /Im0 Do Q"),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
function getNamedNodeInXML(node, path) {
|
function getNamedNodeInXML(node, path) {
|
||||||
for (const component of path.split(".")) {
|
for (const component of path.split(".")) {
|
||||||
if (!node.childNodes) {
|
if (!node.childNodes) {
|
||||||
@ -6404,23 +6406,7 @@ small scripts as well as for`);
|
|||||||
"5 0 obj\n<< /Type /Annot /Subtype /Link /Rect [10 0 20 10] " +
|
"5 0 obj\n<< /Type /Annot /Subtype /Link /Rect [10 0 20 10] " +
|
||||||
"/A << /S /GoToR /F (other.pdf) /D (target) >> >>\nendobj\n",
|
"/A << /S /GoToR /F (other.pdf) /D (target) >> >>\nendobj\n",
|
||||||
];
|
];
|
||||||
let pdfData = "%PDF-1.7\n";
|
let loadingTask = getDocument({ data: assemblePdf(objects) });
|
||||||
const offsets = [];
|
|
||||||
for (const object of objects) {
|
|
||||||
offsets.push(pdfData.length);
|
|
||||||
pdfData += object;
|
|
||||||
}
|
|
||||||
const xrefOffset = pdfData.length;
|
|
||||||
pdfData += `xref\n0 ${objects.length + 1}\n`;
|
|
||||||
pdfData += "0000000000 65535 f \n";
|
|
||||||
for (const offset of offsets) {
|
|
||||||
pdfData += `${offset.toString().padStart(10, "0")} 00000 n \n`;
|
|
||||||
}
|
|
||||||
pdfData +=
|
|
||||||
`trailer\n<< /Size ${objects.length + 1} /Root 1 0 R >>\n` +
|
|
||||||
`startxref\n${xrefOffset}\n%%EOF\n`;
|
|
||||||
|
|
||||||
let loadingTask = getDocument({ data: stringToBytes(pdfData) });
|
|
||||||
let pdfDoc = await loadingTask.promise;
|
let pdfDoc = await loadingTask.promise;
|
||||||
const data = await pdfDoc.extractPages([{ document: null }]);
|
const data = await pdfDoc.extractPages([{ document: null }]);
|
||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
@ -6435,6 +6421,33 @@ small scripts as well as for`);
|
|||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves Unicode destination names", async function () {
|
||||||
|
const objects = [
|
||||||
|
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R " +
|
||||||
|
"/Names << /Dests 4 0 R >> >>\nendobj\n",
|
||||||
|
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
|
||||||
|
"3 0 obj\n<< /Type /Page /Parent 2 0 R " +
|
||||||
|
"/MediaBox [0 0 100 100] /Annots [5 0 R] >>\nendobj\n",
|
||||||
|
"4 0 obj\n<< /Names [<FEFF540D> [3 0 R /Fit]] >>\nendobj\n",
|
||||||
|
"5 0 obj\n<< /Type /Annot /Subtype /Link /Rect [0 0 10 10] " +
|
||||||
|
"/Dest <FEFF540D> >>\nendobj\n",
|
||||||
|
];
|
||||||
|
let loadingTask = getDocument({ data: assemblePdf(objects) });
|
||||||
|
let pdfDoc = await loadingTask.promise;
|
||||||
|
expect(Object.keys(await pdfDoc.getDestinations()))
|
||||||
|
.withContext("before extraction")
|
||||||
|
.toEqual(["名"]);
|
||||||
|
const data = await pdfDoc.extractPages([{ document: null }]);
|
||||||
|
await loadingTask.destroy();
|
||||||
|
|
||||||
|
loadingTask = getDocument({ data });
|
||||||
|
pdfDoc = await loadingTask.promise;
|
||||||
|
expect(Object.keys(await pdfDoc.getDestinations()))
|
||||||
|
.withContext("after extraction")
|
||||||
|
.toEqual(["名"]);
|
||||||
|
await loadingTask.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps colliding deduplicated destination names unique", async function () {
|
it("keeps colliding deduplicated destination names unique", async function () {
|
||||||
let loadingTask = getDocument(
|
let loadingTask = getDocument(
|
||||||
buildGetDocumentParams("named_dest_collision_for_editor.pdf")
|
buildGetDocumentParams("named_dest_collision_for_editor.pdf")
|
||||||
@ -7248,6 +7261,31 @@ small scripts as well as for`);
|
|||||||
|
|
||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves Unicode EmbeddedFiles (attachments) names when deduplicating", async function () {
|
||||||
|
const objects = [
|
||||||
|
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R " +
|
||||||
|
"/Names << /EmbeddedFiles 4 0 R >> >>\nendobj\n",
|
||||||
|
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
|
||||||
|
"3 0 obj\n<< /Type /Page /Parent 2 0 R " +
|
||||||
|
"/MediaBox [0 0 100 100] >>\nendobj\n",
|
||||||
|
"4 0 obj\n<< /Names [<FEFF540D> 5 0 R] >>\nendobj\n",
|
||||||
|
"5 0 obj\n<< /Type /Filespec /F (file) /UF <FEFF540D> >>\nendobj\n",
|
||||||
|
];
|
||||||
|
let loadingTask = getDocument({ data: assemblePdf(objects) });
|
||||||
|
let pdfDoc = await loadingTask.promise;
|
||||||
|
const data = await pdfDoc.extractPages([
|
||||||
|
{ document: null },
|
||||||
|
{ document: null },
|
||||||
|
]);
|
||||||
|
await loadingTask.destroy();
|
||||||
|
|
||||||
|
loadingTask = getDocument({ data });
|
||||||
|
pdfDoc = await loadingTask.promise;
|
||||||
|
const attachments = await pdfDoc.getAttachments();
|
||||||
|
expect(Array.from(attachments.keys()).sort()).toEqual(["名", "名_1"]);
|
||||||
|
await loadingTask.destroy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("AcroForm", function () {
|
describe("AcroForm", function () {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user