How to convert a base64 png to pdf using nodejs? - node.js

Is there a way to convert multiple base64 PNG files to PDF?
Thanks

I was able to solve the problem using node-canvas
const fs = require('fs');
const {createCanvas, loadImage}= require('canvas');
// get the base64 string
const base64String = require("./base64");
loadImage(Buffer.from(base64String, "base64")).then(async (img) => {
const canvas = createCanvas(img.width, img.height, 'pdf');
const ctx = canvas.getContext('2d');
console.log(`w: ${img.width}, h: ${img.height}`);
ctx.drawImage(img, 0, 0, img.width, img.height);
const base64image = canvas.toBuffer();
// write the file to a pdf file
fs.writeFile('simple.pdf', base64image, function(err) {
console.log('File created');
});
});
for more info: https://www.npmjs.com/package/canvas
Note: solution works not only for png but any image type.
Then use any pdf library to merge the pdfs like hummus

I think you are looking for this.
import fs from 'fs';
let base64String = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA';
// Remove header
let base64image = base64String.split(';base64,').pop();
fs.writeFile('filename.pdf', base64image, {encoding: 'base64'}, function(err) {
console.log('File created');
});

Related

How to save base64 image using node js?

I am saving the base64 image using nodejs, it save my image at exact path, but show me the error.
Please help me to find the error.
Here is my code
var express = require('express');
var router = express.Router();
const fs = require('fs');
const mime = require('mime');
const path = './uploads';
router.post('/register', (req, res, next) => {
const base64Image = req.body.image;
const matches = base64Image.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
response = {};
if (matches.length !== 3) {
return new Error('Invalid input String');
}
response.type = matches[1];
response.data = new Buffer(matches[2]);
let decodedImg = response;
let imageBuffer = decodedImg.data;
let type = decodedImg.type;
let extension = mime.extension(type);
let fileName = 'image.' + extension;
try {
fs.writeFileSync(path + '/' + fileName, imageBuffer, 'utf8');
return res.send({ status: 'success' });
} catch (e) {
next(e);
}
return;
});
module.exports = router;
Any solution appreciated!
The mistake you made is when you are creating a buffer you are not specifying an encoding. You should create buffer like this:
new Buffer() is deprecated use Buffer.from() instead.
let buff = Buffer.from(m[2],'base64'); // encoding type base64
Basic code snippet
const fs = require('fs')
let a = 'base64ImageString'
let m = a.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
let b = Buffer.from(m[2],'base64');
fs.writeFile('image.png',b,function(err){
if(!err){
console.log("file is created")
}
});
Also, when you writing a buffer to file you don't have to pass encoding type, but if writing string you have to.
Check this out for a demo
https://repl.it/repls/GrimOfficialLocations
But it is not advised to send an image as base64 string. It is
inefficient for large images. base64 roughly takes 33% more bits than
its binary equivalent. I recommend you to check this out:
Upload base64 image with Ajax

NodeJS append base64 to file

i try to implement a chunk file upload in my React application.
I'm using blob.slice and Filreader to get base64 and send it as POST to my server.
const chunkSize = 1024 * 1024 * 5;
const blob = new Blob([file]);
const currentChunk = blob.slice(0, chunkSize);
const reader = new FileReader();
reader.readAsDataURL(currentChunk);
reader.onload = (e) => {
const base64Data = reader.result.replace('data:;base64,', '');
axios.post('url', {
data: base64Data
}).then((response) => {
// .. Get next chunk and so on
})
}
On my nodejs server side (with express) i try to append every base64 chunk to a file to restore the whole file:
fs = require('fs');
fs.writeFileSync(testFile, req.body.data, {encoding: 'base64'});
If i upload now a file for example with 500mb, the restored file on the server is always 5.2MB
based on docs, this should do the job
fs.appendFileSync(testFile, req.body.data, {encoding: 'base64'});

Node js, piping pdfkit to a memory stream

