Questions regarding pdf to image conversion with Node JS - 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
});

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´´´

How to display base64 string as image in email, using AWS SES

I am trying to show an image inside my e-mail. But that image is not getting displayed. I am using base64 string, which I am fetching from S3 bucket.
I am able to get email in inbox, but only thing image is not working when passing url, if directly using base64 hard coded string in html its working.
I need to fetch image from s3 and that image should be inline with email.
"use strict";
const fs = require("fs");
const path = require("path")
const Handlebars = require('handlebars');
const {SESClient, sendEmailCommand} = require("#aws-sdk/client-ses");
const {S3Client, GetObjectCommand} = require("#aws-sdk/client-s3");
let S3=null, SES=null;
const streamToBuffer = async(stream) =>{
return new Promise((resolve, reject) =>{
const chunks = [];
stream.on("data", (chunk) =>{chunks.push(chunk)});
stream.on("error", reject);
stream.on("end", () =>{resolve(Buffer.conact(chunks))});
})
}
export.handler = async(event) =>{
if(S3 === null){
S3 = new S3Client ({region: MY_REGION})
}
if(SES === null){
SES = new SESClient ({region: MY_REGION})
}
try{
let deatils = event.detail.fullDocument;
let imageKey = `${deatils.dir}/myimage.png`;
let imageFileFromS3 = await S3.send(
new GetObjectCommand({
Bucket: MY_BUCKET_NAME, key: imageKey
}))
let imageFileBuffer = await streamToBuffer(imageFileFromS3.Body)
let bufferToBase64 = imageFileBuffer.toString("base64");
const emailSubject = "Hey!! Test mail with image";
const emailData = {
Name: "Email Tester"
ImageSrc: `data:image/png;base64, ${bufferToBase64}`
}
let htmlTemplate = Handlebars.complie(fs.readFileSync(path.join(__dirname, 'templateSrc', email.html)).toString())
let textTemplate = Handlebars.complie(fs.readFileSync(path.join(__dirname, 'templateSrc', email.txt)).toString())
let emailResult = await SES.send( new SendEmailCommand({
Source: "Source_test#email.com", //dummy email for now
Destination :{
ToAddress: ["to_test#email.com"] // dummy address
},
Message: {
Subject: {
Charset: 'UTF-8',
Data: emailSubject
},
Body: {
Text: {
Charset: 'UTF-8',
Data: textTemplate(emailData)
},
Html:{
Charset: 'UTF-8',
Data: htmlTemplate(emailData)
}
}
}
}))
return emailResult
}catch(error){
console.log(error)
}
}
email.txt
Dear {{Name}}
Thanks for asking images on email.
Please find your requested images below
Face image
Bus image
-----Thanks
Email.html
<h1>Dear {{Name}}</h1>
<p>Thanks for asking images on email.</p>
<p>Please find your requested image below</p>
<p>face Image</p>
<img src={{ImageSrc}} />
<p>Bus Image</p>
<img src="data:image/gif;base64,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">
//This image is working
<p>-------Thanks</p>
I have just resolved this issue...
So I thought, about posting answer for others help.
The root cause of this was- large size of my buffer response form S3, and email only supports 128MB data, as I found in cloud watch logs ( I can comment about AWS SES only, not sure about other email clients)
So the ultimate solution for my problem is just to resize the buffer response, which we are getting from S2.
So I have used sharp https://www.npmjs.com/package/sharp
And add these line in index.js
//Here I will resize the image
const resizedImageFileBuffer =
await sharp(imageFileBuffer)
.resize ({
width:200,
height:200,
fit: 'contain'
})
.toFormat('png')
.png({
quality:100,
compressionLevel: 6
})
.toBuffer()
//Now we will convert resized buffer to base64
let bufferToBase64 =
resizedImageFileBuffer.toString("base64");

Generated docx is empty when downloaded

