PDFToolKit is sending weird data - node.js

I have installed PDFKit for node.js. When i call my method to download a PDF file, i get this kind of characers on my browser:
xœU»nÜ0ìùüËûâ’Œ+8Ò¹.H‘œOn|Eà¿ï!ï> AÔ’”†ÃåÌŠ#áºa4Χð+ðÕ؇ýe£ªMfç)av
This is my code:
var doc = new PDFDocument({ layout: 'landscape' });
doc.pipe(res);
doc.moveDown(1);
doc.text(reportName, { align: "center" });
doc.text(bNames.join(','), { align: "center" });
doc.text(dateFrom + " - " + dateTo, { align: "center" });
doc.moveDown(2);
const table0 = {
headers: allNames,
rows: allData
};
doc.table(table0, {
prepareHeader: () => doc.font('Helvetica-Bold').fontSize(10),
prepareRow: (row, i) => doc.font('Helvetica').fontSize(10)
});
doc.end();
Is there something else i have to tell PDFKit to work right?
EDIT1:
I am running this code on my backend and sending it to my frontend so i can download it there

If you want a PDF response straight to the browser, you need to add the header so it understands the binary data.
Set this before the response is piped.
res.setHeader('Content-type', 'application/pdf')

Related

Generate PDF with pdfkit

I'm changing the configuration of the website regarding the a pdf creation and download by the user. The idea here is that the pdf is created from data from the DB and it's not stored, but directly offered to the user for download.
The original website was using html-pdf, but I'm unable to install it successfully, as I always have issue with phantomjs, phantomjs-prebuilt, etc.
I tried using pdfkit, but i'm not successful, and since I'm not very experienced the problem certainly is something I'm not quite getting.
Can someone give me some pointers, please?
let express = require('express'),
router = express.Router(),
asyncMiddleware = require('../utils/asyncMiddleware'),
auth = require('../middleware/authentication'),
// pdf = require('html-pdf'),
blobStream = require('blob-stream'),
pdf = require('pdfkit'),
juice = require('juice'),
{User, Report} = require('../models');
router.get('/:reportId?', auth.isLoggedIn, asyncMiddleware( async(req, res) => {
let reqUser = req.user;
//get the report
let report = await Report.findById( req.params.reportId ).populate('ownerManagerId');
if( !report ) {
req.flash('error', req.__('flash.error.reportNotFound') );
return res.redirect('back');
}
//check if user on the request is the user of the report or is the group admin stored when the report was generated
if( !reqUser._id.equals(report.ownerId) && !reqUser._id.equals(report.ownerManagerId) && req.user.role !=="admin" ){
req.flash('error', req.__('flash.error.forbiddenAccess') );
return res.redirect('back');
}
//report data
let data = {
layout: 'pdf_layout',
styles: [],
scripts: [],
user: reqUser,
lang: req.lang,
pageTitle: req.__('pageTitle-calculator-report'),
reportOwnerManager: report.ownerManagerId,
reportDate: formatDateInReport( report.createdAt ),
calcData: report.inputs,
output: report.outputs
}
//add some helper functions to ejs views
res.locals.formatNum = formatNum;
res.locals.findInputCorr = findInputCorr;
res.render('calculadora/relatorioPdf', data, (err1, html)=>{
if(err1) return res.send(err1);
//inline all css on html
let inlineHtml = juice(html);
//set pdf options
var pdfOptions = {
width: "297mm",
height: "420mm", // allowed units: mm, cm, in, px
//format: "A4", // allowed units: A3, A4, A5, Legal, Letter, Tabloid
//orientation: "portrait",
border: {
top: "30mm", // default is 0, units: mm, cm, in, px
right: "20mm",
bottom: "0",
left: "20mm"
}
};
// //create the pdf and stream it to response
// pdf.create(inlineHtml, pdfOptions).toStream( (err2, stream)=>{
// if(err2) return res.send(err2);
// //set headers so the file is downloaded instead of shown in browser
// res.writeHead(200, {
// 'Content-Type': 'application/pdf',
// 'Content-disposition': `attachment; filename=relatorio-${report.id}.pdf`
// });
// //redirect stream to response
// stream.pipe(res);
// });
//set headers so the file is downloaded instead of shown in browser
const stream = res.write(200, {
'Content-Type': 'application/pdf',
'Content-disposition': `attachment; filename=relatorio-${report.id}.pdf`
});
buildPDF(
(chunk) => stream.write(chunk),
() => stream.end()
);
function buildPDF() {
const doc = new pdf();
doc.on('data', dataCallback);
doc.end('data', endCallback);
doc.pipe(res);
doc.end();
}
});//render
}))//get´´´

