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

Merge N PDFs and Add Outline/Bookmarks with Custom Title #567

Closed
Resurg3nt opened this issue Aug 16, 2020 · 1 comment
Closed

Merge N PDFs and Add Outline/Bookmarks with Custom Title #567

Resurg3nt opened this issue Aug 16, 2020 · 1 comment

Comments

@Resurg3nt
Copy link

Resurg3nt commented Aug 16, 2020

I run pdf-lib in a node.js environment, together with minimist to pass arguments.

I pass a series of files to merge in using an --a parameter, and the corresponding bookmark/outline name for the first page of each of the PDFs that are being combined into the merged PDF, under the --ab parameter.

So I would call the pdf-lib from the command line as follows:-

node main.js --a 1.pdf --a 2.pdf --a 3.pdf --a 4.pdf --ab "Document 1" --ab "Document 2" --ab "Document 3" --ab "Document 4"

I have tried creating the outlines/bookmarks using the following code, but all that happens is that two outlines/bookmarks are added and they both are named Document 1. Can you let me know where I have gone wrong?

I took this code from a comment in Issue #127.

const { PDFDocument, PDFPageLeaf, PDFDict, PDFString, PDFArray, PDFName, PDFNull, PDFNumber, StandardFonts, rgb } = require('pdf-lib');
const fs = require('fs');
const parameters = require('minimist')(process.argv.slice(2));

var mergeFiles = parameters["a"];
var mergeFLength = mergeFiles.length;
var bookMarkDescs = parameters["ab"];
var buffers = [];
var bookmarkPages = [];
var outlineItemArr = [];
var outlineItemRefsArr = [];
var pageCount = 0;

const getPageRefs = (pdfDoc) => {
    const refs = [];
    pdfDoc.catalog.Pages().traverse((kid, ref) => {
    if (kid instanceof PDFPageLeaf) refs.push(ref);
    });
    return refs;
};

const createOutlineItem = (pdfDoc, title, parent, nextOrPrev, page, isLast = false) => {
    let array = PDFArray.withContext(pdfDoc.context);
    array.push(page);
    array.push(PDFName.of("XYZ"));
    array.push(PDFNull);
    array.push(PDFNull);
    array.push(PDFNull);
    const map = new Map();
    map.set(PDFName.Title, PDFString.of(title));
    map.set(PDFName.Parent, parent);
    map.set(PDFName.of(isLast ? "Prev" : "Next"), nextOrPrev);
    map.set(PDFName.of("Dest"), array);

    return PDFDict.fromMapWithContext(map, pdfDoc.context);
}

for (var i = 0; i < mergeFLength; i++){	
	var fileBuffer = fs.readFileSync(mergeFiles[i]);
	buffers.push(fileBuffer);
}

if(buffers.length != 0){
	var output_file = mergePDFDocuments(buffers);
}

async function mergePDFDocuments(documents) {
	const mergedPdf = await PDFDocument.create();

	for (let document of documents) {
		document = await PDFDocument.load(document);
		if(bookmarkPages.length == 0){
			bookmarkPages.push(0);
			pageCount = pageCount + document.getPageCount();
			bookmarkPages.push(pageCount);			
		} else {
			pageCount = pageCount + document.getPageCount();
			bookmarkPages.push(pageCount);
		}
		const copiedPages = await mergedPdf.copyPages(document, document.getPageIndices());
		copiedPages.forEach((page) => mergedPdf.addPage(page));    
	}
	bookmarkPages.pop();
	
	var references = getPageRefs(mergedPdf);	
	
	const outlinesDictRef = mergedPdf.context.nextRef(); 
	
	for (var y = 0; y < bookmarkPages.length; y++){	
		var pdf_reference = references[bookmarkPages[y]];
		var bookmark_title = bookMarkDescs[y];
		var outlineItemRef = mergedPdf.context.nextRef();
		outlineItemRefsArr.push(outlineItemRef);
		if(bookmarkPages[bookmarkPages.length - 1] === bookmarkPages[y]){
			console.log("last value");
			var outlineItem_last = createOutlineItem(mergedPdf, bookmark_title, outlinesDictRef, outlineItemRef, pdf_reference, true);	
			outlineItemArr.push(outlineItem_last);			
		} else {
			var outlineItem = createOutlineItem(mergedPdf, bookmark_title, outlinesDictRef, outlineItemRef, pdf_reference);		
			outlineItemArr.push(outlineItem);
		}
	}
	
	var outlinesDictMap = new Map();
	var countOfBookmarks = outlineItemArr.length;
	var firstBookmark = outlineItemArr[0];
	var lastBookmark = outlineItemArr[countOfBookmarks-1];
	
	outlinesDictMap.set(PDFName.Type, PDFName.of("Outlines"));
	outlinesDictMap.set(PDFName.of("First"), firstBookmark);
	outlinesDictMap.set(PDFName.of("Last"), lastBookmark);
	outlinesDictMap.set(PDFName.of("Count"), PDFNumber.of(countOfBookmarks));	
	
	for (var z = 0; z < bookmarkPages.length; z++){	
		mergedPdf.context.assign(outlineItemRefsArr[z], outlineItemArr[z]);
	}		
	
	console.log(outlinesDictMap);
	
	mergedPdf.catalog.set(PDFName.of("Outlines"),outlinesDictRef);
	
	const outlinesDict = PDFDict.fromMapWithContext(outlinesDictMap, mergedPdf.context);

	mergedPdf.context.assign(outlinesDictRef, outlinesDict);	

	fs.writeFileSync('output_merge.pdf', await mergedPdf.save());
}
@Hopding
Copy link
Owner

Hopding commented Sep 24, 2021

Added this to the roadmap for tracking: #998.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants