Skip to content

Commit 76bd1f5

Browse files
authored
Copy pages across PDFs (#52)
* Working implementation of page copying * Protect against cyclic traversals when copying in pages * Add PDFObjectCopier class * PDFObjectIndex.set -> PDFObjectIndex.assign * Fix compilation errors * WIP: Clone objects when copying from another PDF * WIP: Working implementation of cloned page copying * Cleanup * Implement clone() on all PDFObjects * Cleanup * Preserve inherited entries during page copy * Add integration test for page copying * Add PDFObjectCopier unit tests * Add more PDFObjectCopier unit tests * Add more PDFObjectCopier unit tests * Add more PDFObjectCopier unit tests * Fix tests * Add more PDFObjectCopier unit tests * Add PDFObject.clone() unit tests * Cleanup * Update docs * Add PDFPage.clone() unit test * Update README * Cleanup
1 parent b71e9d8 commit 76bd1f5

File tree

73 files changed

+1943
-419
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1943
-419
lines changed

README.md

+214-187
Large diffs are not rendered by default.

__integration_tests__/tests/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export { default as test4 } from './test4';
55
export { default as test5 } from './test5';
66
export { default as test6 } from './test6';
77
export { default as test7 } from './test7';
8+
export { default as test8 } from './test8';

__integration_tests__/tests/test8.ts

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import round from 'lodash/round';
2+
import sum from 'lodash/sum';
3+
4+
import {
5+
drawText,
6+
PDFDocument,
7+
PDFDocumentFactory,
8+
PDFDocumentWriter,
9+
PDFNumber,
10+
} from '../../src';
11+
12+
import { ITestAssets, ITestKernel } from '../models';
13+
14+
const createDonorPdf = () => {
15+
const pdfDoc = PDFDocumentFactory.create();
16+
const [FontHelvetica] = pdfDoc.embedStandardFont('Helvetica');
17+
18+
const contentStream = pdfDoc.register(
19+
pdfDoc.createContentStream(
20+
drawText(`I am upside down!`, {
21+
font: 'Helvetica',
22+
x: 50,
23+
y: 225,
24+
size: 50,
25+
}),
26+
),
27+
);
28+
const page = pdfDoc
29+
.createPage([500, 500])
30+
.addFontDictionary('Helvetica', FontHelvetica)
31+
.addContentStreams(contentStream);
32+
pdfDoc.addPage(page);
33+
34+
pdfDoc.catalog.Pages.set('Rotate', PDFNumber.fromNumber(180));
35+
36+
return pdfDoc;
37+
};
38+
39+
const kernel: ITestKernel = (assets: ITestAssets) => {
40+
const pdfDoc = PDFDocumentFactory.load(
41+
assets.pdfs.with_missing_endstream_eol_and_polluted_ctm,
42+
);
43+
44+
const allDonorPdfBytes: Uint8Array[] = [
45+
assets.pdfs.normal,
46+
assets.pdfs.with_update_sections,
47+
assets.pdfs.linearized_with_object_streams,
48+
assets.pdfs.with_large_page_count,
49+
];
50+
51+
allDonorPdfBytes.forEach((donorBytes) => {
52+
const donorPdf = PDFDocumentFactory.load(donorBytes);
53+
pdfDoc.addPage(donorPdf.getPages()[0]);
54+
});
55+
56+
const anotherDonorPdf = createDonorPdf();
57+
pdfDoc.insertPage(1, anotherDonorPdf.getPages()[0]);
58+
59+
const savedBytes = PDFDocumentWriter.saveToBytes(pdfDoc);
60+
61+
const sizeOfAllDonorPdfs = sum(
62+
allDonorPdfBytes
63+
.concat(PDFDocumentWriter.saveToBytes(anotherDonorPdf))
64+
.map((bytes) => bytes.length),
65+
);
66+
const sizeOfCreatedPdf = savedBytes.length;
67+
68+
console.log();
69+
console.log(
70+
'Since pdf-lib only copies the minimum necessary resources from a donor PDF needed to show a copied page, the size of the PDF we create from copied pages should be smaller than the size of all the donor PDFs added together:',
71+
);
72+
console.log();
73+
console.log(
74+
' sizeOfRecipientPdf / sizeOfAllDonorPdfs = ',
75+
round(sizeOfCreatedPdf / sizeOfAllDonorPdfs, 2),
76+
);
77+
78+
return savedBytes;
79+
};
80+
81+
export default {
82+
kernel,
83+
title: 'Page Copying Test',
84+
description:
85+
'This tests that pages can be copied from donor PDFs into a receipient PDF.',
86+
checklist: [
87+
'the document contains 6 pages.',
88+
'the first page is a refund receipt.',
89+
'the second page is an inverted white square containing the text "I am upside down!".',
90+
'the third page is a D-2210 income tax form.',
91+
'the fourth page is a 2013, 1040-V tax form.',
92+
'the fifth page is a 2013, 1040 tax form title sheet.',
93+
'the sixth page is the title sheet of the PDF 1.7 specification.',
94+
],
95+
};

0 commit comments

Comments
 (0)