Questions regarding pdf to image conversion with Node JS

The plan is to create a pdf file (that only consists of a single page) then the user chooses whether to download as PDF or image. I have already written the code for generating the PDF and it is working fine as of now. The problem now is how to convert this to image. Is it possible to convert files without installing stuff like Ghostscript etc?
I am a complete noob, advice is greatly appreciated. (Recommendations on which libraries to use would also be helpful)
Code for generating the PDF
import PDFDocument from "pdfkit";
static async medicalPrescription(req, res) {
// Some code for generating the PDF contents...
filename = encodeURIComponent(filename) + '.pdf'
res.setHeader('Content-disposition', 'attachment; filename="' + filename + '"')
res.setHeader('Content-type', 'application/pdf')
const content = req.body.content
doc.y = 300
doc.text(content, 50, 50)
doc.pipe(res)
doc.end()
}
The client then receives the generated file and opens it in another tab.
React file that sends the request and opens the response
const handleSubmit = async (e) => {
// Some code for sending the pdf content from the user
fetch("http://localhost:5050/generate-rx", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: parsed
})
.then(async res => {
if (res.status === 200) {
const blob = await res.blob();
const file = new Blob(
[blob],
{type: 'application/pdf'}
);
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
})
}
You can use pdf2pic. It can convert pdf to image.
import { fromPath } from "pdf2pic";
const options = {
density: 100,
saveFilename: "untitled",
savePath: "./images",
format: "png",
width: 600,
height: 600
};
const storeAsImage = fromPath("/path/to/pdf/sample.pdf", options);
const pageToConvertAsImage = 1;
storeAsImage(pageToConvertAsImage).then((resolve) => {
console.log("Page 1 is now converted as image");
console.log(resolve); // send resolve to user
});

how to save png format to pdf format using pdfkit in nodejs [code included]

I am doing a server side coding and wanted to convert the file extension to pdf extension and it should be saved in S3. Any suggestions will be valuable. I am a beginner in this area, so apologies if the question offends anyone.
uploadFiles: (files, bucketName, path) =>{
return new Promise(async function(resolve, reject) {
var doc = new PDFDocument()
var file = files[0];
let filename = file.originalFilename;
var file_path = file.path;
doc.image(file_path, {
fit: [250, 300],
align: 'center',
valign: 'center'
});
//doc.y = 300
//doc.pipe(res)
// console.log('img',img);
var path = 'images/'+ file.originalFilename;
console.log( 'path',path);
var path = await uploadFiles(file, bucketName, path);
resolve(path);
//doc.end()
});
}
};
Try by looking into this example. You probably would need to save the PDF on disk before uploading it to a bucket. Also try adding the X and Y position to where the image will be placed on the PDF.
router.route('/generate.pdf')
.post(multiparty(), function(req,res,next){
// Creates a new instace of a Pdf Document Model
var pdfDocument = new PdfDocument(req.body);
// New Instance of the PDFKit PDFDOCUMENT
var pdf = new PDFDocument({
size: 'LETTER',
info: {
Title: 'title',
Author: 'me',
}
});
// Top Logo
pdf.image('./logo.jpg', 500, 35, {width: 60});
// Document Top Title
pdf.fontSize(14).text('Some Title', {
align: 'center' });
var fileName = 'some.pdf';
// Stream contents to a file
pdf.pipe(fs.createWriteStream(fileName))
.on('finish', function () {
console.log('PDF closed');
});
// Close PDF and write file.
pdf.pipe(res);
pdf.end();
});

node PDFkit blank pages

