Pdf-lib: Feeding Pdfkit generated file to pdf-lib.

Created on 7 Aug 2020  Â·  17Comments  Â·  Source: Hopding/pdf-lib

I am trying to send a pdfkit generated pdf file as input to pdflib for merging. I am using async function. My project is being developed using sails Js version:"^1.2.3", "node": "^12.16", my pdf-kit version is: "^0.11.0", "pdf-lib": "^1.9.0",
This is the code:

const  textbytes=fs.readFileSync(textfile);
var bytes1 = new Uint8Array(textbytes);
 const textdoc = await PDFDocument.load(bytes1)

The error i am getting is:

UnhandledPromiseRejectionWarning: Error: Failed to parse PDF document (line:0 col:0 offset=0): No PDF header found

Please help me with this issue.

Most helpful comment

@Patcher56 Looks like a very interesting project! I suspect that in many places the protected access modifier could be used instead of the private one. I don't want to replace all usages of private, and relatively few instances should be changed to public. If something is marked as private then it is not meant to be public interfaces and is subject to breaking changes. If you'd like to open a PR changing the things you need to protected, I'd be happy to take a look.

All 17 comments

I also use pdfkit and pdf-lib at the same time, but I can embed the pdf files generated by pdfkit.
I use embedPdf instead of PDFDocument.load.

const PDFDocument = require('pdf-lib');
const pdfDoc = await PDFDocument.create();
const [ sourcePage ] = await pdfDoc.embedPdf(fs.readFileSync(textfile));

I think the problem may be that the pdf generated by pdfkit is not saved completely, you can try to ensure that the pdf file has been written before load by pdf-lib.

const PDFDocument = require('pdfkit');
const doc = new PDFDocument;
doc.pipe(fs.createWriteStream('/path/to/file.pdf'));

// draw something...

(async () => {
  await new Promise(resolve => {
    // resolve after WriteStream is finished, 
    doc.stream.on('finish', resolve);
    // finalize the PDF and end the stream
    doc.end();
  });

  // open the file after here
})();

Hi.. Thanks for the help. But i am getting an error.. Kindly check. I want to merge pdf generated with pdfkit to 2 more pdfs(edited using pdf-lib). So i have a function for merging.

function fullreport(){
 let doc = new PDFkitDocument({ size: "A4", margin: 50 });
  doc.pipe(fs.createWriteStream('./test1.pdf'));
  // writing to doc
  doc.text("--------------------End of Report--------------------",50,doc.y,{align:'center'});

(async () => {
  await new Promise(resolve => {
    // resolve after WriteStream is finished, 
    doc.stream.on('finish', resolve);
    // finalize the PDF and end the stream
    doc.end();
  });
  mergepdf('./test1.pdf',dashboard[0].dashboard[0].loglocation,dashboard[0].dashboard[0].changelocation)
})();
}

This is my mergefunction:

async function mergepdf (textfile,loglocation,changelocation){
  const docmerge = await PDFDocument.create();
  var change='./assets/images/change/'+changelocation;
   var log='./assets/images/logs/'+loglocation;
  const  existingchangeBytes1=fs.readFileSync(change)
  const  existinglogBytes1=fs.readFileSync(log)
  const  [textbytes]=await docmerge.embedPdf(fs.readFileSync(textfile))
  const changeDoc = await PDFDocument.load(existingchangeBytes1)
  const logDoc = await PDFDocument.load(existinglogBytes1)

  const part1pgs = await docmerge.copyPages(textbytes,textbytes.getPageIndices());
  for(const part1pg of part1pgs){
    docmerge.addPage(part1pg)
  }
  const part2pgs = await docmerge.copyPages(logDoc,logDoc.getPageIndices());
  for(const part2pg of part2pgs){
    docmerge.addPage(part2pg)
      }
  const part3pgs = await docmerge.copyPages(changeDoc,changeDoc.getPageIndices());
  for(const part3pg of part3pgs){
    docmerge.addPage(part3pg)
      }
      fs.writeFileSync('./test.pdf', await docmerge.save());

}

