Create a animated Gif from an array of url images in NodeJs - node.js

I have an array of urls of image which could be of different type (png, jpg) and I would like with it to build a gif, all in nodeJS.
Since I'm very new to this language I'm struggling a bit with it, any leads, ideas ?
I looked at gifencoder
with this exemple :
var GIFEncoder = require('gifencoder');
var encoder = new GIFEncoder(854, 480);
var pngFileStream = require('png-file-stream');
var fs = require('fs');
pngFileStream('test/**/frame?.png')
.pipe(encoder.createWriteStream({ repeat: -1, delay: 500, quality: 10 }))
.pipe(fs.createWriteStream('myanimated.gif'));
What I get, it looks for png files maching the expression.
Since I have an array of urls of different types of images, should I use fs.createReadStream ?
Thanks

const GIFEncoder = require("gif-encoder-2");
const { createCanvas, Image } = require("canvas");
const { createWriteStream, readdir } = require("fs");
const { promisify } = require("util");
const path = require("path");
const fs = require("fs");
var outputDir = path.join(__dirname, "/output")
const createGif = async (fileName, frameURLs) => {
return new Promise(async resolve1 => {
try {
// find the width and height of the image
const [width, height] = await new Promise(resolve2 => {
const image = new Image();
image.onload = () => resolve2([image.width, image.height]);
image.src = frameURLs[0];
});
// base GIF filepath on which algorithm is being used
const dstPath = path.join(__dirname, "/output", `${fileName}.gif`);
// create a write stream for GIF data
const writeStream = createWriteStream(dstPath);
// when stream closes GIF is created so resolve promise
writeStream.on("close", () => {
resolve1(dstPath);
});
// encode with the neuquant algorithm
const encoder = new GIFEncoder(width, height, "neuquant");
// pipe encoder's read stream to our write stream
encoder.createReadStream().pipe(writeStream);
encoder.start();
encoder.setDelay(200);
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
// draw an image for each file and add frame to encoder
const promisses = frames.map(frame => {
return new Promise(resolve3 => {
const image = new Image();
image.onload = () => {
ctx.drawImage(image, 0, 0);
encoder.addFrame(ctx);
resolve3();
};
image.src = frame; // set the frame url
});
});
// run all the frame promisses
await Promise.all(promisses);
// close the writing stream
writeStream.close();
} catch (error) {
console.error("error");
console.error(error);
throw error;
}
});
};

Related

How to get Dicom lossless image from google cloud?

This is my code to get a dicom image from Google Cloud. It works well, but the image is lossy.
router.get("/dicomWebRetrieveInstance/dcmfile", async (req, res, next) => {
const writeFile = util.promisify(fs.writeFile);
emphasized textconst fileName = 'rendered_image.jpeg';
const cloudRegion = "us";
const projectId = "neurocaredicom";
const datasetId = "System_1";
const dicomStoreId = "Site_1A";
const studyUid = "1.2.276.0.7230010.3.1.2.296485376.1.1521713579.1849134";
const seriesUid = "1.2.276.0.7230010.3.1.3.2`enter code here`96485376.1.1521713580.1849651";
const instanceUid = "1.2.276.0.7230010.3.1.4.296485376.1.1521713580.1849652";
const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
const dicomWebPath = `studies/${studyUid}/series/${seriesUid}/instances/${instanceUid}/rendered`;
const request = {parent, dicomWebPath};
const rendered =
await healthcare.projects.locations.datasets.dicomStores.studies.series.instances.retrieveRendered(
request,
{
headers: "application/octet-stream; transfer-syntax=* ",
responseType: 'arraybuffer',
}
);
const fileBytes = Buffer.from(rendered.data);
await writeFile(fileName, fileBytes);
var options = {
root: path.join(__dirname),
};
// read binary data
var bitmap = fs.readFileSync(fileName);
// convert binary data to base64 encoded string
res.status(200).sendFile(fileName, options, function (err) {
if (err) {
next(err);
} else {
console.log(
`Retrieved rendered image and saved to ${fileName} in current directory`
);
}
});
});
Any solution to that problem would be appreciated, so anyone can help.

How to avoid multi-byte file path bug of node-canvas's loadImage?

node-canvas (2.7.0) has bug which cannot load image from the path includes multi-bytes characters.
const { loadImage } = require('canvas');
const image = await loadImage('/Users/ユーザ名/Documents/test.png');
Such case causes "File not found" error.
How to avoid this?
We should wait official bug fix, but following works as work-around:
const { Image } = require('canvas');
const fs = require('fs');
const image = await new Promise((res, rej) => {
fs.readFile('/Users/ユーザ名/Documents/test.png', (err, buf) => {
if (err) rej(err);
const img = new Image();
img.onload = () => { res(img) };
img.onerror = (err) => { rej(err) };
img.src = buf;
});
});

