Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add insert and preserve links example #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions insert-pages-and-preserve-links/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
output.pdf
3 changes: 3 additions & 0 deletions insert-pages-and-preserve-links/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Example of inserting one PDF into another, preserving the links in the first PDF.

See [this issue](https://github.com/galkahana/HummusJS/issues/437) for more explanation.
8 changes: 8 additions & 0 deletions insert-pages-and-preserve-links/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const inserter = require('./inserter');

inserter.insert(
'./insert.pdf',
'./pdf-with-links.pdf',
2,
'./output.pdf'
);
Binary file added insert-pages-and-preserve-links/insert.pdf
Binary file not shown.
137 changes: 137 additions & 0 deletions insert-pages-and-preserve-links/inserter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const fs = require('fs');
const hummus = require('hummus');

function insert(insertPDFPath, mainPDFPath, insertPageNumber, savePath){
if (!fs.existsSync(insertPDFPath)){
throw `No file exists at insertPDFPath ${insertPDFPath}.`;
}
if (!fs.existsSync(mainPDFPath)){
throw `No file exists at mainPDFPath ${mainPDFPath}.`;
}

let insertPage = Number.parseInt(insertPageNumber);
if (isNaN(insertPage) || insertPage < 1){
throw `insertPageNumber was ${insertPageNumber}; it must be a positive integer.`;
}

let mainReader = hummus.createReader(mainPDFPath);
let insertPDFReader = hummus.createReader(insertPDFPath);
let insertPDFLength = insertPDFReader.getPagesCount();
let outputWriter = hummus.createWriter(savePath);

//First pages main.pdf, up to insert point
appendPDFPageFromPDFWithAnnotations(outputWriter, mainPDFPath, 0, insertPage - 1);

//insert.pdf
outputWriter.appendPDFPagesFromPDF(insertPDFPath);

//rest of main.pdf
appendPDFPageFromPDFWithAnnotations(outputWriter, mainPDFPath, insertPage - 1, mainReader.getPagesCount());

outputWriter.end();
console.log(`${insertPDFPath} inserted.`);

updateLinkDestinations(savePath, insertPDFLength);
console.log(`Links updated.`);
console.log(`Saved output to ${savePath}`);
}

//see https://github.com/galkahana/HummusJSSamples/blob/master/appending-pages-with-comments/appendWithComments.js
function appendPDFPageFromPDFWithAnnotations(pdfWriter,sourcePDFPath, startPage, endPage) {
let cpyCxt = pdfWriter.createPDFCopyingContext(sourcePDFPath);
let cpyCxtParser = cpyCxt.getSourceDocumentParser();

for (let i=startPage; i < endPage; ++i) {
let pageDictionary = cpyCxtParser.parsePageDictionary(i);
if(!pageDictionary.exists('Annots')) {
cpyCxt.appendPDFPageFromPDF(i);
}
else {
let reffedObjects;
pdfWriter.getEvents().once('OnPageWrite',function(params) {
params.pageDictionaryContext.writeKey('Annots');
reffedObjects = cpyCxt.copyDirectObjectWithDeepCopy(pageDictionary.queryObject('Annots'))
})
cpyCxt.appendPDFPageFromPDF(i);
if(reffedObjects && reffedObjects.length > 0)
cpyCxt.copyNewObjectsForDirectObject(reffedObjects)
}
}
}

function updateLinkDestinations(savePath, insertPDFLength) {
let writer = hummus.createWriterToModify(savePath);
let reader = writer.getModifiedFileParser(savePath);
let copyingContext = writer.createPDFCopyingContextForModifiedFile();
let pageIDs = getPageIDs(reader);

for (let i = 0; i < reader.getPagesCount(); i++) {
let pageDictionary = reader.parsePageDictionary(i);
if (pageDictionary.exists("Annots")) {
let parsedPageDictionary = reader.parsePageDictionary(i);
let annots = reader.queryDictionaryObject(parsedPageDictionary, "Annots");

for (let j = 0; j < annots.getLength(); j++) {
let annotationIndirectReference = annots.queryObject(j);
let annotation = reader.queryArrayObject(annots, j);
let annotationObject = annotation.toJSObject();
let destPDFArray = reader.queryDictionaryObject(annotation, "Dest");
console.log(destPDFArray);
let destArrayObject = destPDFArray.toJSArray();
let oldDestPageID = destArrayObject[0].getObjectID();
let oldDestPageIndex = getOldPageIDIndexInOldPDF(reader, oldDestPageID);
let newDestPageID = pageIDs[oldDestPageIndex + insertPDFLength];

let objectContext = writer.getObjectsContext();
objectContext.startModifiedIndirectObject(
annotationIndirectReference.getObjectID()
);
let modifiedAnnotation = writer.getObjectsContext().startDictionary();

//copy all keys except Dest to the modified annotation
Object.getOwnPropertyNames(annotationObject).forEach(
(element, index, array) => {
if (element != "Dest") {
modifiedAnnotation.writeKey(element);
copyingContext.copyDirectObjectAsIs(annotationObject[element]);
}
}
);

//Add the Dest key and make it an array with the first element being the new target page
modifiedAnnotation.writeKey("Dest");
objectContext.startArray().writeIndirectObjectReference(newDestPageID);

//copy other elements of the old Dest array
for (let k = 1; k < destArrayObject.length; k++) {
copyingContext.copyDirectObjectAsIs(destArrayObject[k]);
}

objectContext
.endArray()
.endLine()
.endDictionary(modifiedAnnotation)
.endIndirectObject();
}
}
}

writer.end();
}

function getPageIDs(reader){
let IDs = [];
for (let i = 0; i < reader.getPagesCount(); i++){
IDs.push(reader.getPageObjectID(i));
}
return IDs;
}

function getOldPageIDIndexInOldPDF(reader, oldPageID){
let oldPageDict = reader.parseNewObject(oldPageID).toPDFDictionary();
let parent = reader.queryDictionaryObject(oldPageDict, 'Parent').toJSObject();
let oldPageIDs = parent.Kids.toJSArray().map(e => e.getObjectID());
return oldPageIDs.indexOf(oldPageID);
}

module.exports = { insert };
Loading