Base64 image resizer nodejs - node.js

I am working on a node+express backend.
I am receiving base64 images string from API and I am storing it into MongoDB. Before storing I want to reduce the image size (scaling). I tried JIMP but it is not reading base64 input.
Can anyone please suggest any good node module which accepts base64 image, scale it and return a new base64 string.
Thanks

A little late but I just implemented this feature using the sharp library, so might as well share it now.
I created a function that takes a base64 and the height and width (optional) you want to resize the image to and returns the resized base64 string.
I can just call this function whenever I want.
Code
// Don't forget to import sharp
import sharp from "sharp"
export const resizeBase64 = async ({ base64Image, maxHeight = 640, maxWidth = 640 }) => {
const destructImage = base64Image.split(";");
const mimType = destructImage[0].split(":")[1];
const imageData = destructImage[1].split(",")[1];
try {
let resizedImage = Buffer.from(imageData, "base64")
resizedImage = await sharp(resizedImage).resize(maxHeight, maxWidth).toBuffer()
return `data:${mimType};base64,${resizedImage.toString("base64")}`
} catch (error) {
throwError({ error })
}
};

You can still use JIMP, but all you need to do is to drop the prefix before reading the base64.
Depend on your image format use a specific jpg or png in the following example.
Example:
Jimp.read(Buffer.from((yourImageBuffer).replace(/^data:image\/png;base64,/, ""), 'base64'), function (err, image) {
// Do you stuff here with the image
})

....some logic....
let max_width = 371;
let max_height = 280;
var image = new Image();
image.src = YOUR_BASE64_STRING;
let image_width = image.width;
let image_height = image.height;
let ratio = 1;
if(image_width > max_length || image_height > max_length){ //need to scale
ratio = max_length / image_width;
if(image_height > image_width){
ratio = max_length / image_height;
}
}
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = image_width * ratio; // target width
canvas.height = image_height * ratio; // target height
ctx.drawImage(image,
0, 0, image.width, image.height,
0, 0, canvas.width, canvas.height
);
var RESIZED_BASE64_STRING = canvas.toDataURL();
....some logic....

Related

Can we get height and width of image using sharp?

I am using sharp to resize bulk of image. So I am resizing them to 500px by preserving their aspect ratio. Also I want to resize height to 500px and auto resize width if height is greater than with and vice versa. To do that I need to get image, height from Image buffer. I know there are pretty number of packages available to do so. But I was hoping if I can do that using sharp buffer itself.
Yes you can get the width and height of an image with sharp by using the metadata() function :
const image = await sharp(file.buffer)
const metadata = await image.metadata()
console.log(metadata.width, metadata.height)
You can get a lot more information from metadata , here is the documentation : https://sharp.pixelplumbing.com/api-input#metadata
To get the dimensions that are recorded in the header of the input image:
const image = await sharp(file.buffer);
const metadata = await image.metadata();
console.log(metadata.width, metadata.height);
However, operations like image.resize(...) will not affect the .metadata(). To get the dimensions after performing operations on the image, use .toBuffer({ resolveWithObject: true }):
const image = await sharp(file.buffer);
const resizedImage = image.resize(640);
const { info } = await resizedImage.png().toBuffer({ resolveWithObject: true });
console.log(info.width, info.height);
Sharp is very flexible, it has a number of options for resizing images. Using an option of fit: "contain" should accomplish what you wish.
Others are available of course, documented here: https://sharp.pixelplumbing.com/api-resize#resize
You can also specify the background color to fill space within the resized image, I'm using white here.
The code will look something like this:
const fs = require("fs");
const path = require("path");
const sharp = require("sharp");
const inputDir = "./input-images";
const outputDir = "./output-images";
const requiredDimension = 500;
const inputImages = fs.readdirSync(inputDir).map(file => path.join(inputDir, file));
function resizeImage(imagePath) {
sharp(imagePath)
.resize( { width: requiredDimension, height: requiredDimension, fit: "contain", background: { r: 255, g: 255, b: 255, alpha: 1 }})
.toFile(path.join(outputDir, path.basename(imagePath) + "-resized" + path.extname(imagePath)), (err, info) => {
if (err) {
console.error("An error occurred resizing image:", err);
}
});
}
// Ensure output dir exists...
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir)
}
inputImages.forEach(resizeImage);

How do i load google fonts on the sever side node js