image resize on node.js

I want to save images after resize so used sharp package but dont small image size.Iadded await sharp(uploadImage.name).resize(600, 600); line after save folder What am I missing?
const path = require('path');
const fs = require('fs');
const AdminBro = require('admin-bro');
const sharp=require('sharp');
/** #type {AdminBro.After<AdminBro.ActionResponse>} */
const after = async (response, request, context) => {
const { record, uploadImage } = context;
if (record.isValid() && uploadImage) {
// console.log(uploadImage.name);
await sharp(uploadImage.name).resize(600, 600);
const filePath = path.join('uploads', record.id().toString(), uploadImage.name);
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await fs.promises.rename(uploadImage.path, filePath);
await record.update({ imagePath: `/${filePath}` });
}
return response;
};
/** #type {AdminBro.Before} */
const before = async (request, context) => {
if (request.method === 'post') {
const { uploadImage, ...otherParams } = request.payload;
// eslint-disable-next-line no-param-reassign
context.uploadImage = uploadImage;
return {
...request,
payload: otherParams,
};
}
return request;
};
module.exports = { after, before };
const filePath = path.join('uploads', record.id().toString(), uploadImage.name);
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await sharp(uploadImage.path).withMetadata().resize(600,600).toFile(filePath);//here added correct paths but now although it saved image, doesnt reduce imageSize
You are not writing the processed image to a file.
Let's say this is your storage path:
const outputPath = __dirname + `/../storage/myImage.jpeg`;
You can give that path to sharp
await sharp('path/of/originalImage').withMetadata().resize(600, 600).toFile(outputPath);
And I highly recommend you to use withMetaData() otherwise you lose your images metaData

NodeJS : Rotate a base64 image and save it

I want to make a function to rotate at 90° an base64 image. I tried to use Image and Canvas but it returns Error.
Here is the Code :
exports.Test64 = (req, res, next) => {
console.log("TEST UPS")
upsres.find().then(
(ups) => {
console.log('YES');
console.log(ups[0].b64Image); // it works but after i don't think
var image = new Image();
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
image.src = ups[0].b64Image;
canvas.width = image.height;
canvas.height = image.width;
ctx.rotate(-90 * Math.PI / 180);
ctx.translate(-canvas.heigt,0);
ctx.drawImage(image, 0, 0);
dataURL= canvas.toDataURL();
console.log(dataURL);
res.status(200).json(dataURL);
);
}
).catch(
(error) => {
res.status(400).json({
error:'Not Working'
});
}
);
};
Someone have an answer why it doesn't work ? :)
Thanks to everyone
We can use jimp libray to get rotate image.
Please use following code
var Jimp = require('jimp');
const base64str ="R0lG"//
const buf = Buffer.from(base64str, 'base64');
const image = await Jimp.read(buf);
// rotate Function having a rotation as 90
image.rotate(90).getBase64(Jimp.MIME_JPEG, function (err, src) {
console.log("rb is \n")
console.log(src);
})
exports.Testups = async (req, res, next) => {
try {
let ups = await upsres.find()
const base64str = ups[0].b64Image;
const buf = Buffer.from(base64str, 'base64');
let image = await Jimp.read(buf);
// rotate Function having a rotation as 90
image.rotate(-90).getBase64(Jimp.MIME_PNG, function (err, src) {
res.status(200).json(src);
})
}
catch (e) { }
};
That's Working :)

Nodejs and pdfKit and qr-image