i'm creating a PDF with node.js and this package : https://github.com/devongovett/pdfkit
My problem is that when i download the pdf on the browser it is totaly blanck...
server-side code :
PDFDocument = require('pdfkit');
function creaEtichetta(req, res){
doc = new PDFDocument
size: 'a4'
bufferPages: true
doc.addPage().fontSize(25).text('Here is some vector graphics...', 100, 100);
doc.save()
.moveTo(100, 150)
.lineTo(100, 250)
.lineTo(200, 250)
.fill("#FF3300");
doc.addPage().fillColor("blue").text('Here is a link!', 100, 100).link(100, 100, 160, 27, 'http://google.com/')
doc.pipe(res);
doc.end();
}
exports.creaEtichetta = creaEtichetta;
client-side code :
var data = {};
data.azione = "getEtichettaProdotto";
//Scarico i dati anagrafica
$.ajax({
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
url: 'http://46.101.209.16/endpoint',
success: function(etichettas) {
var blob=new Blob([etichettas]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Label"+".pdf";
link.click();
}//SUCCESS
});
sorry for bad english, i'm italian
It could be that the binary characters in your pdf aren't being correctly coded in the transfer, which would explain why locally its ok but not when transferred - pdfs are a mix of ascii and binary characters, and if the binary is corrupted it seems that you get a blank pdf.
That's likely to be a browser side issue, this approach worked for me:
https://stackoverflow.com/a/27442914/2900643
Coupled with this:
https://stackoverflow.com/a/33818646/2900643
EDIT: Better still use: https://github.com/eligrey/FileSaver.js/
Server:
var doc = new PDFDocument();
doc.pipe(res);
doc.circle(280, 200, 50).fill("#6600FF");
doc.end();
Browser:
angular.module('app')
.service('PdfService', function($http) {
var svc = this;
svc.getPdf = function() {
return $http.get('/getpdf/', { responseType : 'arraybuffer' });
};
});
angular.module('app')
.controller('PdfCtrl', function($scope, PdfService) {
$scope.getPdf = function() {
PdfService.getPdf().success(function(data) {
var fileName = 'hello.pdf';
var pdf = new Blob([data], {type : 'application/pdf'});
saveAs(pdf, fileName);
})
};
});
I was running in the same issue but without any client-side code involved. So this issue isn't simply client-side. Obviously, the server-side response PDFKit is piping into isn't encoding "binary" as requested by PDFKit, but applying "utf8".
Sadly, as of today there is no way of assigning some default encoding to the stream provided by ServerResponse. See https://github.com/nodejs/node/issues/14146
In my case I was working around this issue for now by collecting chunked output from PDFKit and writing at once. Instead of
var doc = new PDF();
res.type( "pdf" ).attachment( "test.pdf" );
doc.pipe( res );
I am using this:
var doc = new PDF();
res.type( "pdf" ).attachment( "test.pdf" );
var buffers = [];
doc.on( "data", function( chunk ) { buffers.push( chunk ); } );
doc.on( "end", function() {
res.end( Buffer.concat( buffers ), "binary" );
} );
This comes with a disadvantage: since all PDFs are cached in memory this code has an impact on server side memory consumption when it comes to high number of simultaneous requests or to generating huge PDF files.
Confusingly, trying to hand over PDFKit output chunk by chunk didn't work again:
var doc = new PDF();
res.type( "pdf" ).attachment( "test.pdf" );
doc.on( "data", function( chunk ) { res.write( chunk, "binary" ); } );
doc.on( "end", function() { res.end(); } );

PDF Generation in Nodejs

I have requirement where i need to create a PDF from html in node .In addition to that i must be able to add custom header and footer. Then content of the web page should be in multiple pages based on custom requirement. that is we must able to decide what content should be which page.
After searching i found some solutions like phantom , but there is no proper documentation which could help me.
I need some way to do this.
Found a solution
here , but explanation is not good enough.
I have been using https://github.com/baudehlo/node-phantom-simple module with webshot
Step I follow:-
Create HTML using the node-phantom-simple
After creating the HTML use that HTML to pass it in webshot.
var driver = require('node-phantom-simple');
var webshot = require('webshot');
driver.create({path: require('phantomjs').path, 'parameters': {'disk-cache': 'yes'}}, function (err, browser) {
return browser.createPage(function (err, page) {
return page.open(url, function (err, status) {
console.log("opened site? ", status,page.content);
return page.evaluate(File.cleanHtml, function (err, html) {
var options = {
streamType: 'pdf',
screenSize: {
width: 1200,
height: 300
},
shotSize: {
width: 1200,
height: 300
},
siteType:'html',// include this to generate PDF from html
phantomConfig: {'ssl-protocol':'any','ignore-ssl-errors': 'true'}
}
var pdfname = 'test_' + Date.now() + '.pdf';
webshot(html,pdfname,options, function(err) {
console.log("err in webshot",err);
})
});
// });
});
});
});
Here File.cleanHtml is a function I have created which is used to remove extra content from HTML.you can directly return same HTML from this function.may be something like this :)
cleanHtml: function () {
var data = $("html");
return "<html>" + data.html() + "</html>";
}

Resources