I am using node js and canvas to create an API that writes text on a certain image. I successfully created a route such as -> /:sometext/:fontsize/:color/:position-x/:position-y and sends a static image with the text of the given font-size, color, and position on the canvas.
What I am unable to achieve is that I want to send the font family in the route and have that rendered on the screen. Plus, isn't there any other way that I can load at least google fonts without having to download the .ttf file.
What I have tried:
GetGoogleFonts npm package (which was a bad idea, since it was stuck at the installation)
WebFontLoader (Gives "Window is not defined" error)
Steps to Reproduce
Currently, I am using a ttf file to load the font
router.get('/thumbnail/:name/:fontsize/:color/:posx/:posy', function (req, res, next) {
let name = req.params.name;
let fontsize = req.params.fontsize
let positionx = req.params.posx
let positiony = req.params.posy
let color = req.params.color
let myimage = 'static/image1.jpg'
const canvas = createCanvas(2000, 1000)
const ctx = canvas.getContext('2d')
var str = "hi "+name
registerFont('AvenirNext.ttf', { family: 'Avenir Next' })
loadImage(myimage).then((image) => {
ctx.drawImage(image, 0 , 0, 2000, 1000);
ctx.font = fontsize + "px Avenir Next"
ctx.fillStyle = color
ctx.fillText(str, positionx, positiony);
const out = fs.createWriteStream(__dirname + '/test.jpeg')
const stream = canvas.createJPEGStream()
stream.pipe(res)
out.on('finish', () => console.log('The JPEG file was created.'))
})
});
If you don't want to host the ttf files on your own server you could try to use the Google Font Github repo.
// missing imports
const http = require('http');
const fs = require('fs');
const fontFamily = req.params.fontfamily; // example: ArchivoNarrow
// download font from github
const file = fs.createWriteStream(fontFamily + '.ttf');
const request = http.get('https://github.com/google/fonts/blob/master/ofl/' + fontFamily.toLowerCase() + '/' + fontFamily + '-Regular.ttf?raw=true', function(response) {
response.pipe(file);
});
registerFont(fontFamily + '.ttf', { family: fontFamily });
// delete font after the image is created
try {
fs.unlinkSync(fontFamily + '.ttf');
} catch(err) {
console.error(err);
}
Font I used for this example: ArchivoNarrow
From the docs for registerFont
To use a font file that is not installed as a system font, use registerFont() to register the font with Canvas. This must be done before the Canvas is created.
emphasis not mine
You are calling it after you created your canvas.
// first register the font
registerFont('AvenirNext.ttf', { family: 'Avenir Next' });
// then create the canvas
const canvas = createCanvas(2000, 1000);
const ctx = canvas.getContext('2d');
var str = "hi " + name;
But note that this will try to load a font that would be available on your server. If you want to make it target a font on ://fonts.googleapis.com server, you'd need to pass the full URI to the font file (not for the .css).
Also, you should wrap your family name inside quotes (") since it contains a space:
ctx.font = fontsize + 'px "Avenir Next"';

Capture and save image with robotjs

I tried to capture and save an image from screen with robotjs (http://robotjs.io/) but when I open the file bitmap the image is not in a valid format. This is my code:
var robot = require("robotjs");
var fs = require("fs");
var size = 10;
var img = robot.screen.capture(0, 0, size, size);
fs.writeFileSync('img.bmp',img.image);
Jimp supports converting Raw Pixel Buffer into PNG out-of-the-box and works a lot faster.
let robot = require("robotjs");
let Jimp = require('jimp');
const img = robot.screen.capture(0, 0, width, height).image;
new Jimp({data: img, width, height}, (err, image) => {
image.write(fileName);
});
The image will be saved with the wrong colors. To fix it, you can use the following code:
function screenCaptureToFile2(robotScreenPic, path) {
return new Promise((resolve, reject) => {
try {
const image = new Jimp(robotScreenPic.width, robotScreenPic.height);
let pos = 0;
image.scan(0, 0, image.bitmap.width, image.bitmap.height, (x, y, idx) => {
image.bitmap.data[idx + 2] = robotScreenPic.image.readUInt8(pos++);
image.bitmap.data[idx + 1] = robotScreenPic.image.readUInt8(pos++);
image.bitmap.data[idx + 0] = robotScreenPic.image.readUInt8(pos++);
image.bitmap.data[idx + 3] = robotScreenPic.image.readUInt8(pos++);
});
image.write(path, resolve);
} catch (e) {
console.error(e);
reject(e);
}
});
}
var pic = robot.screen.capture();
screenCaptureToFile2(pic)
Note that your img.image Buffer from Robotjs is a raw buffer with pixels; not a BMP or PNG or any other format.
You should do some data conversion and probably save it using a library that supports writing to file (I do not see that in Robotjs).
Please look at this other question, which also uses robot.screen.capture and saves file to a PNG file using Jimp library. That code answers your question too.

Image found but not visible Nodejs and Express

I have a canvas and in the canvas I want to load an image.
client.js
game.viewport = document.getElementById('viewport');
game.card1 = document.getElementById('card1');
//Adjust their size
game.viewport.width = game.world.width;
game.viewport.height = game.world.height;
game.card1.width = game.cards.width;
game.card1.height = game.cards.height;
//Fetch the rendering contexts
game.ctx = game.viewport.getContext('2d');
game.ctx2 = game.card1.getContext('2d');
game_core.js
game_core.prototype.client_cards = function(game_instance) {
this.instance = game_instance;
var img = new Image();
img.src = '/images/Chainlinks/AddBurn.png';
this.ctx2.drawImage(img,10,20);
this.ctx2.fillStyle = 'rgba(255,255,255,0.7)';
this.ctx2.fillText('Summoner: '+player1, 10 , 20);
The text Summnor2: is visible in the canvas but the image isn't drawed. If I look in de cmd of node.js he finds the image path. so I really don't know where it goes wrong.
Try something like this. Make sure image path is correct.
var self = this;
var img = new Image();
img.src = './images/Chainlinks/AddBurn.png';
img.onload = function() {
self.ctx2.drawImage(img,10,20);
self.ctx2.fillStyle = 'rgba(255,255,255,0.7)';
self.ctx2.fillText('Summoner: '+player1, 10 , 20);
};

Image not saving on a Palm device

Updated: webOS version 2.1...
This base64 encoded image data generates the correct data image when I append it to the source of an image, like this:
var img = new Image();
img.src= data
var data = "data:image/png;base64,iVBORw0KGgoAAAANSUh
EUgAAAJYAAACmCAIAAAC3GeEYAAEkgklEQVR4AQD9/wIAADt8Fj5/GUSA IESAIEN/GUd/H0V+IEaEIE
WFHUOEHEqJIE2KIk2KI1GQKFSQLFaQKlaOKVaQKlORLlaYLVaVKFWV KVWUKliWKFiXJlKVIliTJW2oP
m6jOW+kPGqkPmehPWWePWagOmmjP2ykQ2ulQmylRWykQmqmP2qr QGqnP26qSGyvRmquQWyyQGquP2yu
RHSzSHWwSXGxSGytRG+vSG6vSW2vRWquRGqtRGmsQnO1R3Gw SG+rSXavUXWwUnKqR3CuRWquP26zQ3C
yRXK0SHG0SWupR2qoR3CuS2qrQ3CsSG6vS22pR26qSGyq RWetO22uQ2yqP22wRGetP2yyP4TEWgElAB
UrBRYmAx05AidHBB9MCydRDylSGChWGCZUFyFLEyNK Ex5IDBtJBhc/Bx9FDBxDDh5HDyRGExs8DRs4D
B04DRw8Exo6DxMuBw8kAhEeABIYAQ4VABAUAA0S AAwVAg8bAw0bAgwaAxAYAAULAQgQAQcQBQsPAAwQ
AggMAwMLAQAIAAgOBAYOAAsWBg4bChMgDxUk DxcmERopEh8vFBwuExspEhcnDxUpDhcqERUnDhUnDRQ
rDxgsERgvEx8xGQA+fxk7gxU9hBc9ghg/ gR1CgBxBhBtChRxIhyFMiyNMiyNNjiZNiypRkCpSjydRkC
VVkSpTkihYmi9YlC9XlCxVlClYlixW lSpZlS1eli16skJnqDxppj1qpDxmpD9mpD1loj1opz9qqENvq
Udpp0FmqD9npkFtpUVvp0ZvrUVs q0NsrEFtrURsrkBrsT9vskFvrj5srz5ssUJsrkJsrkNtr0NusEVm
qjxrrz5ttkNquEFqtEFu"
I am trying to save the image using my custom service, but it does not save the correct image. Here is the Node.js code for saving the image:
var fs = IMPORTS.require('fs');
var path = IMPORTS.require('path');
var buff = new Buffer(img.substr('data:image/png;base64,'.length), 'base64');
path.exists('/media/internal/wallpapers/', function(exists){
if (exists) {
var id = fs.openSync('/media/internal/wallpapers/i.png', 'w', 666);
var written = fs.writeSync(id, buff,0, buff.length, 0);
fs.closeSync(id);
}
});
The saved version of the image has totally different pixel values.
EDIT
I came to find out that the image data above isn't correct. I am using a PNG library to generate base64 image data of a canvas pixel array. The link is http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/.
That is an alternative to canvas.toDataURL();. webOS does not support toDataURL, so I had to use a library.
Here is the code I have used with this library to manipulate my canvas image data as a pixel array:
EditorAssistant.prototype.getDataURL = function(width,height,pixelArray) {
var p = new PNGlib(height, width, 256); // Construcor takes height, weight and color-depth.
var background = p.color(0, 0, 0, 0);
var k = 0;
for (var j = 0; j<height; j++) {
for (var i =0; i<width; i++) {
var x =i;
var y =j;
p.buffer[p.index(x,y)] = p.color(pixelArray[k], pixelArray[k+1], pixelArray[k+2]);
k+=4;
}
}
return 'data:image/png;base64,'+p.getBase64() ;
}
Does this library work and where am I doing it wrong?
libpng.js only supports 256 color images, so that may be the problem. There are other base64 encoding utilities which may work as expected.

Resources