The error i am getting is here:

Promise not resolved
(node:14928) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'on' of undefined.

I am new to JS and node. I am using sails Js. Please help me.

I think this problem is the write stream created by fs (doc.stream) cannot be found, can you please paste the pdfkit code for creating the pdf file here?

Or you can try to set the finish listener to the write stream before doc.end().

function fullreport(dashboard,mname,rname)
{ var d=0;
  let doc = new PDFkitDocument({ size: "A4", margin: 50 });
  //doc.pipe(fs.createWriteStream('./output.pdf'));
  for(d=0;d<dashboard.length;d++){
    if(d>0)doc.addPage();
    doc.fontSize(20) .font('Times-Bold')
   .text("Monthly Status Of Cyber Security", { align: "center" })
   .moveDown()
   .text(rname,{align: "center" })
   .moveDown()
   doc .fontSize(12)
   .text("Date: "+dashboard[d].dashboard[0].date,{ align: "right" })
   .text("Month: "+mname,50,doc.y,{ align: "left" })
    doc.moveDown()  
   doc.fontSize(13).font('Times-Bold')
   .text("Type of Network: " + dashboard[d].dashboard[0].type, { align: "left" })
   doc.moveDown()
   .font('Times-Bold')
   .text("1.UTM/Firewall Details:",{underline:true})
   .font('Times-Bold')
   .moveDown()  
   if(dashboard[d].fw[0].fw) dashboard[d].fw[0].fw='Yes';
   else dashboard[d].fw[0].fw-'No'
  doc.text("a.Availability of Internet Gateway Security (UTM/Firewall): ") 
  .font('Times-Roman').text(dashboard[d].fw[0].fw)
  doc.moveDown(); doc.fontSize(12)
  doc.text("License Updated Till: "+dashboard[d].fw[0].fwvalidity)
  .moveDown()
  .font('Times-Bold')
  .text("b.Monitoring of UTM/Firewall logs:")
  .moveDown()
  .font('Times-Roman')
  .text("i.Periodicity of Monitoring of logs: "+dashboard[d].fw[0].period,{align:'justify'})
  .moveDown()
  .text("ii.Responsibility/Ownership: "+dashboard[d].fw[0].adminname,{align:'justify'})
  .moveDown()
  .text("iii.Vulnerability/threats identified during month:  "+dashboard[d].fw[0].fwvuln,{align:'justify'})
  .moveDown()
  .text("iv.Action taken on above point ‘c’:  "+dashboard[d].fw[0].fwact,{align:'justify'})
  .moveDown()
  .text("v.Any change in configuration/rules in gateway device, if yes, reason thereof:  "+dashboard[d].fw[0].fwconf,{align:'justify'})
  .moveDown()
  .text("vi.VPN access provided and availed during the month:  "+dashboard[d].fw[0].vpn,{align:'justify'})
  .moveDown()
  doc.font('Times-Bold');doc.fontSize(13)
doc.text("2.Firewall Signature Updation:",50,doc.y,{underline:true})
doc.moveDown()  
const header=65;
if(doc.y+header+(dashboard[d].patch.length)*42 > 800){doc.addPage()}
var vr=doc.y;
generateHr(doc,45,doc.y,555,doc.y)
var h=doc.y+10;
doc.fontSize(12).font('Times-Bold')
.text("Name of the Patch",50,h,{width:180})
.text("No of firewall where signature needs to be Updated ",250,h, {width:130}) 
.text("Number of firewalls on which Signature Updated",400,h,{width:150})
var a=doc.y+15;
generateHr(doc,45,a,555,a)
for(i=0;i<dashboard[d].patch.length;i++){
  doc.font('Times-Roman')
var p1=dashboard[d].patch[i];
  a=a+10;
  console.log(p1.patchdetail.length);
  ptable(doc,a,p1.patchdetail.substring(0,100),p1.notoupdate,p1.updatedno);
  //ptable(doc,a,p1.patchdetail,p1.notoupdate,p1.updatedno);
a=a+40;
generateHr(doc,45,a,555,a);
}
generateHr(doc,45,vr,45,a); generateHr(doc,555,vr,555,a);generateHr(doc,230,vr,230,a);generateHr(doc,380,vr,380,a);
doc.y=a;
doc.moveDown() .moveDown()
if(doc.y+header+(dashboard[d].os.length)*42 > 800){doc.addPage()}
doc.font('Times-Bold').fontSize(13)
doc.text("3. Desktop Operating System:",50,doc.y,{underline:true}) .moveDown()
o=doc.y+10;
vr=o;
generateHr(doc,25,o,575,o)
o=o+10;
doc.fontSize(12).font('Times-Bold')
.text("Name of OS",30,o,{width:100}) .text("No. of Devices",140,o, {width:50}) .text("License details",200,o,{width:100})
.text("Status of Patch Update",310,o,{width:120}) .text("Last Updated Patch in the System",440,o,{width:130})
o =doc.y+5;
generateHr(doc,25,o,575,o)
for(i=0;i<dashboard[d].os.length;i++){
  doc.font('Times-Roman')
 var p1=dashboard[d].os[i];
  o=o+5;
  console.log(p1.patchstatus.length)
   ostable(doc,o,p1.osname,p1.devicesno,p1.oemdetail.substring(0,60),p1.patchstatus.substring(0,70),p1.lastupdated.substring(0,70));
 o=o+50;
generateHr(doc,25,o,575,o);
}
generateHr(doc,25,vr,25,o);generateHr(doc,575,vr,575,o);generateHr(doc,135,vr,135,o);generateHr(doc,195,vr,195,o);generateHr(doc,305,vr,305,o);generateHr(doc,435,vr,435,o);
doc.y=o;
if(doc.y>700)doc.addPage()
doc.moveDown() .moveDown()
if(doc.y+header+(dashboard[d].server.length)*42 > 800){
doc.addPage()}
doc.fontSize(13).font('Times-Bold').strokeColor('#000000')
.text("4. Server Status:",50,doc.y,{underline:true})
.moveDown()
vr=doc.y;
generateHr(doc,35,doc.y,565,doc.y)
var s= doc.y+10;doc.fontSize(12)
doc.text("Server Details",40,s,{width:120})
doc.text("Operating System",180,s, {width:100})
doc.text("Type of access and list",300,s,{width:130})  .text("Status of Last Update",440,s,{width:120})
doc.y =doc.y+5;
generateHr(doc,35,doc.y,565,doc.y)
s=doc.y;
for(i=0;i<dashboard[d].server.length;i++){
  doc.font('Times-Roman')
 var p1=dashboard[d].server[i];
  s=s+5;
  console.log(p1.access.length)
   stable(doc,s,p1.sname,p1.sos,p1.access.substring(0,60),p1.patchstatus.substring(0,60));
 s=s+40
generateHr(doc,35,s,565,s);
}    generateHr(doc,35,vr,35,s);
generateHr(doc,565,vr,565,s);generateHr(doc,165,vr,165,s);generateHr(doc,285,vr,285,s);generateHr(doc,435,vr,435,s);
doc.y=s;
 doc.moveDown() .moveDown()
 doc.fontSize(13).font('Times-Bold').strokeColor('#000000')
 doc.text("5. Antivirus Details:",50,doc.y,{underline:true})
 doc.fontSize(12).font('Times-Bold')
 doc.moveDown() 
 doc.fontSize(12).font('Times-Roman')
 doc.text("a.Name of AntiVirus:  "+dashboard[d].av[0].avname+"             License Validity up to:  "+dashboard[d].av[0].avvalidity,{align:'justify'})
.moveDown()
 doc.text("b.Last updated date:  "+dashboard[d].av[0].avupdate,{align:'justify'})
 .moveDown()
 doc.text("c.Scheduled full scanning day:  "+dashboard[d].av[0].scheduledscandate,{align:'justify'})
 .moveDown()
 doc.text("d.Last full scanning date:  "+dashboard[d].av[0].lastscandate,{align:'justify'})
 .moveDown()
 doc.text("e.Type of vulnerability/threat observed during the month:   "+dashboard[d].av[0].avvuln,{align:'justify'})
 .moveDown()
 doc.fontSize(12).font('Times-Bold').strokeColor('#000000')
 doc.text("6. VAPT and Audit Details:",50,doc.y,{underline:true})
 .moveDown()
 doc.fontSize(12).font('Times-Roman')
 doc.text("a.VAPT Status Date of completion:    Phase-I:  "+dashboard[d].dashboard[0].vapt1+"        Phase-II:  "+dashboard[d].dashboard[0].vapt2,{align:'justify'})
 .moveDown()
 doc.text("b.Any other Security Audit, if any: "+dashboard[d].dashboard[0].audit,{align:'justify'})  
 .moveDown()
 doc.text("c.VAPT / Audit Compliance Status:  "+dashboard[d].dashboard[0].compliancestatus,{align:'justify'})
 .moveDown()
 doc.text("d.Existing Vulnerabilities, if any:   "+dashboard[d].dashboard[0].exvuln,{align:'justify'})
 .moveDown()
 doc.moveDown()
 doc.fontSize(13).font('Times-Bold').strokeColor('#000000')
.text("7.Security Incidents in Systems:",50,doc.y, {underline:true})
doc.moveDown()
 if(doc.y+header+(dashboard[d].inc.length*42)>800){
 doc.addPage()}
var f=doc.y
vr=f;
generateHr(doc,25,f,575,f)
f=f+10;
doc.fontSize(12)
doc.text("Description of the incident",30,f,{width:120})
doc.text("Date & Time",160,f, {width:70})
doc. 
text("System(s) affected",240,f,{width:80})  .text("Solution",330,f,{width:120}).text("Preventive Measures",460,f,{width:110})
f =doc.y+12;
generateHr(doc,25,f,575,f)
for(a=0;a<dashboard[d].inc.length;a++){
  doc.font('Times-Roman')
 var p1=dashboard[d].inc[a];
  f=f+5;
   itable(doc,f,p1.incdesc.substring(0,60),p1.idate,p1.itime,p1.sysaffected.substring(0,60),p1.solution.substring(0,60),p1.prevmes.substring(0,60));
 f=f+50;
generateHr(doc,25,f,575,f);
} 
generateHr(doc,25,vr,25,f);generateHr(doc,575,vr,575,f);generateHr(doc,155,vr,155,f);generateHr(doc,235,vr,235,f);
generateHr(doc,325,vr,325,f);generateHr(doc,455,vr,455,f);
doc.y=f;
doc.moveDown() .moveDown()
doc.fontSize(13).font('Times-Bold').strokeColor('#000000')
.text("8.Training/Awareness Programs organized on Cyber Security: ",50,doc.y,{underline:true})
.moveDown()
if(doc.y+header+(dashboard[d].tr.length*42)>800){
 doc.addPage()}
var g=doc.y
vr=g;
generateHr(doc,25,vr,585,g);
g=g+10;doc.fontSize(12)
doc.text("Program Title",30,g,{width:120})
doc.text("Date & Duration",170,g, {width:80})
doc. 
text("Delivered By",270,g,{width:100})  .text("No.of particpants",390,g,{width:80}).text("Participants Profile",490,g,{width:90})
g =doc.y+5;
generateHr(doc,25,g,585,g)
for(a=0;a<dashboard[d].tr.length;a++){
  doc.font('Times-Roman')
 var p1=dashboard[d].tr[a];
  g=g+10;
   ttable(doc,g,p1.ptitle,p1.pdate,p1.pduration,p1.pdeliveredby,p1.pno,p1.particprofile);
 g=g+32
 generateHr(doc,25,g,585,g)
} 
generateHr(doc,25,vr,25,g); generateHr(doc,585,vr,585,g);generateHr(doc,155,vr,155,g);generateHr(doc,255,vr,255,g);
generateHr(doc,375,vr,375,g);generateHr(doc,475,vr,475,g);
doc.moveDown();doc.moveDown();doc.moveDown();
doc.fontSize(13).font('Times-Bold').strokeColor('#000000')
(async () => {
  await new Promise(resolve => {
    // resolve after WriteStream is finished, 
    doc.stream.on('finish', resolve);
    // finalize the PDF and end the stream
    doc.end();
  });
  mergepdf('./test1.pdf',dashboard[0].dashboard[0].loglocation,dashboard[0].dashboard[0].changelocation)
})();
}

This is my code. I am generating pdf for a report. Data size is dynamic

I am passing data to the function fullreport () from my controller . And i am using fs.createreadstream to display pad onto browser

fullreport([{dashboard:rid,fw:fw,av:av,server:s,tr:t,patch:p,inc:i,os:os}],mname,rname)

        res.header('content-type', 'application/pdf');
var file = fs.createReadStream('./test.pdf');
         file.pipe(res);

This test.pdf i am writing using mergepdf() function

I think the problem is in the line 4:

//doc.pipe(fs.createWriteStream('./output.pdf'));

You can try to modify to:

const stream = fs.createWriteStream('./output.pdf');
doc.pipe(stream);

and then,

// doc.stream.on('finish', resolve);
stream.on('finish', resolve);

Thanks you are of great help..
But how to send pdf-lib generated pdf directly to browser.
I am using this.
var file = fs.createReadStream('./test.pdf');
file.pipe(res);

if i click on view report many times, it wont work as same file is being used always. So in pdf-kit i directly used doc.pipe(res). But in pdf-lib its showing errors. Can you help me.

You should check the documentation about embedPdf: https://pdf-lib.js.org/docs/api/classes/pdfdocument#embedpdf, the return value of the function is an array of pages, so I think the following code is what you want of,

const part1pgs = await docmerge.embedPdf(fs.readFileSync(textfile));

Thanks you are of great help..
But how to send pdf-lib generated pdf directly to browser.
I am using this.
var file = fs.createReadStream('./test.pdf');
file.pipe(res);

if i click on view report many times, it wont work as same file is being used always. So in pdf-kit i directly used doc.pipe(res). But in pdf-lib its showing errors. Can you help me.

const pdfBytes = await pdfDoc.save();
res.writeHead(200, { 'Content-Type': 'application/pdf' });
res.end(bdfBytes);

From mergepdf(), i want to return the document like (return pdfDoc) to controller. But how to handle it in controller. I cant create const doc1=new pdfDocument() using pdf lib.
from my controller i am calling fullreport(). In full report() i am calling mergepdf(). and i want to show the pdf in broswer. How can i do that. How to return the pdf-lib document to controller.

Sorry, I don't understand what you mean, maybe you can create pdfDoc in the controller and pass it to functions.

@hdwong Thanks for helping out with this!

@SiriSuri Please open a new issue if you are still having trouble with pdf-lib.

Just want to hang in there. I am currently also using both libraries, but my goal is to just use pdf-lib. Therefore I am trying to build a small library on top of pdf-lib for easier document creation. Help is greatly appreciated.

https://github.com/Patcher56/pdf-lib-builder

@Hopding Because so many variables and methods are using the private keyword, it makes "addon"-creation a bit difficult, maybe we could improve that by using the public or at least the protected keyword (would need to extend then).

@Patcher56 Looks like a very interesting project! I suspect that in many places the protected access modifier could be used instead of the private one. I don't want to replace all usages of private, and relatively few instances should be changed to public. If something is marked as private then it is not meant to be public interfaces and is subject to breaking changes. If you'd like to open a PR changing the things you need to protected, I'd be happy to take a look.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kevinswartz picture kevinswartz  Â·  5Comments

dhollenbeck picture dhollenbeck  Â·  4Comments

faxemaxee picture faxemaxee  Â·  6Comments

jboxxx picture jboxxx  Â·  4Comments

nachum37 picture nachum37  Â·  3Comments