This is a Meteor-app. I need to generate a docx-file and download it.
I am testing it by running: localhost:3000/download.
Word file is generated, but it is totally empty.
Why? I would appreciate any advice!
This is my server-side code:
const officegen = require('officegen');
const fs = require('fs');
Meteor.startup(() => {
WebApp.connectHandlers.use('/download', function(req, res, next) {
const filename = 'test.docx';
let docx = officegen('docx')
// Create a new paragraph:
let pObj = docx.createP()
pObj.addText('Simple')
pObj.addText(' with color', { color: '000088' })
pObj.addText(' and back color.', { color: '00ffff', back: '000088' })
pObj = docx.createP()
pObj.addText(' you can do ')
pObj.addText('more cool ', { highlight: true }) // Highlight!
pObj.addText('stuff!', { highlight: 'darkGreen' }) // Different highlight color.
docx.putPageBreak()
pObj = docx.createP()
let out = fs.createWriteStream(filename);
res.writeHead(200, {
'Content-Disposition': `attachment;filename=${filename}`,
'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
res.end(docx.generate(out));
});
});
The problem you are facing is that docx.generate(out) is an async function: when calling res.end(docx.generate(out)) you end the request right now while you start generating the docx in the file test.docx. Hence the doc does not exist yet.
You should modify your code to send over the file directly like this:
res.writeHead(200, {
'Content-Disposition': `attachment;filename=${filename}`,
'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
docx.generate(res)
If you still need the file on the server side you can use another approach waiting for the file being generated (see here)

Posting An Image from Webcam to Azure Face Api

I am trying to upload an image that I get from my webcam to the Microsoft Azure Face Api. I get the image from canvas.toDataUrl(‘image/png’) which contains the Data Uri. I change the Content Type to application/octet-stream and when I attach the Data Uri to the post request, I get a Bad Request (400) Invalid Face Image. If I change the attached data to a Blob, I stop receiving errors however I only get back an empty array instead of a JSON object. I would really appreciate any help for pointing me in the right direction.
Thanks!
Oh you're in such luck, i've just (successfully!) attempted this 2 days ago.
Sending base64-encoded JPEGs to Face API is seriously inefficient, The ratio of encoded output bytes to input bytes is 4:3 (33% overhead). Just send a byte array, it works, the docs mention it briefly.
And try to read as JPEG not PNG, that's just wasting bandwidth for webcam footage.
...
var dataUri = canvas.toDataURL('image/' + format);
var data = dataUri.split(',')[1];
var mimeType = dataUri.split(';')[0].slice(5)
var bytes = window.atob(data);
var buf = new ArrayBuffer(bytes.length);
var byteArr = new Uint8Array(buf);
for (var i = 0; i < bytes.length; i++) {
byteArr[i] = bytes.charCodeAt(i);
}
return byteArr;
Now use byteArr as your payload (data:) in $.ajax() for jQuery or iDontUnderStandHowWeGotHereAsAPeople() in any other hipster JS framework people use these days.
The reverse-hipster way of doing it is:
var payload = byteArr;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://SERVICE_URL');
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.send(payload);
To extend Dalvor's answer: this is the AJAX call that works for me:
fetch(data)
.then(res => res.blob())
.then(blobData => {
$.post({
url: "https://westus.api.cognitive.microsoft.com/face/v1.0/detect",
contentType: "application/octet-stream",
headers: {
'Ocp-Apim-Subscription-Key': '<YOUR-KEY-HERE>'
},
processData: false,
data: blobData
})
.done(function(data) {
$("#results").text(JSON.stringify(data));
})
.fail(function(err) {
$("#results").text(JSON.stringify(err));
})
Full demo code here: https://jsfiddle.net/miparnisari/b1zzpvye/
For saving someone's 6 hours, I appended my right code.
I hope this code helps you.
Tools
React
Typescript
React-webcam
Mac OS
Axios
Code
index.tsx
Constants and ref
/**
* Constants
*/
const videoConstraints = {
width: 1280,
height: 720,
facingMode: 'user',
};
/**
* Refs
*/
const webcamRef = React.useRef<Webcam>(null);
Call back function
const capture = React.useCallback(() => {
const base64Str = webcamRef.current!.getScreenshot() || '';
const s = base64Str.split(',');
const blob = b64toBlob(s[1]);
callCognitiveApi(blob);
}, [webcamRef]);
In render
<Webcam audio={false} ref={webcamRef} screenshotFormat="image/jpeg" videoConstraints={videoConstraints} />
<button onClick={capture}>Capture photo</button>
base64toBlob
Thanks to creating-a-blob-from-a-base64-string-in-javascript
export const b64toBlob = (b64DataStr: string, contentType = '', sliceSize = 512) => {
const byteCharacters = atob(b64DataStr);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, { type: contentType });
return blob;
};
callCognitiveApi
import axios from 'axios';
const subscriptionKey: string = 'This_is_your_subscription_key';
const url: string = 'https://this-is-your-site.cognitiveservices.azure.com/face/v1.0/detect';
export const callCognitiveApi = (data: any) => {
const config = {
headers: { 'content-type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key': subscriptionKey },
};
const response = axios
.post(url, data, config)
.then((res) => {
console.log(res);
})
.catch((error) => {
console.error(error);
});
};
Result
So I got the answer finally by sending the image as a blob object. You first grab the image from canvas with:
let data = canvas.toDataURL('image/jpeg');
Afterwards, you can reformat it to a blob data object by running:
fetch(data)
.then(res => res.blob())
.then(blobData => {
// attach blobData as the data for the post request
}
You will also need to switch the Content-Type of the post request to "application/octet-stream"

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(); } );

Resources