I am working on a small project that works with generating pdf's in node and express, l am using the pdfkit npm module but to generate a pdf. l am also using the qr-image npm module to generate the QR code image but l am struggling to attach the generated the QR code to the pdf. This is the code that l am using to generate the QR code:
var file = "./"+certificationnumber+".png";
var qr_svg = qr.image(file, { type: 'png' });
qr_svg.pipe(require('fs').createWriteStream(file));
And this is how l am trying attache it to the pdf using pdfkit npm module:
doc.image(qr_svg.pipe(require('fs').createWriteStream(file)))
since every pdf has a unique QR code.
Thanks in advance
I exactly have the same problem where get the PDF in 64 bit encoded form and i have to attach qr image to it.
Below is the code snippet.
PDFDocument = require('pdfkit');
const base64 = require('base64-stream');
const doc = new PDFDocument();
doc.image('test.jpeg', {
fit: [250, 300],
align: 'center',
valign: 'center'
});
const finalString = pdf.response;
// contains the base64 string
//logic to append the qr image.
const stream = doc.pipe(base64.encode());
stream.on('data', chunk => finalString += chunk);
stream.on('end', () => {
// the stream is at its end, so push the resulting base64 string to the response
const backToPDF = new Buffer(finalString, 'base64');
read.writeFileSync('./Report.pdf', backToPDF);
});
doc.end();
Here is the solution I have come up with. Using pdf-lib, express and qrcode
const fs = require("fs");
const path = require("path");
const express = require("express");
const http = require("http");
const cors = require("cors");
const multer = require("multer");
const app = express();
const server = http.createServer(app);
const { PDFDocument } = require("pdf-lib");
const QRCode = require("qrcode");
const Joi = require("joi");
const { readFile, writeFile, unlink } = require("fs/promises");
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
const dir = "public";
const subDirectory = "public/uploads";
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
fs.mkdirSync(subDirectory);
}
const generateQRCodeImage = async function (filePath, text, color) {
return new Promise((resolve, reject) => {
QRCode.toFile(
filePath,
text,
{
color,
},
function (err) {
if (err) return reject(err);
resolve();
}
);
});
};
const run = async ({
width,
height,
x,
y,
pathToImage,
pathToPDF,
pathToOutputPDF,
qrCodeText,
qrDarkColor = "#000",
qrLightColor = "#0000",
}) => {
await generateQRCodeImage(pathToImage, qrCodeText, {
dark: qrDarkColor,
light: qrLightColor,
});
const pdfDoc = await PDFDocument.load(await readFile(pathToPDF));
const img = await pdfDoc.embedPng(await readFile(pathToImage));
Array.from({ length: pdfDoc.getPageCount() }).forEach((_, index) => {
let imagePage = pdfDoc.getPage(index);
imagePage.drawImage(img, {
x,
y,
width,
height,
});
});
const pdfBytes = await pdfDoc.save();
await writeFile(pathToOutputPDF, pdfBytes);
};
const pdfFileFilter = function (req, file, callback) {
const ext = path.extname(file.originalname);
if (ext !== ".pdf") {
return callback("This extension is not supported");
}
callback(null, true);
};
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/uploads");
},
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
const filesToProcess = multer({ storage: storage, fileFilter: pdfFileFilter });
const schema = Joi.object({
width: Joi.string().regex(/^\d+$/).required(),
height: Joi.string().regex(/^\d+$/).required(),
x: Joi.string().regex(/^\d+$/).required(),
y: Joi.string().regex(/^\d+$/).required(),
qrCodeData: Joi.string().required(),
qrDarkColor: Joi.string(),
qrLightColor: Joi.string(),
});
app.post("/addQrToPdf", filesToProcess.array("file", 1), async (req, res) => {
const pathToImage = "public/uploads/" + Date.now() + "temp-qr.png";
const pathToOutputPDF = "public/uploads/" + Date.now() + "-output.pdf";
if (req.files) {
const [file] = req.files;
if (!file) {
res.send("No file detected on input");
}
const pathToPDF = file.path;
try {
const { width, height, x, y, qrCodeData, qrDarkColor, qrLightColor } =
await schema.validateAsync(req.body);
await run({
width: +width,
height: +height,
x: +x,
y: +y,
qrDarkColor,
qrLightColor,
qrCodeText: qrCodeData,
pathToImage,
pathToOutputPDF,
pathToPDF,
});
const pdfFile = await readFile(pathToOutputPDF);
res.contentType("application/pdf");
res.send(pdfFile);
await unlink(pathToImage);
await unlink(pathToPDF);
await unlink(pathToOutputPDF);
} catch (error) {
try {
await unlink(pathToPDF);
await unlink(pathToImage);
} catch (err) {
console.warn(err);
}
res.send(error);
}
}
});
server.listen(4000, () => console.log("listening on port *:4000"));
I had the same problem.
It looks like it is an async issue.
If you treat the generation of the QR code like a promise and then run the create pdf code, it works.
My code is below (using promise). It should also work using async await
const createPDF = (ticketID) => {
const doc = new pdf
doc.pipe(fs.createWriteStream(`${ticketID}.pdf`))
doc.text('Your Tickets').fontSize(25)
doc.image(`./qrCodes/${ticketID}.png`, {
fit: [250, 300],
align: 'center',
valign: 'center'
});
doc.end()
}
const createQRCode = (ticketID) => {
QR.toFile(`./qrCodes/${ticketID}.png`, String(ticket), {width: 250}).then(qr => {
createPDF(ticketID)
})
}
I use a version without writing to the disk, using the svg path output, extract the path from the qrcode data and render it into the pdf
let s = await toString('hallo2', {
type: 'svg'
})
// extract the 2nd path element
let path = Array.from(s.matchAll(/d="(.*?)"/gm))[1][1]
let doc = new PDFDocument({ size: 'A4' });
doc.scale(8)
.path(data)
.stroke()
doc.pipe(fs.createWriteStream('out2.pdf', {}));
doc.end()

Resources