I am using pdfkit on my node server, typically creating pdf files, and then uploading them to s3.
The problem is that pdfkit examples pipe the pdf doc into a node write stream, which writes the file to the disk, I followed the example and worked correctly, however my requirement now is to pipe the pdf doc to a memory stream rather than save it on the disk (I am uploading to s3 anyway).
I've followed some node memory streams procedures but none of them seem to work with pdf pipe with me, I could just write strings to memory streams.
So my question is: How to pipe the pdf kit output to a memory stream (or something alike) and then read it as an object to upload to s3?
var fsStream = fs.createWriteStream(outputPath + fileName);
doc.pipe(fsStream);
An updated answer for 2020. There is no need to introduce a new memory stream because "PDFDocument instances are readable Node streams".
You can use the get-stream package to make it easy to wait for the document to finish before passing the result back to your caller.
https://www.npmjs.com/package/get-stream
const PDFDocument = require('pdfkit')
const getStream = require('get-stream')
const pdf = () => {
const doc = new PDFDocument()
doc.text('Hello, World!')
doc.end()
return await getStream.buffer(doc)
}
// Caller could do this:
const pdfBuffer = await pdf()
const pdfBase64string = pdfBuffer.toString('base64')
You don't have to return a buffer if your needs are different. The get-stream readme offers other examples.
There's no need to use an intermediate memory stream1 – just pipe the pdfkit output stream directly into a HTTP upload stream.
In my experience, the AWS SDK is garbage when it comes to working with streams, so I usually use request.
var upload = request({
method: 'PUT',
url: 'https://bucket.s3.amazonaws.com/doc.pdf',
aws: { bucket: 'bucket', key: ..., secret: ... }
});
doc.pipe(upload);
1 - in fact, it is usually undesirable to use a memory stream because that means buffering the entire thing in RAM, which is exactly what streams are supposed to avoid!
You could try something like this, and upload it to S3 inside the end event.
var doc = new pdfkit();
var MemoryStream = require('memorystream');
var memStream = new MemoryStream(null, {
readable : false
});
doc.pipe(memStream);
doc.on('end', function () {
var buffer = Buffer.concat(memStream.queue);
awsservice.putS3Object(buffer, fileName, fileType, folder).then(function () { }, reject);
})
A tweak of #bolav's answer worked for me trying to work with pdfmake and not pdfkit. First you need to have memorystream added to your project using npm or yarn.
const MemoryStream = require('memorystream');
const PdfPrinter = require('pdfmake');
const pdfPrinter = new PdfPrinter();
const docDef = {};
const pdfDoc = pdfPrinter.createPdfKitDocument(docDef);
const memStream = new MemoryStream(null, {readable: false});
const pdfDocStream = pdfDoc.pipe(memStream);
pdfDoc.end();
pdfDocStream.on('finish', () => {
console.log(Buffer.concat(memStream.queue);
});
My code to return a base64 for pdfkit:
import * as PDFDocument from 'pdfkit'
import getStream from 'get-stream'
const pdf = {
createPdf: async (text: string) => {
const doc = new PDFDocument()
doc.fontSize(10).text(text, 50, 50)
doc.end()
const data = await getStream.buffer(doc)
let b64 = Buffer.from(data).toString('base64')
return b64
}
}
export default pdf
Thanks to Troy's answer, mine worked with get-stream as well. The difference was I did not convert it to base64string, but rather uploaded it to AWS S3 as a buffer.
Here is my code:
import PDFDocument from 'pdfkit'
import getStream from 'get-stream';
import s3Client from 'your s3 config file';
const pdfGenerator = () => {
const doc = new PDFDocument();
doc.text('Hello, World!');
doc.end();
return doc;
}
const uploadFile = async () => {
const pdf = pdfGenerator();
const pdfBuffer = await getStream.buffer(pdf)
await s3Client.send(
new PutObjectCommand({
Bucket: 'bucket-name',
Key: 'filename.pdf',
Body: pdfBuffer,
ContentType: 'application/pdf',
})
);
}
uploadFile()

nodejs memory buffer synchronized .pipe()

I am trying to migrate pdfkit from png-js to pngsj2, because png-js doesn't support interlaced png. I need to load PNG file in a sync way. I try to do it this way:
var fs = require('fs'),
PNG = require('pngjs2').PNG;
var stream = require('stream');
var bufferStream = new stream.PassThrough();
var buf = fs.readFileSync('./logs/rid12.png');
bufferStream.end(buf);
var png = null;
bufferStream.pipe(new PNG())
.on('parsed', function() {
console.log("here");
png = this;
});
console.log("there",png);
"there" happens before "here", so png is null. Is it possible to pipe inmemory buffer to PNG parser so that I don't have to make callback architecture?
There is no way to make a async method synchronous.
There are librarys such as https://github.com/scriby/asyncblock which may be able to fake it.
asyncblock(function(flow) {
var png = null
flow.sync( bufferStream.pipe(new PNG())
.on('parsed', function() {
console.log("here");
png = this;
flow.callback()
})
);
// png not null
});

How to render image as pdf (canvas and pdfkit)

I have a function on nodejs that generates an image from many images and then generate a pdf file from that. Im trying with just one image but i need to add more, but this doesnt seems to work
function HelperHandler() {
this.pdf = function(req, res, next) {
var doc = new PDFDocument;
mergeImages(function(err, image) {
if (err)
return res.json(err);
doc.image(image, 100, 100);
doc.output(function(string) {
res.contentType = "application/pdf";
res.send(string);
});
})
}
}
var mergeImages = function(callback) {
var Canvas = require("canvas")
, fs = require("fs");
fs.readFile(global.root_path + "/images/bg.jpg", function(err, data) {
if (err)
callback("error loading image");
else {
var canvas = new Canvas(408, 939)
, img = new Canvas.Image(data);
ctx = canvas.getContext("2d");
img.onload = function() {
ctx.drawImage(img, 0, 0, 408, 939);
}
canvas.toDataURL('image/png', function(err, str) {
callback(null, str);
});
}
});
}
Error
Error: ENAMETOOLONG, name too long 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZgAAAOrCAYAAABqSpB/AAAABmJLR0QA/wD/AP+gvaeTAAAF5UlEQVR4nO3BMQEAAADCoPVPbQo/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICjAWckAAHF4EUcAAAAAElFTkSuQmCC'
at Object.openSync (fs.js:427:18)
at Object.readFileSync (fs.js:284:15)
at Function.open (/Users/jtomasrl/code/app/server/node_modules/pdfkit/js/image.js:27:28)
at PDFDocument.image (/Users/jtomasrl/code/app/server/node_modules/pdfkit/js/mixins/images.js:27:26)
at /Users/jtomasrl/code/app/server/lib/handler/current/helper.js:15:11
at /Users/jtomasrl/code/app/server/lib/handler/current/helper.js:41:9
at /Users/jtomasrl/code/app/server/node_modules/canvas/lib/canvas.js:217:7
You can use a buffer or a path with PDFKit image.
But you can't use a base64 URL, you need to decode this string to a buffer.
To use base64 data:
doc.image(new Buffer(image.replace('data:image/png;base64,',''), 'base64'), 100, 100); // this will decode your base64 to a new buffer
More information on base64 de/encode with Node Buffer here.

Resources